UFCData 0.7.0__tar.gz → 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UFCData
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Obtain UFC data and functions to manipulate it
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE.md
@@ -1,11 +1,14 @@
1
1
  # UFCData
2
2
 
3
+
3
4
  An open-source Python package for accessing, manipulating, and analyzing UFC data.
4
5
 
5
6
  https://pypi.org/project/UFCData/
6
7
 
7
8
  ## Table of Contents
8
9
 
10
+
11
+
9
12
  - [Installation](#installation)
10
13
  - [Updating the Package](#updating-the-package)
11
14
 
@@ -30,7 +33,7 @@ Model Evaluation
30
33
  - [Train Test Split](#train-test-split)
31
34
  - [Get Expanding Window](#get-expanding-window)
32
35
  - [Plotting Accuracy](#plotting-accuracy)
33
- - [Expanding Window Baseline Example](#expanding-window-cv-and-final-test-example)
36
+ - [Expanding Window Baseline Example](#expanding-window-baseline-example)
34
37
 
35
38
 
36
39
  Data Sources
@@ -38,6 +41,12 @@ Data Sources
38
41
  - [Discrepancies in Data](#discrepancies-in-data)
39
42
  - [Update Frequency](#update-frequency)
40
43
 
44
+ <br>
45
+
46
+ ---
47
+
48
+ <br>
49
+
41
50
  ## Installation
42
51
 
43
52
  ```bash
@@ -50,18 +59,23 @@ pip install ufcdata
50
59
  pip install --no-cache-dir --upgrade ufcdata
51
60
  ```
52
61
 
62
+ ---
63
+
64
+ <br>
65
+
53
66
  ## Loading Data
54
67
 
55
68
  ```python
56
69
  import ufcdata as ufc
57
70
  data = ufc.get_data()
58
71
 
59
- ## Obtain fighter_bio dataframe
60
72
  fighter_bio = data["fighter_bio"].copy()
61
73
  ```
62
74
 
63
75
  Note that data in here and other functions below reference data that is available prior to the data.
64
76
 
77
+ <br>
78
+
65
79
  ## Search
66
80
 
67
81
  Due to various event naming conventions and fighters sharing the same name, the primary keys for the dataframes are links, which can be difficult for humans to interpret. The `search()` function uses fuzzy string matching to make it easier to find fighters, events, and other records.
@@ -77,28 +91,19 @@ search(data, column, query, matches=1)
77
91
  * `query` — The name or text you are searching for.
78
92
  * `matches` — The number of closest matches to return. Defaults to `1`.
79
93
 
94
+ ### Returns
95
+ The results are returned as a dataframe, with the closest match appearing first.
96
+
80
97
  ### Example
81
98
 
82
99
  ```python
83
100
  john_jones = ufc.search(fighter_bio, "Name", "Jon Jones", 3)
84
101
  ```
85
102
 
86
- This searches the `"Name"` column of the `fighter_bio` dataframe for the three closest matches to `"Jon Jones"`.
87
103
 
88
- The results are returned as a dataframe, with the closest match appearing first.
89
-
90
- ### Returns
91
-
92
-
93
- ```text
94
- Name Nickname Height Weight Reach Stance Total W Total L Fighter Link
95
- Jon Jones Bones 76.0 248.0 84.0 Orthodox 28 1 ufcstats.com/...
96
- Roshaun Jones NaN 68.0 135.0 NaN NaN 2 6 ufcstats.com/...
97
- Mason Jones The Dragon 70.0 155.0 74.0 Orthodox 18 2 ufcstats.com/...
98
- ```
99
-
100
- This is useful when the exact value stored in the dataset is unknown or when there are multiple similar names. The same function can be used with any dataframe and column containing searchable text.
104
+ ---
101
105
 
106
+ <br>
102
107
 
103
108
  ## Elo
104
109
 
@@ -123,8 +128,8 @@ ufc.get_elo(fight_df, fighter_df, r=1500, k=30, s=400)
123
128
 
124
129
  The function returns a tuple containing:
125
130
 
126
- 1. `fighter_elo` — A dictionary mapping each fighter's UFCStats link to their final Elo rating.
127
- 2. `elo` — The original `fights_df` dataframe with `"Fighter 1 Elo"` and `"Fighter 2 Elo"` columns added. These columns contain each fighter's Elo rating immediately prior to the corresponding fight.
131
+ 1. `current_elo` — A dictionary mapping each fighter's UFCStats link to their final Elo rating.
132
+ 2. `past_elo` — The original `fights_df` dataframe with `"Fighter 1 Elo"` and `"Fighter 2 Elo"` columns added. These columns contain each fighter's Elo rating immediately prior to the corresponding fight.
128
133
 
129
134
 
130
135
  ### Example
@@ -143,13 +148,17 @@ current_elo, past_elo = ufc.get_elo(
143
148
  )
144
149
  ```
145
150
 
146
- The resulting `fight_elo` dataframe can then be used to examine or incorporate pre-fight Elo ratings into analysis and predictive models.
147
-
148
151
 
149
152
  ### Important
150
153
 
151
154
  `fight_df` should be sorted from most recent to least recent before being passed to `get_elo()`. The function reverses the dataframe internally to process fights chronologically and returns the resulting data in the original order.
152
155
 
156
+ <br>
157
+
158
+ ---
159
+
160
+ <br>
161
+
153
162
  ## Fighter History
154
163
 
155
164
  The function `get_fighter_history()` retrieves all UFC fights for a specific fighter and formats the results from the fighter's perspective. The requested fighter is always represented as `"Fighter 1"`, regardless of which side of the original fight dataframe they appeared on.
@@ -198,6 +207,8 @@ jon_jones_history = ufc.get_fighter_history(
198
207
 
199
208
  This function is useful for analyzing an individual fighter's career, constructing fighter-level features, or preparing historical data for predictive modeling.
200
209
 
210
+ <br>
211
+
201
212
  ## Fighter Statistic
202
213
 
203
214
 
@@ -235,11 +246,7 @@ get_fighter_statistic(
235
246
 
236
247
  ### Returns
237
248
 
238
- The function returns two DataFrames:
239
-
240
- ```python
241
- current_statistic, past_statistic = get_fighter_statistic(...)
242
- ```
249
+ The function returns a tuple containing:
243
250
 
244
251
  #### `current_statistic`
245
252
 
@@ -303,9 +310,13 @@ jon_jones_current, jon_jones_past = ufc.get_fighter_statistic(
303
310
  fights_df,
304
311
  rounds_df
305
312
  )
306
-
307
- jon_jones_current
308
313
  ```
314
+
315
+
316
+ ---
317
+
318
+ <br>
319
+
309
320
  ## Odds Convert
310
321
 
311
322
  The function `convert_odds()` converts American betting odds from strings into integers or floating point numbers.
@@ -344,10 +355,18 @@ data = ufc.get_data()
344
355
 
345
356
  fights_df = data["past_fights"].copy()
346
357
 
347
- fights_df["Fighter 1 Odds"] = fights_df["Fighter 1 Odds"].apply(ufc.convert_odds)
348
- fights_df["Fighter 1 Probability"] = fights_df["Fighter 1 Odds"].apply(ufc.convert_odds, probability=True)
349
- fights_df
358
+ fights_df["Fighter 1 Odds"] = fights_df["Fighter 1 Odds"].apply(
359
+ ufc.convert_odds
360
+ )
361
+
362
+ fights_df["Fighter 1 Probability"] = fights_df["Fighter 1 Odds"].apply(
363
+ ufc.convert_odds,
364
+ probability=True
365
+ )
350
366
  ```
367
+
368
+ <br>
369
+
351
370
  ## Gender Convert
352
371
 
353
372
  The function `convert_gender()` converts gender labels into binary numeric values, with `"Male"` represented as `1` and `"Female"` represented as `0`.
@@ -375,9 +394,10 @@ data = ufc.get_data()
375
394
 
376
395
  fights_df = data["past_fights"].copy()
377
396
  fights_df["Gender"] = fights_df["Gender"].apply(ufc.convert_gender)
378
- fights_df
379
397
  ```
380
398
 
399
+ <br>
400
+
381
401
  ## Weight Convert
382
402
 
383
403
  The function `convert_weight()` converts UFC weight class labels into their corresponding weight limits in pounds.
@@ -418,10 +438,10 @@ data = ufc.get_data()
418
438
 
419
439
  past_fights = data["past_fights"].copy()
420
440
  past_fights["Weight Class"] = past_fights["Weight Class"].apply(ufc.convert_weight)
421
-
422
- past_fights
423
441
  ```
424
442
 
443
+ <br>
444
+
425
445
  ## Mirror
426
446
 
427
447
  The function `mirror()` creates mirrored versions of a DataFrame by swapping corresponding pairs of columns. This is useful when analyzing UFC fights from both fighters' perspectives.
@@ -453,7 +473,16 @@ import ufcdata as ufc
453
473
  data = ufc.get_data()
454
474
 
455
475
  fights_df = data["past_fights"].copy()
456
- fights_df = fights_df[["Date", "Event Link", "Fighter 1", "Fighter 1 Odds","Fighter 2", "Fighter 2 Odds"]]
476
+ fights_df = fights_df[
477
+ [
478
+ "Date",
479
+ "Event Link",
480
+ "Fighter 1",
481
+ "Fighter 1 Odds",
482
+ "Fighter 2",
483
+ "Fighter 2 Odds",
484
+ ]
485
+ ]
457
486
 
458
487
  cols_1 = [
459
488
  "Fighter 1",
@@ -470,8 +499,6 @@ mirrored_fights = ufc.mirror(
470
499
  cols_1,
471
500
  cols_2
472
501
  )
473
-
474
- mirrored_fights
475
502
  ```
476
503
 
477
504
  Using `in_order=True` produces rows in the following order:
@@ -483,7 +510,7 @@ Original Fight 2
483
510
  Mirrored Fight 2
484
511
  ...
485
512
  ```
486
- Using `in_order=True` produces rows in the following order:
513
+ Using `in_order=False` produces rows in the following order:
487
514
  ```text
488
515
  Original Fight 1
489
516
  Original Fight 2
@@ -494,6 +521,12 @@ Mirrored Fight 2
494
521
 
495
522
  This is useful when creating fighter-level datasets where each fight should be represented from both fighters' perspectives.
496
523
 
524
+ <br>
525
+
526
+ ---
527
+
528
+ <br>
529
+
497
530
  ## Train Test Split
498
531
  The function `train_test_split()` splits a UFC fight DataFrame into training and test sets while preserving the chronological order of the data. Unlike `sklearn.model_selection.train_test_split()`, this function does not randomly shuffle the data.
499
532
 
@@ -518,9 +551,10 @@ data = ufc.get_data()
518
551
 
519
552
  past_fights = data["past_fights"].copy()
520
553
  train, test = ufc.train_test_split(past_fights, 0.2)
521
- train
522
554
  ```
523
555
 
556
+ <br>
557
+
524
558
  ## Get Expanding Window
525
559
  The function `get_expanding_window()` creates multiple training and test sets for time-series cross-validation using an expanding training window.
526
560
 
@@ -567,6 +601,7 @@ train, test = ufc.train_test_split(past_fights, 0.2)
567
601
  folds = ufc.get_expanding_window(train, 10, 500)
568
602
  folds['Fold 2']['test']
569
603
  ```
604
+ <br>
570
605
 
571
606
  ## Plotting Accuracy
572
607
 
@@ -593,8 +628,127 @@ plot_test(accuracy, title="Test Accuracy")
593
628
  * `accuracy` — The accuracy.
594
629
  * `title` — The title of the plot.
595
630
 
631
+ <br>
632
+
596
633
  ## Expanding Window Baseline Example
597
634
 
635
+ In this example, we split the data into 80% for cross-validation and 20% for final testing. The cross-validation set is evaluated using a 15-fold expanding window approach. Using only betting odds, we convert the odds into implied probabilities and predict the fighter with the higher probability as the winner. The example also demonstrates how the model can be deployed.
636
+
637
+ <br>
638
+
639
+ ```python
640
+ import ufcdata as ufc
641
+ import numpy as np
642
+
643
+ data = ufc.get_data()
644
+
645
+ ## Select rows where outcome is W or L and both fighters have odds associated with the fights.
646
+ past_fights = data["past_fights"].copy()
647
+ past_fights = past_fights[~past_fights["Fighter 1 Outcome"].isin(["NC", "D"])]
648
+ past_fights = past_fights[past_fights[["Fighter 1 Odds", "Fighter 2 Odds"]].notna().all(axis=1)]
649
+
650
+ ## Convert odds to probabilities
651
+ past_fights["Fighter 1 Probability"] = (
652
+ past_fights["Fighter 1 Odds"]
653
+ .apply(ufc.convert_odds, probability=True)
654
+ )
655
+
656
+ past_fights["Fighter 2 Probability"] = (
657
+ past_fights["Fighter 2 Odds"]
658
+ .apply(ufc.convert_odds, probability=True)
659
+ )
660
+
661
+ ## Create the data for cross validation and final testing
662
+ folds = 15
663
+ fold_names = [f"Fold {i}" for i in range(1, folds + 1)]
664
+ cross_validation, final_test = ufc.train_test_split(past_fights, 0.2)
665
+ fifteen_fold_cv = ufc.get_expanding_window(cross_validation, folds, 500)
666
+
667
+ fold_accuracy = []
668
+ ## Loop through each fold to get the accuracy
669
+ for fold in fold_names:
670
+ test = fifteen_fold_cv[fold]['test']
671
+ test["Predicted Winner"] = np.where(
672
+ test["Fighter 1 Probability"] > test["Fighter 2 Probability"],
673
+ test["Fighter 1 Link"],
674
+ test["Fighter 2 Link"])
675
+ accuracy = float((test["Predicted Winner"] == test["Winner"]).mean())
676
+ fold_accuracy.append(accuracy)
677
+
678
+ ## Plot cross validation accuracy
679
+ ufc.plot_cv(fold_names, fold_accuracy)
680
+ ```
681
+ <br>
682
+
683
+ ![Cross Validation Plot](https://i.imgur.com/sjCpnRE.png)
684
+
685
+ <br>
686
+
687
+ ```python
688
+ ## Evaluate on final test set
689
+ final_test["Predicted Winner"] = np.where(
690
+ final_test["Fighter 1 Probability"] > final_test["Fighter 2 Probability"],
691
+ final_test["Fighter 1 Link"],
692
+ final_test["Fighter 2 Link"])
693
+ test_accuracy = float((final_test["Predicted Winner"] == final_test["Winner"]).mean())
694
+ ufc.plot_test(test_accuracy)
695
+ ```
696
+
697
+ <br>
698
+
699
+ ![Test Plot](https://i.imgur.com/5dHR59X.png)
700
+
701
+ <br>
702
+
703
+ ```python
704
+ ## Deploy the model
705
+ future_fights = data["future_fights"].copy()
706
+
707
+ future_fights["Fighter 1 Probability"] = (
708
+ future_fights["Fighter 1 Odds"]
709
+ .apply(ufc.convert_odds, probability=True)
710
+ )
711
+
712
+ future_fights["Fighter 2 Probability"] = (
713
+ future_fights["Fighter 2 Odds"]
714
+ .apply(ufc.convert_odds, probability=True)
715
+ )
716
+
717
+ future_fights["Predicted Winner"] = np.where(
718
+ future_fights["Fighter 1 Probability"] > future_fights["Fighter 2 Probability"],
719
+ future_fights["Fighter 1 Link"],
720
+ future_fights["Fighter 2 Link"])
721
+
722
+ future_fights["Predicted Winner"] = np.where(
723
+ future_fights["Predicted Winner"] == future_fights["Fighter 1 Link"],
724
+ future_fights["Fighter 1"],
725
+ future_fights["Fighter 2"]
726
+ )
727
+
728
+ future_fights = future_fights[
729
+ [
730
+ "Date",
731
+ "Event Link",
732
+ "Fight Link",
733
+ "Fighter 1",
734
+ "Fighter 2",
735
+ "Predicted Winner"
736
+ ]
737
+ ]
738
+
739
+ future_fights
740
+
741
+ ```
742
+ <br>
743
+
744
+ ![Deployed Predictions](https://i.imgur.com/jgZzNOV.png)
745
+
746
+ <br>
747
+
748
+ ---
749
+
750
+ <br>
751
+
598
752
  ## Online Sources
599
753
 
600
754
  Odds and birthplace data was obtained from https://www.tapology.com
@@ -603,12 +757,16 @@ Venue and attendance data was obtained from https://en.wikipedia.org/wiki/List_o
603
757
 
604
758
  All other data was obtained from http://ufcstats.com
605
759
 
760
+ <br>
761
+
606
762
  ## Discrepancies in Data
607
763
 
608
764
  UFCStats is treated as the authoritative source for UFCData. When discrepancies exist between UFCStats and other sources, such as Wikipedia or Tapology, the UFCStats data is used.
609
765
 
610
766
  The UFCStats completed events page serves as the authoritative source for event, fight, and round data. Individual fighter profiles may contain fights from organizations or events that are not included in the completed events database, including WEC, Strikeforce, and PRIDE. These events are therefore excluded from UFCData.
611
767
 
768
+ <br>
769
+
612
770
  ## Update Frequency
613
771
 
614
772
  The dataset is updated at the start of the scheduled broadcast time for each event. This update captures changes to betting odds, as well as any cancelled, postponed, or otherwise modified fights.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UFCData
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Obtain UFC data and functions to manipulate it
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "UFCData"
3
- version = "0.7.0"
3
+ version = "0.7.1"
4
4
  description = "Obtain UFC data and functions to manipulate it"
5
5
  requires-python = ">=3.10"
6
6
 
@@ -181,7 +181,7 @@ def get_fighter_statistic(fighter_link, fighter_bio, fights_df, rounds_df,
181
181
 
182
182
  record = get_fighter_history(fighter_link, fighter_bio, fights_df)
183
183
 
184
- date = ("2001-01-01")
184
+ date = ("2201-01-01")
185
185
 
186
186
  if len(record) == 0:
187
187
  columns = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes