Clojure and The UFC: Part 1

Professional fighting and the world of Clojure // Exploring fighters and their fights with the Clojure programming language.

NOTE: This was written in a Gorilla REPL and is known to not format/indent Clojure code correctly

Preparing a namespace

A namespace allows other namespaces to use it's components and to avoid symbol collision. Using the :as keyword means that the required namespace will be given an alias within the current namespace. Aliases are usally a shorter, clearer, representation of an original namespace name.

Using :refer imports foreign symbols into the current namespace so that the symbol can be used without the original namespace prefix.

(ns ufc
  (:require 
   [gorilla-plot.core :as plot]
   [gorilla-renderable.core :as r]
   [gorilla-repl.html :as h]
   [clojure.pprint :refer [pprint
                           print-table]]
   [clojure.data.json :as json]
   [hiccup.core :refer [html]]))
(println "My NS => " *ns*)
My NS =>  #<Namespace ufc>
nil

Loading a dataset

  • stats-json-fp represents the default file path relative to the project directory and is used as a fallback file path.

  • entry-point is the default key which holds an array value.

  • stats* loads the data file and returns the value of the entry point.

  • stats calls stats* but memoizes the results to improve the performance of future calls.

xxxxxxxxxx
 
;; Define the default dataset file
(def stats-json-fp "complete-fighter-stats.json")
;; Define the default JSON key
(def entry-point
  "Default JSON key for main data"
  "fighters")
;; Function gets the JSON value of entry-point
(defn stats*
  "Loads JSON file and returns entry-point value."
  ([fp] (stats* fp entry-point))
  ([fp entry-point]
     (-> fp
         slurp
         json/read-str
         (get entry-point {}))))
;; Create a version of stats* that caches the results
(let [f (memoize stats*)]
  (defn stats
  "Loads JSON file and returns cached entry-point value."
  ([]
     (f stats-json-fp))
  ([^:String fp] 
     (f fp entry-point))
  ([^:String fp 
    ^:String entry-point]
     (f fp entry-point))))
#'ufc/stats

Loading...

Taking a peek at the dataset, the first read is going to be much slower than future reads.

(-> (stats) count time)
"Elapsed time: 1617.038439 msecs"
574

And now the cached version should be a lot faster.

xxxxxxxxxx
 
(-> (stats) count time)
"Elapsed time: 0.064509 msecs"
574

A Single Fighter

Finding possible attributes of a fighter is as simple as pulling the keys off of a fighter hash-map.

(let [fighter (first (stats))
      fighter-attributes (keys fighter)
      attr-sz (count fighter-attributes)]
  (println
   (format "A single fighter has %d attributes => \n"
       attr-sz))
  
  (doseq [a fighter-attributes]
    (println a)))
A single fighter has 18 attributes => 

Age
CareerStats
UFCWeightClass
Fights
UFCRecord
FightingOutOf
Weight
MMAID
Record
DOB
Reach
Height
FighterID
Born
UFCRanking
Name
Stance
Accolade
nil

Just The Fighter

Each fighter has an array of objects under the Fights key. fighter-without-fights is created so that fighters can be presented without every single fight associated with them.

xxxxxxxxxx
 
(defn fighter-without-fights
  "Returns a fighter without fights collection"
  [fighter-map]
  (dissoc fighter-map "Fights"))
#'ufc/fighter-without-fights

Let's take a look.

(def example-fighter
     (rand-nth (stats)))
(-> example-fighter 
    fighter-without-fights
    pprint)
{"Age" "37",
 "CareerStats"
 {"StrikingDefense" "61.30",
  "TakedownDefense" "31.25",
  "KDAverage" "0.0000",
  "AverageFightTime" "11:25",
  "TakedownAverage" "3.7412",
  "TakedownAccuracy" "38.14",
  "SLpM" "2.0155",
  "SubmissionsAverage" "1.2133",
  "AverageFightTime_Seconds" "685",
  "StrikingAccuracy" "49.18",
  "SApM" "1.1999"},
 "UFCWeightClass"
 {"WeightClassID" nil, "Description" nil, "Abbreviation" nil},
 "UFCRecord"
 {"Wins" "13", "Losses" "5", "Draws" "0", "NoContests" "0"},
 "FightingOutOf" {"City" nil, "State" nil, "Country" nil},
 "Weight" "170",
 "MMAID" nil,
 "Record" {"Wins" "13", "Losses" "5", "Draws" "0", "NoContests" "0"},
 "DOB" "1976-11-29",
 "Reach" "74.0",
 "Height" "72",
 "FighterID" "287",
 "Born" {"City" "Weehawken", "State" "New Jersey", "Country" "USA"},
 "UFCRanking" {"Current" nil, "Previous" nil},
 "Name"
 {"FirstName" "Ricardo", "LastName" "Almeida", "NickName" "Big Dog"},
 "Stance" "Orthodox",
 "Accolade" {"Type" nil, "Name" nil}}
nil

What You Need

It looks okay, but some of the numeric values are represented as Strings.

  • enforce-type is used to bruteforce an acceptable representation value. Original object when all possibilities are exhausted.
  • recursive-enforce-type is used to apply enforce-type to nested values. Eagerly evaluated, so becareful.
  • fighter-without-fights calls recursive-enforce-type after removing a fighters fight array collection.
 
;; Bruteforce numeric type enforcement
(defn enforce-type
  "Integer first, Float second, original object on fail"
  [x]
  (try
    (Integer/parseInt x)
    (catch Exception ex
      (try
        (Float/parseFloat x)
        (catch Exception ex x)))))
;; The function used to modify fighter-without-fights fn
(defn recursive-enforce-type
  "recursively calls enforce-type on a fighter map"
  [x]
  (reduce merge
          (for [[k v] x
                :let [f (if-not (map? v)
                          enforce-type
                          recursive-enforce-type)]]
            (hash-map k (f v)))))
;; Add recursive-enforce-type to this fn
(defn fighter-without-fights
  "Returns a fighter without fights collection"
  [fighter-map]
  (recursive-enforce-type
   (dissoc fighter-map "Fights")))
;; Let's see the results
(-> example-fighter
    fighter-without-fights
    pprint)
{"Age" 37,
 "CareerStats"
 {"StrikingDefense" 61.3,
  "TakedownDefense" 31.25,
  "KDAverage" 0.0,
  "AverageFightTime" "11:25",
  "TakedownAverage" 3.7412,
  "TakedownAccuracy" 38.14,
  "SLpM" 2.0155,
  "SubmissionsAverage" 1.2133,
  "AverageFightTime_Seconds" 685,
  "StrikingAccuracy" 49.18,
  "SApM" 1.1999},
 "UFCWeightClass"
 {"Description" nil, "Abbreviation" nil, "WeightClassID" nil},
 "UFCRecord" {"NoContests" 0, "Wins" 13, "Draws" 0, "Losses" 5},
 "FightingOutOf" {"Country" nil, "City" nil, "State" nil},
 "Weight" 170,
 "MMAID" nil,
 "Record" {"NoContests" 0, "Wins" 13, "Draws" 0, "Losses" 5},
 "DOB" "1976-11-29",
 "Reach" 74.0,
 "Height" 72,
 "FighterID" 287,
 "Born" {"Country" "USA", "City" "Weehawken", "State" "New Jersey"},
 "UFCRanking" {"Current" nil, "Previous" nil},
 "Name"
 {"NickName" "Big Dog", "LastName" "Almeida", "FirstName" "Ricardo"},
 "Stance" "Orthodox",
 "Accolade" {"Type" nil, "Name" nil}}
nil

Numbers Where Needed

The ->numerics function is needed for finding numeric values in a fighter map. Eagerly evaluated, so becareful.

xxxxxxxxxx
 
(defn ->numerics
  "Recursively filters for numeric values from a fighter map"
  [x]
  (let [data (recursive-enforce-type x)]
    (reduce merge
            (for [[k v] data]
              (if-not (map? v)
                (if-not (number? v)
                  {}
                  (hash-map k v))
                (->numerics v))))))
(-> example-fighter
    fighter-without-fights
    ->numerics
    pprint)
{"Age" 37,
 "StrikingDefense" 61.3,
 "NoContests" 0,
 "TakedownDefense" 31.25,
 "KDAverage" 0.0,
 "TakedownAverage" 3.7412,
 "Weight" 170,
 "Wins" 13,
 "Draws" 0,
 "Reach" 74.0,
 "Height" 72,
 "FighterID" 287,
 "TakedownAccuracy" 38.14,
 "SLpM" 2.0155,
 "SubmissionsAverage" 1.2133,
 "Losses" 5,
 "AverageFightTime_Seconds" 685,
 "StrikingAccuracy" 49.18,
 "SApM" 1.1999}
nil

Everything should be good to go after removing some ID numbers.

  • without removes any kv pair from any map object
  • ->numeric-map creates a hash-map based on ->numerics. All versions of "*ID" are removed.
xxxxxxxxxx
 
(defn without
  "Removes values from a fighter map"
  [x & ks]
  (apply dissoc x ks))
(defn ->numeric-map
  "Returns numeric map without IDs"
  [x]
  (-> x
      fighter-without-fights
      ->numerics
      (without "MMAID"
                   "WeightClassID"
                   "FighterID")))
(-> example-fighter
    ->numeric-map
    pprint)
{"Age" 37,
 "StrikingDefense" 61.3,
 "NoContests" 0,
 "TakedownDefense" 31.25,
 "KDAverage" 0.0,
 "TakedownAverage" 3.7412,
 "Weight" 170,
 "Wins" 13,
 "Draws" 0,
 "Reach" 74.0,
 "Height" 72,
 "TakedownAccuracy" 38.14,
 "SLpM" 2.0155,
 "SubmissionsAverage" 1.2133,
 "Losses" 5,
 "AverageFightTime_Seconds" 685,
 "StrikingAccuracy" 49.18,
 "SApM" 1.1999}
nil

Charting Sequence

Charting the values only requires two attribute charting functions and an attribute loop.

  • attr-chart creates a raw Gorilla REPL plotting object and alters internal attributes to create a horizontal value display.
  • single-fighter-attr-chart calls attr-chart and places the result into a display body with the attribute key as the display header title.
xxxxxxxxxx
 
(defn attr-chart
  [fighter-numerical-map k color]
  (r/render
   (let [v (get fighter-numerical-map k)
         v (if (nil? v) 0 v)
         chart (plot/list-plot [[v 0]]
                               :opacity 0.80
                               :aspect-ratio 0.40
                               :plot-size 540
                               :symbol-size 200
                               :color color
                               :plot-range [[0 (+ v 10)] :all])]
     (assoc-in chart [:content :height] 10))))
(defn single-fighter-attr-chart
  [fighter-numerical-map k color]
    (reify
      r/Renderable
      (render [_]
        {:type :list-like
         :open ""
         :close ""
         :separator ""
         :items
         [{:type :html
           :content (html [:h3 {:style {:float :left}} k])
           :value (pr-str k)}
          (attr-chart fighter-numerical-map
                      k
                      color)]})))
;; Charting Loop
(let [fighter (->numeric-map example-fighter)
      attributes (keys fighter)]
  (for [a attributes]
    (single-fighter-attr-chart fighter a "green")))
(

Age

051015202530354045

StrikingDefense

010203040506070

NoContests

012345678910

TakedownDefense

0510152025303540

KDAverage

012345678910

TakedownAverage

024681012

Weight

020406080100120140160180

Wins

0246810121416182022

Draws

012345678910

Reach

01020304050607080

Height

01020304050607080

TakedownAccuracy

051015202530354045

SLpM

0123456789101112

SubmissionsAverage

01234567891011

Losses

02468101214

AverageFightTime_Seconds

0100200300400500600

StrikingAccuracy

0510152025303540455055

SApM

01234567891011
)

Fighter1 VS Fighter2 Comparisons

A single function can merge the original charting sequence of two seperate fighters to produce a single chart.

  • example-fighter2 is the comparison object for example-fighter
  • double-fighter-attr-chart calls attr-chart for each fighter passed with a fighter's value display color.
xxxxxxxxxx
;; Define a second fighter for comparison charts
(def example-fighter2 (rand-nth (stats)))
(defn double-fighter-attr-chart
  [fighter-numerical-map
   color
   fighter2-numerical-map
   color2
   k]
    (reify
      r/Renderable
      (render [_]
        {:type :list-like
         :open ""
         :close ""
         :separator ""
         :items
         [{:type :html
           :content (html [:h3 {:style {:float :left}} k])
           :value (pr-str k)}
          (r/render
           (plot/compose
            (attr-chart fighter-numerical-map
                        k
                        color)
            (attr-chart fighter2-numerical-map
                        k
                       color2)))]})))
;; Charting Loop
(let [fighter (->numeric-map example-fighter)
      fighter2 (->numeric-map example-fighter2)
      attributes (keys fighter)]
  
  (for [a attributes]
    (double-fighter-attr-chart fighter
                               "orange"
                               fighter2
                               "red"
                               a)))
(

Age

051015202530354045

StrikingDefense

010203040506070

NoContests

012345678910

TakedownDefense

0510152025303540

KDAverage

012345678910

TakedownAverage

024681012

Weight

020406080100120140160180

Wins

0246810121416182022

Draws

012345678910

Reach

01020304050607080

Height

01020304050607080

TakedownAccuracy

051015202530354045

SLpM

0123456789101112

SubmissionsAverage

01234567891011

Losses

02468101214

AverageFightTime_Seconds

0100200300400500600

StrikingAccuracy

0510152025303540455055

SApM

01234567891011
)

Fighting Headers

Each fighting comparison chart will need a competitor header, so there will be two functions to make this happen.

  • fighter-header builds a HTML display of a single fighter for Gorilla REPL Renderable.
  • fighter-vs-header takes two fighters and their assigned value colors. Calls fighter-header for each fighter and merges the display with a H3 HTML element with innerHTML value of "VS".
xxxxxxxxxx
 
(defn fighter-header
  [fighter color]
  (let [names ["FirstName"
               "NickName"
               "LastName"]
        fname (interpose " "
                         (map #(get-in fighter
                                       ["Name" %])
                              names))
        fname (reduce str fname)]
    {:type :html
     :content (html
               [:h2 {:style (str "color:" color)}
                fname])
     :value fname}))
(defn fighter-vs-header
  [fighter
   color
   fighter2
   color2]
  (reify
    r/Renderable
    (render [_]
      {:type :list-like
       :open ""
       :close ""
       :separator (html [:h3 "VS"])
       :items
       [(fighter-header fighter color)
        (fighter-header fighter2 color2)]})))
         
 
(fighter-vs-header example-fighter
                   "purple"
                   example-fighter2
                   "blue") 

Ricardo Big Dog Almeida

VS

Antoni Hardonk

Two Fighters = 1 Chart

  • compare-fighters calls fighter-vs-header with the supplied fighters and their assigned colors. Merges the result of fighter-vs-header with every attribute found in a valid numeric-map of the first supplied fighter.
xxxxxxxxxx
 
(defn compare-fighters
  [fighter
   color
   fighter2
   color2]
  (let [fnm (->numeric-map fighter)
        fnm2 (->numeric-map fighter2)
        attributes (keys fnm)]
    (into
     [(fighter-vs-header fighter
                         color
                         fighter2
                         color2)]
     (for [a attributes]
       (double-fighter-attr-chart fnm
                                  color
                                  fnm2
                                  color2
                                  a)))))
;; Generate the chart
(compare-fighters example-fighter 
                  "purple"
                  example-fighter2
                  "blue")
[

Ricardo Big Dog Almeida

VS

Antoni Hardonk

Age

051015202530354045

StrikingDefense

010203040506070

NoContests

012345678910

TakedownDefense

0510152025303540

KDAverage

012345678910

TakedownAverage

024681012

Weight

020406080100120140160180

Wins

0246810121416182022

Draws

012345678910

Reach

01020304050607080

Height

01020304050607080

TakedownAccuracy

051015202530354045

SLpM

0123456789101112

SubmissionsAverage

01234567891011

Losses

02468101214

AverageFightTime_Seconds

0100200300400500600

StrikingAccuracy

0510152025303540455055

SApM

01234567891011
]

Know What's Best

A visual comparison is nice but sometimes it's better to have a clear winner.

  • comparatives is a hash-map of attributes and how they should be perceived.
  • comparative-outcome will return a collection with the first value being a string stating how the attribute should be perceived. The second value is the winner if not a value of :tied
 
(def comparatives
  {"Age" :lower
   ;; Average Fight Time
   
   "AverageFightTime_Seconds" :higher
   
   ;; Knockdowns Landed
   
   "KDAverage" :lower
   
   ;; Strikes Absorbed Per Minute
   
   "SApM" :lower
   
   ;; Strikes Landed Per Minute
   
   "SLpM" :higher
   "StrikingAccuracy" :higher
   "StrikingDefense" :higher
   "SubmissionsAverage" :higher
   "TakedownAccuracy" :higher
   "TakedownAverage" :higher
   "TakedownDefense" :higher
   "Height" :higher
   "Reach" :higher
   "Draws" :lower
   "Losses" :lower
   "NoContests" :lower
   "Wins" :higher
   "Weight" :higher})
(defn comparative-outcome
  "Returns coll [^:String better-measure k].
   k is :fighter, :fighter2, or :tied"
  [fighter
   fighter2
   k]
  (let [win-when (get comparatives k)
        win-fn (case win-when
                 :lower min
                 :higher max)
        fnm (->numeric-map fighter)
        f2nm (->numeric-map fighter2)
        fv (get fnm k)
        f2v (get f2nm k)]
    
    [(condp = win-when
       :lower "Lower is better"
       :higher "Higher is better")
     
     (if (= fv f2v)
       :tied
       (condp = (win-fn fv f2v)
         fv :fighter
         f2v :fighter2))]))
;; Comparing the Age of two example fighters
(comparative-outcome example-fighter
                     example-fighter2
                     "Age")
["Lower is better" :fighter]

Naming The Best

Knowing who's best isn't the same as stating who's best. This is where comparative-outcome-named comes in.

  • comparative-outcome-named calls comparative-outcome and assigns the results to the supplied fighters.
 
(defn comparative-outcome-named
  [fighter
   fighter2
   k]
  (let [full (fn [fighter]
               (reduce str
                       (interpose " "
                                  (map #(get-in fighter
                                                ["Name" %])
                                       ["FirstName"
                                        "NickName"
                                        "LastName"]))))
        possible {:fighter (full fighter)
                  :fighter2 (full fighter2)
                  :tied "Tied"}
        [measure-string winner] (comparative-outcome fighter
                                                     fighter2
                                                     k)]
    (format "%s - %s"
            measure-string
            (winner possible))))
;; Comparing the Age of two example fighters
(print "Age: "
       (comparative-outcome-named example-fighter
                                  example-fighter2
                                  "Age"))
Age:  Lower is better - Ricardo Big Dog Almeida
nil

Winners On Display

  • compare-fighters-with-attr-wins calls double-fighter-attr-chart for every possible attribute between two fighters and then merges the results with a call to fighter-vs-header
xxxxxxxxxx
(defn compare-fighters-with-attr-wins
  [fighter
   color
   fighter2
   color2]
  (let [fnm (->numeric-map fighter)
        f2nm (->numeric-map fighter2)
        attributes (keys fnm)]
    (reduce into
            [(fighter-vs-header fighter
                        color
                        fighter2
                        color2)]
     (for [a attributes]
       [(double-fighter-attr-chart fnm
                                   color
                                   f2nm
                                   color2
                                   a)
        (comparative-outcome-named fighter
                                   fighter2
                                   a)]))))
;; Compare all attributes
(compare-fighters-with-attr-wins example-fighter
                                 "blue"
                                 example-fighter2
                                 "green")
[

Ricardo Big Dog Almeida

VS

Antoni Hardonk

Age

051015202530354045
"Lower is better - Ricardo Big Dog Almeida"

StrikingDefense

010203040506070
"Higher is better - Ricardo Big Dog Almeida"

NoContests

012345678910
"Lower is better - Tied"

TakedownDefense

0510152025303540
"Higher is better - Antoni Hardonk"

KDAverage

012345678910
"Lower is better - Ricardo Big Dog Almeida"

TakedownAverage

024681012
"Higher is better - Ricardo Big Dog Almeida"

Weight

020406080100120140160180
"Higher is better - Antoni Hardonk"

Wins

0246810121416182022
"Higher is better - Ricardo Big Dog Almeida"

Draws

012345678910
"Lower is better - Tied"

Reach

01020304050607080
"Higher is better - Antoni Hardonk"

Height

01020304050607080
"Higher is better - Antoni Hardonk"

TakedownAccuracy

051015202530354045
"Higher is better - Ricardo Big Dog Almeida"

SLpM

0123456789101112
"Higher is better - Antoni Hardonk"

SubmissionsAverage

01234567891011
"Higher is better - Ricardo Big Dog Almeida"

Losses

02468101214
"Lower is better - Ricardo Big Dog Almeida"

AverageFightTime_Seconds

0100200300400500600
"Higher is better - Ricardo Big Dog Almeida"

StrikingAccuracy

0510152025303540455055
"Higher is better - Antoni Hardonk"

SApM

01234567891011
"Lower is better - Ricardo Big Dog Almeida"]

Fighter VS The World

Comparing a single fighter with all fighters in the UFC requires seveal new functions.

  • x-numeric-map creates a map call fn to ->numeric-map
  • x-gen-fighters-attr creates a map call fn to get a value for the supplied attr key
  • x-gen-numeric-attr creates a fn composed of x-numeric-map and a result of a x-gen-fighters-attr
xxxxxxxxxx
(def x-numeric-map
  (map ->numeric-map))
(defn x-gen-fighters-attr
  [k]
  (map #(get % k)))
(defn x-gen-numeric-attr
  [attr]
  (let [x-map (x-gen-fighters-attr attr)]
    (comp
     x-numeric-map
     x-map)))
(defn list-plot
  [attr color]
  (plot/list-plot
   (sequence (x-gen-numeric-attr attr)
             (stats))
   :color color))
(defn fighter-compare-all-attr
  [fighter color attr]
  (plot/compose
   (list-plot attr "black")
   (plot/list-plot
    (sequence (x-gen-numeric-attr attr)
               [fighter])
    :color color
    :symbol-size 240)))
;; Compare
(fighter-compare-all-attr example-fighter
                          "red"
                          "Age")
0501001502002503003504004505005502022242628303234363840424446

Fighters VS The World

  • fighter-vs-fighter-compare-all-attr calls fighter-compare-all-attr for every supplied fighter and their assigned color. The results are merged to create a single attribute chart.
  • fighters-attr-vs-all compares a single attribute of two fighters against every other fighter in the UFC to create a single image.
xxxxxxxxxx
 
(defn fighter-vs-fighter-compare-all-attr
  [fighter
   color
   fighter2
   color2
   attr]
  (plot/compose
   (fighter-compare-all-attr fighter
                             color
                             attr)
   (fighter-compare-all-attr fighter2
                             color2
                             attr)))
(defn fighters-attr-vs-all
  [fighter
   color
   fighter2
   color2
   attr]
  (let [f fighter
        fcolor color
        f2 fighter2
        f2color color2]
    (reify
      r/Renderable
      (render [_]
        {:type :list-like
         :open ""
         :close ""
         :separator ""
         :items
         
         [{:type :html
           :content (html
                     [:p
                      [:h3 attr]
                      (comparative-outcome-named f f2 attr)])                    
           :value attr}
          (r/render
           (fighter-vs-fighter-compare-all-attr f
                                                fcolor
                                                f2
                                                f2color
                                                attr))]}))))
;; Compare
(fighters-attr-vs-all example-fighter
                      "purple"
                      example-fighter2
                      "orange"
                      "Age")

Age

Lower is better - Ricardo Big Dog Almeida

0501001502002503003504004505005502022242628303234363840424446

The Final Result

xxxxxxxxxx
(defn fighters-vs-all
  [fighter
   color
   fighter2
   color2]
  (let [attrs (-> fighter
                  ->numeric-map
                  keys)]
    (into [(fighter-vs-header fighter
                              color
                              fighter2
                              color2)]
          (for [a attrs]
            (fighters-attr-vs-all fighter
                                  color
                                  fighter2
                                  color2
                                  a)))))
;; Compare
(fighters-vs-all example-fighter
                 "purple"
                 example-fighter2
                 "orange")
[

Ricardo Big Dog Almeida

VS

Antoni Hardonk

Age

Lower is better - Ricardo Big Dog Almeida

0501001502002503003504004505005502022242628303234363840424446

StrikingDefense

Higher is better - Ricardo Big Dog Almeida

0501001502002503003504004505005500102030405060708090100

NoContests

Lower is better - Tied

0501001502002503003504004505005500.00.20.40.60.81.01.21.41.61.82.0

TakedownDefense

Higher is better - Antoni Hardonk

0501001502002503003504004505005500102030405060708090100

KDAverage

Lower is better - Ricardo Big Dog Almeida

050100150200250300350400450500550012345678910111213

TakedownAverage

Higher is better - Ricardo Big Dog Almeida

050100150200250300350400450500550051015202530

Weight

Higher is better - Antoni Hardonk

050100150200250300350400450500550120140160180200220240260

Wins

Higher is better - Ricardo Big Dog Almeida

05010015020025030035040045050055005101520253035404550

Draws

Lower is better - Tied

05010015020025030035040045050055001234567

Reach

Higher is better - Antoni Hardonk

0501001502002503003504004505005500102030405060708090

Height

Higher is better - Antoni Hardonk

0501001502002503003504004505005500102030405060708090

TakedownAccuracy

Higher is better - Ricardo Big Dog Almeida

0501001502002503003504004505005500102030405060708090100

SLpM

Higher is better - Antoni Hardonk

050100150200250300350400450500550024681012141618202224

SubmissionsAverage

Higher is better - Ricardo Big Dog Almeida

0501001502002503003504004505005500246810121416182022

Losses

Lower is better - Ricardo Big Dog Almeida

05010015020025030035040045050055002468101214161820

AverageFightTime_Seconds

Higher is better - Ricardo Big Dog Almeida

05010015020025030035040045050055001002003004005006007008009001,0001,100

StrikingAccuracy

Higher is better - Antoni Hardonk

0501001502002503003504004505005500102030405060708090

SApM

Lower is better - Ricardo Big Dog Almeida

050100150200250300350400450500550024681012141618202224
]