UFCData 0.2.0__tar.gz → 0.3.0__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.
- {ufcdata-0.2.0 → ufcdata-0.3.0}/PKG-INFO +1 -1
- {ufcdata-0.2.0 → ufcdata-0.3.0}/README.md +61 -18
- {ufcdata-0.2.0 → ufcdata-0.3.0}/UFCData.egg-info/PKG-INFO +1 -1
- {ufcdata-0.2.0 → ufcdata-0.3.0}/UFCData.egg-info/SOURCES.txt +1 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/pyproject.toml +1 -1
- ufcdata-0.3.0/ufcdata/__init__.py +4 -0
- ufcdata-0.3.0/ufcdata/fighter.py +701 -0
- ufcdata-0.2.0/ufcdata/__init__.py +0 -3
- {ufcdata-0.2.0 → ufcdata-0.3.0}/LICENSE.md +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/UFCData.egg-info/dependency_links.txt +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/UFCData.egg-info/requires.txt +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/UFCData.egg-info/top_level.txt +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/setup.cfg +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/ufcdata/data.py +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/ufcdata/ratings.py +0 -0
- {ufcdata-0.2.0 → ufcdata-0.3.0}/ufcdata/search.py +0 -0
|
@@ -14,23 +14,22 @@ Accessing Data
|
|
|
14
14
|
- [Loading Data](#loading-data)
|
|
15
15
|
- [Search](#search)
|
|
16
16
|
|
|
17
|
-
Rating
|
|
17
|
+
Rating Function
|
|
18
18
|
- [Elo](#elo)
|
|
19
|
-
- [Glicko-2](#glicko-2)
|
|
20
19
|
|
|
21
20
|
Fighter Functions
|
|
22
|
-
- [Fighter
|
|
21
|
+
- [Fighter History](#elo)
|
|
23
22
|
- [Fighter Statistic](#elo)
|
|
24
23
|
|
|
25
|
-
Model Evaluation
|
|
26
|
-
- [Odds Baseline Example](#elo)
|
|
27
|
-
|
|
28
24
|
Helper Functions
|
|
29
25
|
- [Odds Convert](#elo)
|
|
30
26
|
- [Weight Convert](#elo)
|
|
31
27
|
- [Gender Convert](#elo)
|
|
32
28
|
- [Mirror](#elo)
|
|
33
29
|
|
|
30
|
+
Model Evaluation
|
|
31
|
+
- [Odds Baseline Example](#elo)
|
|
32
|
+
|
|
34
33
|
Data Sources
|
|
35
34
|
- [Online Sources](#online-sources)
|
|
36
35
|
- [Discrepancies in Data](#discrepancies-in-data)
|
|
@@ -80,7 +79,8 @@ This searches the `"Name"` column of the `fighter_bio` dataframe for the three c
|
|
|
80
79
|
|
|
81
80
|
The results are returned as a dataframe, with the closest match appearing first.
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
### Returns
|
|
83
|
+
|
|
84
84
|
|
|
85
85
|
```text
|
|
86
86
|
Name Nickname Height Weight Reach Stance Total W Total L Fighter Link
|
|
@@ -116,7 +116,8 @@ ufc.get_elo(fight_df, fighter_df, r=1500, k=30, s=400)
|
|
|
116
116
|
The function returns a tuple containing:
|
|
117
117
|
|
|
118
118
|
1. `fighter_elo` — A dictionary mapping each fighter's UFCStats link to their final Elo rating.
|
|
119
|
-
2. `elo` —
|
|
119
|
+
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.
|
|
120
|
+
|
|
120
121
|
|
|
121
122
|
### Example
|
|
122
123
|
|
|
@@ -125,12 +126,12 @@ import ufcdata as ufc
|
|
|
125
126
|
|
|
126
127
|
data = ufc.get_data()
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
past_fights = data["past_fights"].copy()
|
|
130
|
+
fighter_bio = data["fighter_bio"].copy()
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
current_elo, past_elo = ufc.get_elo(
|
|
133
|
+
past_fights,
|
|
134
|
+
fighter_bio
|
|
134
135
|
)
|
|
135
136
|
```
|
|
136
137
|
|
|
@@ -141,14 +142,56 @@ The resulting `fight_elo` dataframe can then be used to examine or incorporate p
|
|
|
141
142
|
|
|
142
143
|
`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.
|
|
143
144
|
|
|
145
|
+
## Fighter History
|
|
146
|
+
|
|
147
|
+
The `get_fighter_history()` function 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.
|
|
148
|
+
|
|
149
|
+
The function also calculates the age of both fighters at the time of each fight and assigns a `"UFC Fight"` number to each fight.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
ufc.get_fighter_history(fighter_link, fighter_bio, fights_df)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Parameters
|
|
157
|
+
|
|
158
|
+
* `fighter_link` — The UFCStats link identifying the fighter.
|
|
159
|
+
* `fighter_bio` — The fighter information dataframe returned by `get_data()`.
|
|
160
|
+
* `fights_df` — The fight dataframe containing UFC fight history.
|
|
161
|
+
|
|
162
|
+
### Returns
|
|
163
|
+
|
|
164
|
+
A dataframe containing the fighter's fight history from most recent to least recent. The requested fighter is always `"Fighter 1"`, with fighter ages and `"UFC Fight"` numbers included.
|
|
144
165
|
|
|
145
|
-
## Glicko-2
|
|
146
|
-
Glicko-2 extends the Elo system by incorporating rating deviation (RD) and volatility. Unlike Elo, which represents a fighter's ability with a single rating, Glicko-2 also estimates the uncertainty and consistency of that rating.
|
|
147
166
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
|
|
168
|
+
### Example
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import ufcdata as ufc
|
|
172
|
+
|
|
173
|
+
data = ufc.get_data()
|
|
174
|
+
|
|
175
|
+
fighter_bio = data["fighter_bio"].copy()
|
|
176
|
+
fights_df = data["past_fights"].copy()
|
|
177
|
+
|
|
178
|
+
# Obtain Jon Jones' UFCStats link
|
|
179
|
+
jon_jones = ufc.search(
|
|
180
|
+
fighter_bio,
|
|
181
|
+
"Name",
|
|
182
|
+
"Jon Jones"
|
|
183
|
+
).iloc[0]["Fighter Link"]
|
|
184
|
+
|
|
185
|
+
# Get Jon Jones' fight history
|
|
186
|
+
jon_jones_history = ufc.get_fighter_history(
|
|
187
|
+
jon_jones,
|
|
188
|
+
fighter_bio,
|
|
189
|
+
fights_df
|
|
151
190
|
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This function is useful for analyzing an individual fighter's career, constructing fighter-level features, or preparing historical data for predictive modeling.
|
|
194
|
+
|
|
152
195
|
|
|
153
196
|
## Online Sources
|
|
154
197
|
|
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
from datetime import date
|
|
4
|
+
from .ratings import get_elo, expected_score, update_rating
|
|
5
|
+
|
|
6
|
+
def _mirror_helper(df, cols_1, cols_2, in_order=True):
|
|
7
|
+
df = df.copy()
|
|
8
|
+
df_original = df.copy()
|
|
9
|
+
|
|
10
|
+
# Swap each pair of columns
|
|
11
|
+
for c1, c2 in zip(cols_1, cols_2):
|
|
12
|
+
temp = df[c1].copy()
|
|
13
|
+
df[c1] = df[c2]
|
|
14
|
+
df[c2] = temp
|
|
15
|
+
|
|
16
|
+
if not in_order:
|
|
17
|
+
return pd.concat([df_original, df], ignore_index=True)
|
|
18
|
+
|
|
19
|
+
rows = []
|
|
20
|
+
for r1, r2 in zip(df_original.itertuples(index=False), df.itertuples(index=False)):
|
|
21
|
+
rows.append(r1)
|
|
22
|
+
rows.append(r2)
|
|
23
|
+
|
|
24
|
+
return pd.DataFrame(rows, columns=df.columns)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_fighter_history(fighter_link, fighter_bio, fights_df):
|
|
28
|
+
"""
|
|
29
|
+
Retrieves the fight history of a UFC fighter.
|
|
30
|
+
|
|
31
|
+
The returned DataFrame contains all fights involving the specified
|
|
32
|
+
fighter, with the fighter consistently represented as "Fighter 1".
|
|
33
|
+
The function also calculates the age of both fighters at the time
|
|
34
|
+
of each fight and assigns a chronological UFC fight number.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
fighter_link : str
|
|
39
|
+
UFCStats link identifying the fighter whose history is being
|
|
40
|
+
retrieved.
|
|
41
|
+
fighter_bio : pandas.DataFrame
|
|
42
|
+
DataFrame containing fighter information. Must contain
|
|
43
|
+
"Fighter Link" and "DOB" columns.
|
|
44
|
+
fights_df : pandas.DataFrame
|
|
45
|
+
DataFrame containing UFC fight data. Must contain fighter links,
|
|
46
|
+
fight dates, outcomes, and the other fight information required
|
|
47
|
+
by the function.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
pandas.DataFrame
|
|
52
|
+
DataFrame containing the fighter's complete fight history.
|
|
53
|
+
The requested fighter is represented as "Fighter 1" in every
|
|
54
|
+
row. The DataFrame includes the ages of both fighters at the
|
|
55
|
+
time of each fight and a "UFC Fight" column numbering the
|
|
56
|
+
fighter's fights chronologically.
|
|
57
|
+
|
|
58
|
+
Notes
|
|
59
|
+
-----
|
|
60
|
+
Fighter ages are calculated using 365.25 days per year. The
|
|
61
|
+
"UFC Fight" column counts fights from the most recent fight
|
|
62
|
+
backwards, with the most recent fight numbered 1.
|
|
63
|
+
|
|
64
|
+
Examples
|
|
65
|
+
--------
|
|
66
|
+
>>> fighter_history = get_fighter_history(
|
|
67
|
+
... fighter_link,
|
|
68
|
+
... fighter_bio,
|
|
69
|
+
... fights_df
|
|
70
|
+
... )
|
|
71
|
+
>>> fighter_history[["Date", "UFC Fight", "Fighter 1",
|
|
72
|
+
... "Fighter 1 Age"]].head()
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
fighter_history = fights_df[
|
|
77
|
+
(fights_df["Fighter 1 Link"] == fighter_link) |
|
|
78
|
+
(fights_df["Fighter 2 Link"] == fighter_link)]
|
|
79
|
+
|
|
80
|
+
fighter_history = _mirror_helper(fighter_history, ['Fighter 1', 'Fighter 1 Odds', 'Fighter 1 Link',
|
|
81
|
+
'Fighter 1 Outcome', 'Fighter 1 Bonus'],['Fighter 2', 'Fighter 2 Odds',
|
|
82
|
+
'Fighter 2 Link', 'Fighter 2 Outcome', 'Fighter 2 Bonus'])
|
|
83
|
+
|
|
84
|
+
fighter_history = fighter_history[
|
|
85
|
+
(fighter_history["Fighter 1 Link"] == fighter_link)]
|
|
86
|
+
|
|
87
|
+
fighter_history = fighter_history.merge(
|
|
88
|
+
fighter_bio[["Fighter Link", "DOB"]],
|
|
89
|
+
left_on="Fighter 1 Link",
|
|
90
|
+
right_on="Fighter Link",
|
|
91
|
+
how="left")
|
|
92
|
+
fighter_history = fighter_history.rename(columns={"DOB": "Fighter 1 Age"})
|
|
93
|
+
fighter_history = fighter_history.drop(columns=["Fighter Link"])
|
|
94
|
+
fighter_history["Fighter 1 Age"] = (pd.to_datetime(fighter_history["Date"]) - pd.to_datetime(fighter_history["Fighter 1 Age"])).dt.days / 365.25
|
|
95
|
+
|
|
96
|
+
fighter_history = fighter_history.merge(
|
|
97
|
+
fighter_bio[["Fighter Link", "DOB"]],
|
|
98
|
+
left_on="Fighter 2 Link",
|
|
99
|
+
right_on="Fighter Link",
|
|
100
|
+
how="left")
|
|
101
|
+
fighter_history = fighter_history.rename(columns={"DOB": "Fighter 2 Age"})
|
|
102
|
+
fighter_history = fighter_history.drop(columns=["Fighter Link"])
|
|
103
|
+
fighter_history["Fighter 2 Age"] = (pd.to_datetime(fighter_history["Date"]) - pd.to_datetime(fighter_history["Fighter 2 Age"])).dt.days / 365.25
|
|
104
|
+
fighter_history["UFC Fight"] = range(len(fighter_history), 0, -1)
|
|
105
|
+
fighter_history = fighter_history[['Date', "UFC Fight", 'Event Link', 'Fight Link', 'Weight Class',
|
|
106
|
+
'Gender', 'Title', 'Fighter 1', 'Fighter 1 Odds', 'Fighter 1 Age', 'Fighter 1 Link',
|
|
107
|
+
'Fighter 1 Outcome', 'Fighter 1 Bonus', 'Fighter 2', 'Fighter 2 Odds','Fighter 2 Age',
|
|
108
|
+
'Fighter 2 Link', 'Fighter 2 Outcome', 'Fighter 2 Bonus', 'Method',
|
|
109
|
+
'Round', 'Time', 'Time Format', 'Referee', 'Details']]
|
|
110
|
+
|
|
111
|
+
return fighter_history
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_fighter_statistic(fighter_link, fighter_bio, fights_df, rounds_df, future_df,
|
|
116
|
+
r=1500, k=30, s=400):
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
Generate historical and current statistics for a UFC fighter.
|
|
120
|
+
|
|
121
|
+
The function retrieves the fighter's UFC fight history and constructs
|
|
122
|
+
fight-level statistics from the fighter's perspective. It calculates
|
|
123
|
+
the fighter's UFC record, win rate, Elo rating, cumulative fight time,
|
|
124
|
+
striking statistics, takedown statistics, and submission averages using
|
|
125
|
+
only information available before each fight.
|
|
126
|
+
|
|
127
|
+
For fighters with no previous UFC fights, a baseline row is returned
|
|
128
|
+
with an initial Elo rating and zero UFC wins, losses, draws, and
|
|
129
|
+
no-contests. Historical performance statistics are left as NaN because
|
|
130
|
+
no prior fight data is available.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
fighter_link : str
|
|
135
|
+
URL or unique identifier for the fighter.
|
|
136
|
+
fighter_bio : pandas.DataFrame
|
|
137
|
+
DataFrame containing fighter biographical information, including
|
|
138
|
+
fighter names and links.
|
|
139
|
+
fights_df : pandas.DataFrame
|
|
140
|
+
DataFrame containing UFC fight-level results.
|
|
141
|
+
rounds_df : pandas.DataFrame
|
|
142
|
+
DataFrame containing round-level UFC statistics.
|
|
143
|
+
future_df : pandas.DataFrame
|
|
144
|
+
DataFrame containing the fighter's upcoming fight. The first row
|
|
145
|
+
is used to determine the date for which current statistics are
|
|
146
|
+
calculated.
|
|
147
|
+
r : float, default=1500
|
|
148
|
+
Initial Elo rating assigned to fighters with no previous Elo history.
|
|
149
|
+
k : float, default=30
|
|
150
|
+
Elo update factor.
|
|
151
|
+
s : float, default=400
|
|
152
|
+
Elo scaling factor used when calculating expected scores.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
current_statistic : pandas.DataFrame
|
|
157
|
+
One-row DataFrame containing the fighter's statistics immediately
|
|
158
|
+
before the upcoming fight. Historical statistics are calculated
|
|
159
|
+
using only fights occurring before the upcoming fight date.
|
|
160
|
+
|
|
161
|
+
past_statistic : pandas.DataFrame
|
|
162
|
+
DataFrame containing the fighter's historical statistics for each
|
|
163
|
+
previous UFC fight, with statistics calculated using only fights
|
|
164
|
+
occurring before each respective fight.
|
|
165
|
+
|
|
166
|
+
Notes
|
|
167
|
+
-----
|
|
168
|
+
The function calculates cumulative statistics rather than statistics
|
|
169
|
+
from a single fight. This prevents information from a future fight from
|
|
170
|
+
being used when generating features for an earlier fight.
|
|
171
|
+
|
|
172
|
+
Historical statistics include:
|
|
173
|
+
- UFC record and win rate
|
|
174
|
+
- Elo rating
|
|
175
|
+
- Total fight time
|
|
176
|
+
- Significant strikes landed per minute (SLpM)
|
|
177
|
+
- Significant strike accuracy and defense
|
|
178
|
+
- Significant strikes absorbed per minute (SApM)
|
|
179
|
+
- Takedown average, accuracy, and defense
|
|
180
|
+
- Submission attempts per 15 minutes
|
|
181
|
+
|
|
182
|
+
Fighters with no previous UFC fights receive an initial Elo rating of
|
|
183
|
+
`r`, while statistics requiring historical fight data are set to NaN.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
record = get_fighter_history(fighter_link, fighter_bio, fights_df)
|
|
187
|
+
|
|
188
|
+
date = future_df.iloc[0]["Date"]
|
|
189
|
+
date = pd.to_datetime(date).date()
|
|
190
|
+
|
|
191
|
+
if len(record) == 0:
|
|
192
|
+
columns = [
|
|
193
|
+
'Date', 'UFC Fight', 'Weight Class', 'Gender', 'Title', 'Fighter',
|
|
194
|
+
'Fighter Link', 'Fighter Outcome', 'UFC W', 'UFC L', 'UFC D', 'UFC NC',
|
|
195
|
+
'UFC Win Rate', 'Fighter Elo',
|
|
196
|
+
'Fight Time', 'SLpM', 'Str Acc', 'SApM',
|
|
197
|
+
'Str Def', 'TD Avg', 'TD Acc', 'TD Def', 'Sub Avg'
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
fighter_name = fighter_bio.loc[
|
|
201
|
+
fighter_bio["Fighter Link"] == fighter_link,
|
|
202
|
+
"Name"
|
|
203
|
+
].iloc[0]
|
|
204
|
+
|
|
205
|
+
df = pd.DataFrame([{
|
|
206
|
+
"Date": date,
|
|
207
|
+
"UFC Fight": 1,
|
|
208
|
+
"Weight Class": None,
|
|
209
|
+
"Gender": None,
|
|
210
|
+
"Title": None,
|
|
211
|
+
"Fighter": fighter_name,
|
|
212
|
+
"Fighter Link": fighter_link,
|
|
213
|
+
"Fighter Outcome": None,
|
|
214
|
+
"UFC W": 0,
|
|
215
|
+
"UFC L": 0,
|
|
216
|
+
"UFC D": 0,
|
|
217
|
+
"UFC NC": 0,
|
|
218
|
+
"UFC Win Rate": None,
|
|
219
|
+
"Fighter Elo": r,
|
|
220
|
+
"Fight Time": None,
|
|
221
|
+
"SLpM": None,
|
|
222
|
+
"Str Acc": None,
|
|
223
|
+
"SApM": None,
|
|
224
|
+
"Str Def": None,
|
|
225
|
+
"TD Avg": None,
|
|
226
|
+
"TD Acc": None,
|
|
227
|
+
"TD Def": None,
|
|
228
|
+
"Sub Avg": None
|
|
229
|
+
}], columns=columns)
|
|
230
|
+
|
|
231
|
+
df = df.astype(object).where(pd.notna(df), np.nan)
|
|
232
|
+
|
|
233
|
+
df["UFC Fight"] = df["UFC Fight"].astype("Int64")
|
|
234
|
+
df[["UFC W", "UFC L", "UFC D", "UFC NC"]] = (
|
|
235
|
+
df[["UFC W", "UFC L", "UFC D", "UFC NC"]].astype("Int64")
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return (df, df)
|
|
239
|
+
|
|
240
|
+
statistic = record[
|
|
241
|
+
['Date', 'UFC Fight', 'Weight Class',
|
|
242
|
+
'Gender', 'Title', 'Fighter 1',
|
|
243
|
+
'Fighter 1 Link', 'Fighter 1 Outcome']
|
|
244
|
+
].copy()
|
|
245
|
+
|
|
246
|
+
cols = [
|
|
247
|
+
"Date", "UFC Fight", "Weight Class", "Gender",
|
|
248
|
+
"Title", "Fighter 1", "Fighter 1 Link",
|
|
249
|
+
"Fighter 1 Outcome"
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
statistic[cols] = statistic[cols].iloc[::-1].to_numpy()
|
|
253
|
+
|
|
254
|
+
last = statistic.iloc[-1]
|
|
255
|
+
new_row = last.copy()
|
|
256
|
+
new_row[:] = pd.NA
|
|
257
|
+
|
|
258
|
+
new_row["Date"] = date
|
|
259
|
+
new_row["UFC Fight"] = last["UFC Fight"] + 1
|
|
260
|
+
new_row["Gender"] = last["Gender"]
|
|
261
|
+
new_row["Fighter 1"] = last["Fighter 1"]
|
|
262
|
+
new_row["Fighter 1 Link"] = last["Fighter 1 Link"]
|
|
263
|
+
|
|
264
|
+
statistic = pd.concat(
|
|
265
|
+
[statistic, new_row.to_frame().T],
|
|
266
|
+
ignore_index=True
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
statistic["Date"] = pd.to_datetime(statistic["Date"]).dt.date
|
|
270
|
+
|
|
271
|
+
statistic["UFC W"] = 0
|
|
272
|
+
statistic["UFC L"] = 0
|
|
273
|
+
statistic["UFC D"] = 0
|
|
274
|
+
statistic["UFC NC"] = 0
|
|
275
|
+
|
|
276
|
+
for i in range(1, len(statistic)):
|
|
277
|
+
|
|
278
|
+
# Carry forward previous totals
|
|
279
|
+
statistic.loc[i, "UFC W"] = statistic.loc[i-1, "UFC W"]
|
|
280
|
+
statistic.loc[i, "UFC L"] = statistic.loc[i-1, "UFC L"]
|
|
281
|
+
statistic.loc[i, "UFC D"] = statistic.loc[i-1, "UFC D"]
|
|
282
|
+
statistic.loc[i, "UFC NC"] = statistic.loc[i-1, "UFC NC"]
|
|
283
|
+
|
|
284
|
+
# Update based on previous fight result
|
|
285
|
+
result = statistic.loc[i-1, "Fighter 1 Outcome"]
|
|
286
|
+
|
|
287
|
+
if result == "W":
|
|
288
|
+
statistic.loc[i, "UFC W"] += 1
|
|
289
|
+
elif result == "L":
|
|
290
|
+
statistic.loc[i, "UFC L"] += 1
|
|
291
|
+
elif result == "D":
|
|
292
|
+
statistic.loc[i, "UFC D"] += 1
|
|
293
|
+
elif result == "NC":
|
|
294
|
+
statistic.loc[i, "UFC NC"] += 1
|
|
295
|
+
|
|
296
|
+
statistic = statistic[::-1]
|
|
297
|
+
|
|
298
|
+
statistic[
|
|
299
|
+
["UFC W", "UFC L", "UFC D", "UFC NC"]
|
|
300
|
+
] = (
|
|
301
|
+
statistic[
|
|
302
|
+
["UFC W", "UFC L", "UFC D", "UFC NC"]
|
|
303
|
+
].iloc[::-1].reset_index(drop=True)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
statistic["UFC Win Rate"] = (
|
|
307
|
+
statistic["UFC W"] /
|
|
308
|
+
(
|
|
309
|
+
statistic["UFC L"]
|
|
310
|
+
+ statistic["UFC D"]
|
|
311
|
+
+ statistic["UFC NC"]
|
|
312
|
+
+ statistic["UFC W"]
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Elo
|
|
317
|
+
current_elo,elo = get_elo(fights_df, fighter_bio, r, k, s)
|
|
318
|
+
|
|
319
|
+
elo = elo[
|
|
320
|
+
(elo["Fighter 1 Link"] == fighter_link) |
|
|
321
|
+
(elo["Fighter 2 Link"] == fighter_link)
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
elo = elo[
|
|
325
|
+
[
|
|
326
|
+
"Fighter 1", "Fighter 1 Elo", "Fighter 1 Outcome",
|
|
327
|
+
"Fighter 1 Link",
|
|
328
|
+
"Fighter 2", "Fighter 2 Elo", "Fighter 2 Outcome",
|
|
329
|
+
"Fighter 2 Link"
|
|
330
|
+
]
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
elo = _mirror_helper(
|
|
334
|
+
elo,
|
|
335
|
+
[
|
|
336
|
+
"Fighter 1", "Fighter 1 Elo",
|
|
337
|
+
"Fighter 1 Outcome", "Fighter 1 Link"
|
|
338
|
+
],
|
|
339
|
+
[
|
|
340
|
+
"Fighter 2", "Fighter 2 Elo",
|
|
341
|
+
"Fighter 2 Outcome", "Fighter 2 Link"
|
|
342
|
+
]
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
elo = elo[
|
|
346
|
+
elo["Fighter 1 Link"] == fighter_link
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
elo = elo.reset_index(drop=True)
|
|
350
|
+
|
|
351
|
+
new_row = pd.DataFrame(
|
|
352
|
+
[[np.nan] * len(elo.columns)],
|
|
353
|
+
columns=elo.columns
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
elo = pd.concat(
|
|
357
|
+
[new_row, elo],
|
|
358
|
+
ignore_index=True
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
fighter_elo = elo.at[1, "Fighter 1 Elo"]
|
|
362
|
+
opponent_elo = elo.at[1, "Fighter 2 Elo"]
|
|
363
|
+
fighter_outcome = elo.at[1, "Fighter 1 Outcome"]
|
|
364
|
+
|
|
365
|
+
if fighter_outcome == "W":
|
|
366
|
+
expected_1 = expected_score(
|
|
367
|
+
fighter_elo,
|
|
368
|
+
opponent_elo,
|
|
369
|
+
s
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
elo_1_new = update_rating(
|
|
373
|
+
fighter_elo,
|
|
374
|
+
k,
|
|
375
|
+
1,
|
|
376
|
+
expected_1
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
elo.at[0, "Fighter 1 Elo"] = elo_1_new
|
|
380
|
+
|
|
381
|
+
elif fighter_outcome == "L":
|
|
382
|
+
expected_1 = expected_score(
|
|
383
|
+
fighter_elo,
|
|
384
|
+
opponent_elo,
|
|
385
|
+
s
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
elo_1_new = update_rating(
|
|
389
|
+
fighter_elo,
|
|
390
|
+
k,
|
|
391
|
+
0,
|
|
392
|
+
expected_1
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
elo.at[0, "Fighter 1 Elo"] = elo_1_new
|
|
396
|
+
|
|
397
|
+
elif fighter_outcome == "D":
|
|
398
|
+
expected_1 = expected_score(
|
|
399
|
+
fighter_elo,
|
|
400
|
+
opponent_elo,
|
|
401
|
+
s
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
elo_1_new = update_rating(
|
|
405
|
+
fighter_elo,
|
|
406
|
+
k,
|
|
407
|
+
0.5,
|
|
408
|
+
expected_1
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
elo.at[0, "Fighter 1 Elo"] = elo_1_new
|
|
412
|
+
|
|
413
|
+
elif fighter_outcome == "NC":
|
|
414
|
+
elo.at[0, "Fighter 1 Elo"] = fighter_elo
|
|
415
|
+
|
|
416
|
+
elo = elo["Fighter 1 Elo"]
|
|
417
|
+
elo = elo.iloc[::-1].reset_index(drop=True)
|
|
418
|
+
|
|
419
|
+
statistic = pd.concat(
|
|
420
|
+
[statistic, elo],
|
|
421
|
+
axis=1
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Get fight statistics
|
|
425
|
+
fight_time = get_fighter_history(
|
|
426
|
+
fighter_link,
|
|
427
|
+
fighter_bio,
|
|
428
|
+
fights_df
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
fight_time["new_time"] = fight_time["Time"].apply(
|
|
432
|
+
lambda x: int(x.split(":")[0]) * 60
|
|
433
|
+
+ int(x.split(":")[1])
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
fight_time["new_round"] = (
|
|
437
|
+
(fight_time["Round"] - 1) * 60 * 5
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
fight_time["Fight Time"] = (
|
|
441
|
+
fight_time["new_time"]
|
|
442
|
+
+ fight_time["new_round"]
|
|
443
|
+
) / 60
|
|
444
|
+
|
|
445
|
+
statistic["Fight Time"] = statistic["Date"].apply(
|
|
446
|
+
lambda d: fight_time.loc[
|
|
447
|
+
fight_time["Date"].dt.date < d,
|
|
448
|
+
"Fight Time"
|
|
449
|
+
].sum()
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
rounds = rounds_df
|
|
453
|
+
|
|
454
|
+
rounds = rounds[
|
|
455
|
+
(rounds["Fighter 1 Link"] == fighter_link) |
|
|
456
|
+
(rounds["Fighter 2 Link"] == fighter_link)
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
rounds = _mirror_helper(
|
|
460
|
+
rounds,
|
|
461
|
+
[
|
|
462
|
+
"Fighter 1", "Fighter 1 Link",
|
|
463
|
+
"Fighter 1 TD", "Fighter 1 Sub Att",
|
|
464
|
+
"Fighter 1 Rev", "Fighter 1 Ctrl",
|
|
465
|
+
"Fighter 1 KD", "Fighter 1 Total SS"
|
|
466
|
+
],
|
|
467
|
+
[
|
|
468
|
+
"Fighter 2", "Fighter 2 Link",
|
|
469
|
+
"Fighter 2 TD", "Fighter 2 Sub Att",
|
|
470
|
+
"Fighter 2 Rev", "Fighter 2 Ctrl",
|
|
471
|
+
"Fighter 2 KD", "Fighter 2 Total SS"
|
|
472
|
+
]
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
rounds = rounds[
|
|
476
|
+
rounds["Fighter 1 Link"] == fighter_link
|
|
477
|
+
]
|
|
478
|
+
|
|
479
|
+
rounds["Sig landed"] = (
|
|
480
|
+
rounds["Fighter 1 Total SS"]
|
|
481
|
+
.str.split()
|
|
482
|
+
.str[0]
|
|
483
|
+
.astype(int)
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
rounds["Sig att"] = (
|
|
487
|
+
rounds["Fighter 1 Total SS"]
|
|
488
|
+
.str.split()
|
|
489
|
+
.str[2]
|
|
490
|
+
.astype(int)
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
rounds["Strike acc"] = (
|
|
494
|
+
rounds["Sig landed"] / rounds["Sig att"]
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
rounds["Strikes absorbed"] = (
|
|
498
|
+
rounds["Fighter 2 Total SS"]
|
|
499
|
+
.str.split()
|
|
500
|
+
.str[0]
|
|
501
|
+
.astype(int)
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
rounds["Opp sig att"] = (
|
|
505
|
+
rounds["Fighter 2 Total SS"]
|
|
506
|
+
.str.split()
|
|
507
|
+
.str[2]
|
|
508
|
+
.astype(int)
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
statistic["SLpM"] = statistic["Date"].apply(
|
|
512
|
+
lambda d: rounds.loc[
|
|
513
|
+
rounds["Date"].dt.date < d,
|
|
514
|
+
"Sig landed"
|
|
515
|
+
].sum()
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
statistic["SLpM"] = (
|
|
519
|
+
statistic["SLpM"] / statistic["Fight Time"]
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
statistic["Sig landed"] = statistic["Date"].apply(
|
|
523
|
+
lambda d: rounds.loc[
|
|
524
|
+
rounds["Date"].dt.date < d,
|
|
525
|
+
"Sig landed"
|
|
526
|
+
].sum()
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
statistic["Sig att"] = statistic["Date"].apply(
|
|
530
|
+
lambda d: rounds.loc[
|
|
531
|
+
rounds["Date"].dt.date < d,
|
|
532
|
+
"Sig att"
|
|
533
|
+
].sum()
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
statistic["Str Acc"] = (
|
|
537
|
+
statistic["Sig landed"] /
|
|
538
|
+
statistic["Sig att"]
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
statistic["SApM"] = statistic["Date"].apply(
|
|
542
|
+
lambda d: rounds.loc[
|
|
543
|
+
rounds["Date"].dt.date < d,
|
|
544
|
+
"Strikes absorbed"
|
|
545
|
+
].sum()
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
statistic["SApM"] = (
|
|
549
|
+
statistic["SApM"] / statistic["Fight Time"]
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
statistic["Strikes absorbed"] = statistic["Date"].apply(
|
|
553
|
+
lambda d: rounds.loc[
|
|
554
|
+
rounds["Date"].dt.date < d,
|
|
555
|
+
"Strikes absorbed"
|
|
556
|
+
].sum()
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
statistic["Opp sig att"] = statistic["Date"].apply(
|
|
560
|
+
lambda d: rounds.loc[
|
|
561
|
+
rounds["Date"].dt.date < d,
|
|
562
|
+
"Opp sig att"
|
|
563
|
+
].sum()
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
statistic["Str Def"] = 1 - (
|
|
567
|
+
statistic["Strikes absorbed"] /
|
|
568
|
+
statistic["Opp sig att"]
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
rounds["Td"] = (
|
|
572
|
+
rounds["Fighter 1 TD"]
|
|
573
|
+
.str.split()
|
|
574
|
+
.str[0]
|
|
575
|
+
.astype(int)
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
rounds["TD landed"] = (
|
|
579
|
+
rounds["Fighter 1 TD"]
|
|
580
|
+
.str.split()
|
|
581
|
+
.str[0]
|
|
582
|
+
.astype(int)
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
rounds["TD att"] = (
|
|
586
|
+
rounds["Fighter 1 TD"]
|
|
587
|
+
.str.split()
|
|
588
|
+
.str[2]
|
|
589
|
+
.astype(int)
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
rounds["TD absorbed"] = (
|
|
593
|
+
rounds["Fighter 2 TD"]
|
|
594
|
+
.str.split()
|
|
595
|
+
.str[0]
|
|
596
|
+
.astype(int)
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
rounds["Opp TD att"] = (
|
|
600
|
+
rounds["Fighter 2 TD"]
|
|
601
|
+
.str.split()
|
|
602
|
+
.str[2]
|
|
603
|
+
.astype(int)
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
rounds["Sub att"] = rounds["Fighter 1 Sub Att"]
|
|
607
|
+
|
|
608
|
+
statistic["TD Avg"] = statistic["Date"].apply(
|
|
609
|
+
lambda d: rounds.loc[
|
|
610
|
+
rounds["Date"].dt.date < d,
|
|
611
|
+
"Td"
|
|
612
|
+
].sum()
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
statistic["TD Avg"] = (
|
|
616
|
+
statistic["TD Avg"] /
|
|
617
|
+
statistic["Fight Time"] * 15
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
statistic["TD landed"] = statistic["Date"].apply(
|
|
621
|
+
lambda d: rounds.loc[
|
|
622
|
+
rounds["Date"].dt.date < d,
|
|
623
|
+
"TD landed"
|
|
624
|
+
].sum()
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
statistic["TD att"] = statistic["Date"].apply(
|
|
628
|
+
lambda d: rounds.loc[
|
|
629
|
+
rounds["Date"].dt.date < d,
|
|
630
|
+
"TD att"
|
|
631
|
+
].sum()
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
statistic["TD absorbed"] = statistic["Date"].apply(
|
|
635
|
+
lambda d: rounds.loc[
|
|
636
|
+
rounds["Date"].dt.date < d,
|
|
637
|
+
"TD absorbed"
|
|
638
|
+
].sum()
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
statistic["Opp TD att"] = statistic["Date"].apply(
|
|
642
|
+
lambda d: rounds.loc[
|
|
643
|
+
rounds["Date"].dt.date < d,
|
|
644
|
+
"Opp TD att"
|
|
645
|
+
].sum()
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
statistic["TD Acc"] = np.where(
|
|
649
|
+
statistic["TD att"] > 0,
|
|
650
|
+
statistic["TD landed"] /
|
|
651
|
+
statistic["TD att"],
|
|
652
|
+
np.nan
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
statistic["TD Def"] = np.where(
|
|
656
|
+
statistic["Opp TD att"] > 0,
|
|
657
|
+
1 - (
|
|
658
|
+
statistic["TD absorbed"] /
|
|
659
|
+
statistic["Opp TD att"]
|
|
660
|
+
),
|
|
661
|
+
np.nan
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
statistic["Sub Avg"] = statistic["Date"].apply(
|
|
665
|
+
lambda d: rounds.loc[
|
|
666
|
+
rounds["Date"].dt.date < d,
|
|
667
|
+
"Sub att"
|
|
668
|
+
].sum()
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
statistic["Sub Avg"] = (
|
|
672
|
+
statistic["Sub Avg"] /
|
|
673
|
+
statistic["Fight Time"] * 15
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
statistic = statistic.drop(
|
|
677
|
+
columns=[
|
|
678
|
+
"Sig landed",
|
|
679
|
+
"Sig att",
|
|
680
|
+
"Strikes absorbed",
|
|
681
|
+
"Opp sig att",
|
|
682
|
+
"TD landed",
|
|
683
|
+
"TD att",
|
|
684
|
+
"TD absorbed",
|
|
685
|
+
"Opp TD att"
|
|
686
|
+
]
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
statistic.columns = statistic.columns.str.replace(
|
|
690
|
+
"Fighter 1",
|
|
691
|
+
"Fighter",
|
|
692
|
+
regex=False
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
statistic = statistic.reset_index(drop=True)
|
|
696
|
+
|
|
697
|
+
current_statistic = statistic.iloc[[0]].copy()
|
|
698
|
+
current_statistic["Date"] = np.nan
|
|
699
|
+
past_statistic = statistic.iloc[1:].reset_index(drop=True)
|
|
700
|
+
|
|
701
|
+
return (current_statistic, past_statistic)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|