impectPy 2.4.4__py3-none-any.whl
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.
- impectPy/__init__.py +19 -0
- impectPy/access_token.py +38 -0
- impectPy/config.py +4 -0
- impectPy/events.py +516 -0
- impectPy/helpers.py +284 -0
- impectPy/impect.py +132 -0
- impectPy/iteration_averages.py +382 -0
- impectPy/iterations.py +93 -0
- impectPy/match_info.py +647 -0
- impectPy/matches.py +164 -0
- impectPy/matchsums.py +504 -0
- impectPy/player_profile_scores.py +260 -0
- impectPy/player_scores.py +588 -0
- impectPy/set_pieces.py +281 -0
- impectPy/squad_ratings.py +129 -0
- impectPy/squad_scores.py +359 -0
- impectPy/xml.py +990 -0
- impectPy-2.4.4.dist-info/LICENSE.md +21 -0
- impectPy-2.4.4.dist-info/METADATA +424 -0
- impectPy-2.4.4.dist-info/RECORD +22 -0
- impectPy-2.4.4.dist-info/WHEEL +5 -0
- impectPy-2.4.4.dist-info/top_level.txt +1 -0
impectPy/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# define version attribute
|
|
2
|
+
__version__ = "2.4.4"
|
|
3
|
+
|
|
4
|
+
# import modules
|
|
5
|
+
from .access_token import getAccessToken
|
|
6
|
+
from .iterations import getIterations
|
|
7
|
+
from .matches import getMatches
|
|
8
|
+
from .events import getEvents
|
|
9
|
+
from .matchsums import getPlayerMatchsums, getSquadMatchsums
|
|
10
|
+
from .iteration_averages import getPlayerIterationAverages, getSquadIterationAverages
|
|
11
|
+
from .player_scores import getPlayerMatchScores, getPlayerIterationScores
|
|
12
|
+
from .squad_scores import getSquadMatchScores, getSquadIterationScores
|
|
13
|
+
from .player_profile_scores import getPlayerProfileScores
|
|
14
|
+
from .xml import generateXML
|
|
15
|
+
from .set_pieces import getSetPieces
|
|
16
|
+
from .squad_ratings import getSquadRatings
|
|
17
|
+
from .match_info import getFormations, getSubstitutions, getStartingPositions
|
|
18
|
+
from .config import Config as Config
|
|
19
|
+
from .impect import Impect as Impect
|
impectPy/access_token.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# load packages
|
|
2
|
+
import urllib
|
|
3
|
+
import requests
|
|
4
|
+
from impectPy.helpers import RateLimitedAPI
|
|
5
|
+
|
|
6
|
+
######
|
|
7
|
+
#
|
|
8
|
+
# This function returns an access token for the external API
|
|
9
|
+
#
|
|
10
|
+
######
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# define function
|
|
14
|
+
def getAccessToken(username: str, password: str, session: requests.Session = requests.Session()) -> str:
|
|
15
|
+
|
|
16
|
+
# create an instance of RateLimitedAPI
|
|
17
|
+
connection = RateLimitedAPI(session)
|
|
18
|
+
|
|
19
|
+
return getAccessTokenFromUrl(username, password, connection, "https://login.impect.com/auth/realms/production/protocol/openid-connect/token")
|
|
20
|
+
|
|
21
|
+
def getAccessTokenFromUrl(username: str, password: str, connection: RateLimitedAPI, token_url: str) -> str:
|
|
22
|
+
|
|
23
|
+
# define request parameters
|
|
24
|
+
login = 'client_id=api&grant_type=password&username=' + urllib.parse.quote(
|
|
25
|
+
username) + '&password=' + urllib.parse.quote(password)
|
|
26
|
+
|
|
27
|
+
# define request headers
|
|
28
|
+
connection.session.headers.update({"body": login, "Content-Type": "application/x-www-form-urlencoded"})
|
|
29
|
+
|
|
30
|
+
# request access token
|
|
31
|
+
response = connection.make_api_request(url=token_url, method="POST", data=login)
|
|
32
|
+
|
|
33
|
+
# remove headers again
|
|
34
|
+
connection.session.headers.clear()
|
|
35
|
+
|
|
36
|
+
# get access token from response and return it
|
|
37
|
+
token = response.json()["access_token"]
|
|
38
|
+
return token
|
impectPy/config.py
ADDED
impectPy/events.py
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# load packages
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import requests
|
|
5
|
+
from impectPy.helpers import RateLimitedAPI
|
|
6
|
+
from .matches import getMatchesFromHost
|
|
7
|
+
from .iterations import getIterationsFromHost
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
######
|
|
11
|
+
#
|
|
12
|
+
# This function returns a pandas dataframe that contains all events for a
|
|
13
|
+
# given match
|
|
14
|
+
#
|
|
15
|
+
######
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def getEvents(
|
|
19
|
+
matches: list, token: str, include_kpis: bool = True,
|
|
20
|
+
include_set_pieces: bool = True, session: requests.Session = requests.Session()
|
|
21
|
+
) -> pd.DataFrame:
|
|
22
|
+
|
|
23
|
+
# create an instance of RateLimitedAPI
|
|
24
|
+
connection = RateLimitedAPI(session)
|
|
25
|
+
|
|
26
|
+
# construct header with access token
|
|
27
|
+
connection.session.headers.update({"Authorization": f"Bearer {token}"})
|
|
28
|
+
|
|
29
|
+
return getEventsFromHost(matches, include_kpis, include_set_pieces, connection, "https://api.impect.com")
|
|
30
|
+
|
|
31
|
+
# define function
|
|
32
|
+
def getEventsFromHost(
|
|
33
|
+
matches: list, include_kpis: bool, include_set_pieces: bool, connection: RateLimitedAPI, host: str
|
|
34
|
+
) -> pd.DataFrame:
|
|
35
|
+
|
|
36
|
+
# check input for matches argument
|
|
37
|
+
if not isinstance(matches, list):
|
|
38
|
+
raise Exception("Argument 'matches' must be a list of integers.")
|
|
39
|
+
|
|
40
|
+
# get match info
|
|
41
|
+
iterations = pd.concat(
|
|
42
|
+
map(lambda match: connection.make_api_request_limited(
|
|
43
|
+
url=f"{host}/v5/customerapi/matches/{match}",
|
|
44
|
+
method="GET"
|
|
45
|
+
).process_response(
|
|
46
|
+
endpoint="Iterations"
|
|
47
|
+
),
|
|
48
|
+
matches),
|
|
49
|
+
ignore_index=True)
|
|
50
|
+
|
|
51
|
+
# filter for matches that are unavailable
|
|
52
|
+
fail_matches = iterations[iterations.lastCalculationDate.isnull()].id.drop_duplicates().to_list()
|
|
53
|
+
|
|
54
|
+
# drop matches that are unavailable from list of matches
|
|
55
|
+
matches = [match for match in matches if match not in fail_matches]
|
|
56
|
+
|
|
57
|
+
# raise exception if no matches remaining or report removed matches
|
|
58
|
+
if len(fail_matches) > 0:
|
|
59
|
+
if len(matches) == 0:
|
|
60
|
+
raise Exception("All supplied matches are unavailable. Execution stopped.")
|
|
61
|
+
else:
|
|
62
|
+
print(f"The following matches are not available yet and were ignored:\n{fail_matches}")
|
|
63
|
+
|
|
64
|
+
# extract iterationIds
|
|
65
|
+
iterations = list(iterations[iterations.lastCalculationDate.notnull()].iterationId.unique())
|
|
66
|
+
|
|
67
|
+
# get match events
|
|
68
|
+
events = pd.concat(
|
|
69
|
+
map(lambda match: connection.make_api_request_limited(
|
|
70
|
+
url=f"{host}/v5/customerapi/matches/{match}/events",
|
|
71
|
+
method="GET"
|
|
72
|
+
).process_response(
|
|
73
|
+
endpoint="Events"
|
|
74
|
+
).assign(
|
|
75
|
+
matchId=match
|
|
76
|
+
),
|
|
77
|
+
matches),
|
|
78
|
+
ignore_index=True)
|
|
79
|
+
|
|
80
|
+
# account for matches without dribbles, duels or opponents tagged
|
|
81
|
+
attributes = [
|
|
82
|
+
"dribbleDistance",
|
|
83
|
+
"dribbleType",
|
|
84
|
+
"dribbleResult",
|
|
85
|
+
"dribblePlayerId",
|
|
86
|
+
"duelDuelType",
|
|
87
|
+
"duelPlayerId",
|
|
88
|
+
"opponentCoordinatesX",
|
|
89
|
+
"opponentCoordinatesY",
|
|
90
|
+
"opponentAdjCoordinatesX",
|
|
91
|
+
"opponentAdjCoordinatesY"
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
# add attribute if it doesn't exist in df
|
|
95
|
+
for attribute in attributes:
|
|
96
|
+
if attribute not in events.columns:
|
|
97
|
+
events[attribute] = np.nan
|
|
98
|
+
|
|
99
|
+
# get players
|
|
100
|
+
players = pd.concat(
|
|
101
|
+
map(lambda iteration: connection.make_api_request_limited(
|
|
102
|
+
url=f"{host}/v5/customerapi/iterations/{iteration}/players",
|
|
103
|
+
method="GET"
|
|
104
|
+
).process_response(
|
|
105
|
+
endpoint="Players"
|
|
106
|
+
),
|
|
107
|
+
iterations),
|
|
108
|
+
ignore_index=True)[["id", "commonname"]].drop_duplicates()
|
|
109
|
+
|
|
110
|
+
# get squads
|
|
111
|
+
squads = pd.concat(
|
|
112
|
+
map(lambda iteration: connection.make_api_request_limited(
|
|
113
|
+
url=f"{host}/v5/customerapi/iterations/{iteration}/squads",
|
|
114
|
+
method="GET"
|
|
115
|
+
).process_response(
|
|
116
|
+
endpoint="Squads"
|
|
117
|
+
),
|
|
118
|
+
iterations),
|
|
119
|
+
ignore_index=True)[["id", "name"]].drop_duplicates()
|
|
120
|
+
|
|
121
|
+
# get matches
|
|
122
|
+
matchplan = pd.concat(
|
|
123
|
+
map(lambda iteration: getMatchesFromHost(
|
|
124
|
+
iteration=iteration,
|
|
125
|
+
connection=connection,
|
|
126
|
+
host=host
|
|
127
|
+
),
|
|
128
|
+
iterations),
|
|
129
|
+
ignore_index=True)
|
|
130
|
+
|
|
131
|
+
# get iterations
|
|
132
|
+
iterations = getIterationsFromHost(connection=connection, host=host)
|
|
133
|
+
|
|
134
|
+
if include_kpis:
|
|
135
|
+
# get event scorings
|
|
136
|
+
scorings = pd.concat(
|
|
137
|
+
map(lambda match: connection.make_api_request_limited(
|
|
138
|
+
url=f"{host}/v5/customerapi/matches/{match}/event-kpis",
|
|
139
|
+
method="GET"
|
|
140
|
+
).process_response(
|
|
141
|
+
endpoint="Scorings"
|
|
142
|
+
),
|
|
143
|
+
matches),
|
|
144
|
+
ignore_index=True)
|
|
145
|
+
|
|
146
|
+
# get kpis
|
|
147
|
+
kpis = connection.make_api_request_limited(
|
|
148
|
+
url=f"{host}/v5/customerapi/kpis/event",
|
|
149
|
+
method="GET"
|
|
150
|
+
).process_response(
|
|
151
|
+
endpoint="EventKPIs"
|
|
152
|
+
)[["id", "name"]]
|
|
153
|
+
|
|
154
|
+
if include_set_pieces:
|
|
155
|
+
# get set piece data
|
|
156
|
+
set_pieces = pd.concat(
|
|
157
|
+
map(lambda match: connection.make_api_request_limited(
|
|
158
|
+
url=f"{host}/v5/customerapi/matches/{match}/set-pieces",
|
|
159
|
+
method="GET"
|
|
160
|
+
).process_response(
|
|
161
|
+
endpoint="Set-Pieces"
|
|
162
|
+
),
|
|
163
|
+
matches),
|
|
164
|
+
ignore_index=True
|
|
165
|
+
).rename(
|
|
166
|
+
columns={"id": "setPieceId"}
|
|
167
|
+
).explode("setPieceSubPhase", ignore_index=True)
|
|
168
|
+
|
|
169
|
+
# unpack setPieceSubPhase column
|
|
170
|
+
set_pieces = pd.concat(
|
|
171
|
+
[
|
|
172
|
+
set_pieces.drop(columns=["setPieceSubPhase"]),
|
|
173
|
+
pd.json_normalize(set_pieces["setPieceSubPhase"]).add_prefix("setPieceSubPhase.")
|
|
174
|
+
],
|
|
175
|
+
axis=1
|
|
176
|
+
).rename(columns=lambda x: re.sub(r"\.(.)", lambda y: y.group(1).upper(), x))
|
|
177
|
+
|
|
178
|
+
# fix potential typing issues
|
|
179
|
+
events.pressingPlayerId = events.pressingPlayerId.astype("Int64")
|
|
180
|
+
events.fouledPlayerId = events.fouledPlayerId.astype("Int64")
|
|
181
|
+
events.passReceiverPlayerId = events.passReceiverPlayerId.astype("Int64")
|
|
182
|
+
events.duelPlayerId = events.duelPlayerId.astype("Int64")
|
|
183
|
+
events.fouledPlayerId = events.fouledPlayerId.astype("Int64")
|
|
184
|
+
if include_set_pieces:
|
|
185
|
+
set_pieces.setPieceSubPhaseMainEventPlayerId = set_pieces.setPieceSubPhaseMainEventPlayerId.astype("Int64")
|
|
186
|
+
set_pieces.setPieceSubPhaseFirstTouchPlayerId = set_pieces.setPieceSubPhaseFirstTouchPlayerId.astype("Int64")
|
|
187
|
+
set_pieces.setPieceSubPhaseSecondTouchPlayerId = set_pieces.setPieceSubPhaseSecondTouchPlayerId.astype("Int64")
|
|
188
|
+
|
|
189
|
+
# start merging dfs
|
|
190
|
+
|
|
191
|
+
# merge events with squads
|
|
192
|
+
events = events.merge(
|
|
193
|
+
squads[["id", "name"]].rename(columns={"id": "squadId", "name": "squadName"}),
|
|
194
|
+
left_on="squadId",
|
|
195
|
+
right_on="squadId",
|
|
196
|
+
how="left",
|
|
197
|
+
suffixes=("", "_home")
|
|
198
|
+
).merge(
|
|
199
|
+
squads[["id", "name"]].rename(columns={"id": "squadId", "name": "currentAttackingSquadName"}),
|
|
200
|
+
left_on="currentAttackingSquadId",
|
|
201
|
+
right_on="squadId",
|
|
202
|
+
how="left",
|
|
203
|
+
suffixes=("", "_away")
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# merge events with players
|
|
207
|
+
events = events.merge(
|
|
208
|
+
players[["id", "commonname"]].rename(columns={"id": "playerId", "commonname": "playerName"}),
|
|
209
|
+
left_on="playerId",
|
|
210
|
+
right_on="playerId",
|
|
211
|
+
how="left",
|
|
212
|
+
suffixes=("", "_right")
|
|
213
|
+
).merge(
|
|
214
|
+
players[["id", "commonname"]].rename(
|
|
215
|
+
columns={"id": "pressingPlayerId", "commonname": "pressingPlayerName"}),
|
|
216
|
+
left_on="pressingPlayerId",
|
|
217
|
+
right_on="pressingPlayerId",
|
|
218
|
+
how="left",
|
|
219
|
+
suffixes=("", "_right")
|
|
220
|
+
).merge(
|
|
221
|
+
players[["id", "commonname"]].rename(columns={"id": "fouledPlayerId", "commonname": "fouledPlayerName"}),
|
|
222
|
+
left_on="fouledPlayerId",
|
|
223
|
+
right_on="fouledPlayerId",
|
|
224
|
+
how="left",
|
|
225
|
+
suffixes=("", "_right")
|
|
226
|
+
).merge(
|
|
227
|
+
players[["id", "commonname"]].rename(columns={"id": "duelPlayerId", "commonname": "duelPlayerName"}),
|
|
228
|
+
left_on="duelPlayerId",
|
|
229
|
+
right_on="duelPlayerId",
|
|
230
|
+
how="left",
|
|
231
|
+
suffixes=("", "_right")
|
|
232
|
+
).merge(
|
|
233
|
+
players[["id", "commonname"]].rename(
|
|
234
|
+
columns={"id": "passReceiverPlayerId", "commonname": "passReceiverPlayerName"}),
|
|
235
|
+
left_on="passReceiverPlayerId",
|
|
236
|
+
right_on="passReceiverPlayerId",
|
|
237
|
+
how="left",
|
|
238
|
+
suffixes=("", "_right")
|
|
239
|
+
).merge(
|
|
240
|
+
players[["id", "commonname"]].rename(
|
|
241
|
+
columns={"id": "dribbleOpponentPlayerId", "commonname": "dribbleOpponentPlayerName"}),
|
|
242
|
+
left_on="dribblePlayerId",
|
|
243
|
+
right_on="dribbleOpponentPlayerId",
|
|
244
|
+
how="left",
|
|
245
|
+
suffixes=("", "_right")
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# merge with matches info
|
|
249
|
+
events = events.merge(
|
|
250
|
+
matchplan,
|
|
251
|
+
left_on="matchId",
|
|
252
|
+
right_on="id",
|
|
253
|
+
how="left",
|
|
254
|
+
suffixes=("", "_right")
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# merge with competition info
|
|
258
|
+
events = events.merge(
|
|
259
|
+
iterations,
|
|
260
|
+
left_on="iterationId",
|
|
261
|
+
right_on="id",
|
|
262
|
+
how="left",
|
|
263
|
+
suffixes=("", "_right")
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if include_kpis:
|
|
267
|
+
# unnest scorings and full join with kpi list to ensure all kpis are present
|
|
268
|
+
scorings = scorings.merge(kpis, left_on="kpiId", right_on="id", how="outer") \
|
|
269
|
+
.sort_values("kpiId") \
|
|
270
|
+
.drop("kpiId", axis=1) \
|
|
271
|
+
.fillna({"eventId": "", "position": "", "playerId": ""}) \
|
|
272
|
+
.pivot_table(index=["eventId", "position", "playerId"], columns="name", values="value", aggfunc="sum",
|
|
273
|
+
fill_value=None) \
|
|
274
|
+
.reset_index() \
|
|
275
|
+
.loc[lambda df: df["eventId"].notna()]
|
|
276
|
+
|
|
277
|
+
# Replace empty strings with None in the eventId and playerId column
|
|
278
|
+
scorings["eventId"] = scorings["eventId"].mask(scorings["eventId"] == "", None)
|
|
279
|
+
scorings["playerId"] = scorings["playerId"].mask(scorings["playerId"] == "", None)
|
|
280
|
+
events["playerId"] = events["playerId"].mask(events["playerId"] == "", None)
|
|
281
|
+
|
|
282
|
+
# Convert column eventId from float to int
|
|
283
|
+
scorings["eventId"] = scorings["eventId"].astype(pd.Int64Dtype())
|
|
284
|
+
scorings["playerId"] = scorings["playerId"].astype(pd.Int64Dtype())
|
|
285
|
+
events["playerId"] = events["playerId"].astype(pd.Int64Dtype())
|
|
286
|
+
|
|
287
|
+
# merge events and scorings
|
|
288
|
+
events = events.merge(scorings,
|
|
289
|
+
left_on=["playerPosition", "playerId", "id"],
|
|
290
|
+
right_on=["position", "playerId", "eventId"],
|
|
291
|
+
how="left",
|
|
292
|
+
suffixes=("", "_scorings"))
|
|
293
|
+
|
|
294
|
+
if include_set_pieces:
|
|
295
|
+
events = events.merge(
|
|
296
|
+
set_pieces,
|
|
297
|
+
left_on=["setPieceId", "setPieceSubPhaseId"],
|
|
298
|
+
right_on=["setPieceId", "setPieceSubPhaseId"],
|
|
299
|
+
how="left",
|
|
300
|
+
suffixes=("", "_right")
|
|
301
|
+
).merge(
|
|
302
|
+
players[["id", "commonname"]].rename(
|
|
303
|
+
columns={
|
|
304
|
+
"id": "setPieceSubPhaseMainEventPlayerId",
|
|
305
|
+
"commonname": "setPieceSubPhaseMainEventPlayerName"
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
left_on="setPieceSubPhaseMainEventPlayerId",
|
|
309
|
+
right_on="setPieceSubPhaseMainEventPlayerId",
|
|
310
|
+
how="left",
|
|
311
|
+
suffixes=("", "_right")
|
|
312
|
+
).merge(
|
|
313
|
+
players[["id", "commonname"]].rename(
|
|
314
|
+
columns={
|
|
315
|
+
"id": "setPieceSubPhasePassReceiverId",
|
|
316
|
+
"commonname": "setPieceSubPhasePassReceiverName"
|
|
317
|
+
}
|
|
318
|
+
),
|
|
319
|
+
left_on="setPieceSubPhasePassReceiverId",
|
|
320
|
+
right_on="setPieceSubPhasePassReceiverId",
|
|
321
|
+
how="left",
|
|
322
|
+
suffixes=("", "_right")
|
|
323
|
+
).merge(
|
|
324
|
+
players[["id", "commonname"]].rename(
|
|
325
|
+
columns={
|
|
326
|
+
"id": "setPieceSubPhaseFirstTouchPlayerId",
|
|
327
|
+
"commonname": "setPieceSubPhaseFirstTouchPlayerName"
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
left_on="setPieceSubPhaseFirstTouchPlayerId",
|
|
331
|
+
right_on="setPieceSubPhaseFirstTouchPlayerId",
|
|
332
|
+
how="left",
|
|
333
|
+
suffixes=("", "_right")
|
|
334
|
+
).merge(
|
|
335
|
+
players[["id", "commonname"]].rename(
|
|
336
|
+
columns={
|
|
337
|
+
"id": "setPieceSubPhaseSecondTouchPlayerId",
|
|
338
|
+
"commonname": "setPieceSubPhaseSecondTouchPlayerName"
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
left_on="setPieceSubPhaseSecondTouchPlayerId",
|
|
342
|
+
right_on="setPieceSubPhaseSecondTouchPlayerId",
|
|
343
|
+
how="left",
|
|
344
|
+
suffixes=("", "_right")
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# rename some columns
|
|
348
|
+
events = events.rename(columns={
|
|
349
|
+
"currentAttackingSquadId": "attackingSquadId",
|
|
350
|
+
"currentAttackingSquadName": "attackingSquadName",
|
|
351
|
+
"duelDuelType": "duelType",
|
|
352
|
+
"scheduledDate": "dateTime",
|
|
353
|
+
"gameTimeGameTime": "gameTime",
|
|
354
|
+
"gameTimeGameTimeInSec": "gameTimeInSec",
|
|
355
|
+
"eventId": "eventId_scorings",
|
|
356
|
+
"id": "eventId",
|
|
357
|
+
"index": "eventNumber",
|
|
358
|
+
"phaseIndex": "setPiecePhaseIndex",
|
|
359
|
+
"setPieceMainEvent": "setPieceSubPhaseMainEvent",
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
# define desired column order
|
|
363
|
+
event_cols = [
|
|
364
|
+
"matchId",
|
|
365
|
+
"dateTime",
|
|
366
|
+
"competitionId",
|
|
367
|
+
"competitionName",
|
|
368
|
+
"competitionType",
|
|
369
|
+
"iterationId",
|
|
370
|
+
"season",
|
|
371
|
+
"matchDayIndex",
|
|
372
|
+
"matchDayName",
|
|
373
|
+
"homeSquadId",
|
|
374
|
+
"homeSquadName",
|
|
375
|
+
"homeSquadCountryId",
|
|
376
|
+
"homeSquadCountryName",
|
|
377
|
+
"homeSquadType",
|
|
378
|
+
"awaySquadId",
|
|
379
|
+
"awaySquadName",
|
|
380
|
+
"awaySquadCountryId",
|
|
381
|
+
"awaySquadCountryName",
|
|
382
|
+
"awaySquadType",
|
|
383
|
+
"eventId",
|
|
384
|
+
"eventNumber",
|
|
385
|
+
"sequenceIndex",
|
|
386
|
+
"periodId",
|
|
387
|
+
"gameTime",
|
|
388
|
+
"gameTimeInSec",
|
|
389
|
+
"duration",
|
|
390
|
+
"squadId",
|
|
391
|
+
"squadName",
|
|
392
|
+
"attackingSquadId",
|
|
393
|
+
"attackingSquadName",
|
|
394
|
+
"phase",
|
|
395
|
+
"playerId",
|
|
396
|
+
"playerName",
|
|
397
|
+
"playerPosition",
|
|
398
|
+
"playerPositionSide",
|
|
399
|
+
"actionType",
|
|
400
|
+
"action",
|
|
401
|
+
"bodyPart",
|
|
402
|
+
"bodyPartExtended",
|
|
403
|
+
"previousPassHeight",
|
|
404
|
+
"result",
|
|
405
|
+
"startCoordinatesX",
|
|
406
|
+
"startCoordinatesY",
|
|
407
|
+
"startAdjCoordinatesX",
|
|
408
|
+
"startAdjCoordinatesY",
|
|
409
|
+
"startPackingZone",
|
|
410
|
+
"startPitchPosition",
|
|
411
|
+
"startLane",
|
|
412
|
+
"endCoordinatesX",
|
|
413
|
+
"endCoordinatesY",
|
|
414
|
+
"endAdjCoordinatesX",
|
|
415
|
+
"endAdjCoordinatesY",
|
|
416
|
+
"endPackingZone",
|
|
417
|
+
"endPitchPosition",
|
|
418
|
+
"endLane",
|
|
419
|
+
"opponents",
|
|
420
|
+
"pressure",
|
|
421
|
+
"distanceToGoal",
|
|
422
|
+
"pxTTeam",
|
|
423
|
+
"pxTOpponent",
|
|
424
|
+
"pressingPlayerId",
|
|
425
|
+
"pressingPlayerName",
|
|
426
|
+
"distanceToOpponent",
|
|
427
|
+
"opponentCoordinatesX",
|
|
428
|
+
"opponentCoordinatesY",
|
|
429
|
+
"opponentAdjCoordinatesX",
|
|
430
|
+
"opponentAdjCoordinatesY",
|
|
431
|
+
"passReceiverType",
|
|
432
|
+
"passReceiverPlayerId",
|
|
433
|
+
"passReceiverPlayerName",
|
|
434
|
+
"passDistance",
|
|
435
|
+
"passAngle",
|
|
436
|
+
"dribbleDistance",
|
|
437
|
+
"dribbleType",
|
|
438
|
+
"dribbleResult",
|
|
439
|
+
"dribbleOpponentPlayerId",
|
|
440
|
+
"dribbleOpponentPlayerName",
|
|
441
|
+
"shotDistance",
|
|
442
|
+
"shotAngle",
|
|
443
|
+
"shotTargetPointY",
|
|
444
|
+
"shotTargetPointZ",
|
|
445
|
+
"shotWoodwork",
|
|
446
|
+
"shotGkCoordinatesX",
|
|
447
|
+
"shotGkCoordinatesY",
|
|
448
|
+
"shotGkAdjCoordinatesX",
|
|
449
|
+
"shotGkAdjCoordinatesY",
|
|
450
|
+
"shotGkDivePointY",
|
|
451
|
+
"shotGkDivePointZ",
|
|
452
|
+
"duelType",
|
|
453
|
+
"duelPlayerId",
|
|
454
|
+
"duelPlayerName",
|
|
455
|
+
"fouledPlayerId",
|
|
456
|
+
"fouledPlayerName",
|
|
457
|
+
"formationTeam",
|
|
458
|
+
"formationOpponent",
|
|
459
|
+
"inferredSetPiece",
|
|
460
|
+
]
|
|
461
|
+
|
|
462
|
+
set_piece_cols = [
|
|
463
|
+
"setPieceId",
|
|
464
|
+
"setPiecePhaseIndex",
|
|
465
|
+
"setPieceCategory",
|
|
466
|
+
"adjSetPieceCategory",
|
|
467
|
+
"setPieceExecutionType",
|
|
468
|
+
"setPieceSubPhaseId",
|
|
469
|
+
"setPieceSubPhaseIndex",
|
|
470
|
+
"setPieceSubPhaseStartZone",
|
|
471
|
+
"setPieceSubPhaseCornerEndZone",
|
|
472
|
+
"setPieceSubPhaseCornerType",
|
|
473
|
+
"setPieceSubPhaseFreeKickEndZone",
|
|
474
|
+
"setPieceSubPhaseFreeKickType",
|
|
475
|
+
"setPieceSubPhaseMainEvent",
|
|
476
|
+
"setPieceSubPhaseMainEventPlayerId",
|
|
477
|
+
"setPieceSubPhaseMainEventPlayerName",
|
|
478
|
+
"setPieceSubPhaseMainEventOutcome",
|
|
479
|
+
"setPieceSubPhasePassReceiverId",
|
|
480
|
+
"setPieceSubPhasePassReceiverName",
|
|
481
|
+
"setPieceSubPhaseFirstTouchPlayerId",
|
|
482
|
+
"setPieceSubPhaseFirstTouchPlayerName",
|
|
483
|
+
"setPieceSubPhaseFirstTouchWon",
|
|
484
|
+
"setPieceSubPhaseIndirectHeader",
|
|
485
|
+
"setPieceSubPhaseSecondTouchPlayerId",
|
|
486
|
+
"setPieceSubPhaseSecondTouchPlayerName",
|
|
487
|
+
"setPieceSubPhaseSecondTouchWon",
|
|
488
|
+
]
|
|
489
|
+
|
|
490
|
+
# add columns that might not exist in previous data versions
|
|
491
|
+
for col in event_cols:
|
|
492
|
+
if col not in events.columns:
|
|
493
|
+
events[col] = np.nan
|
|
494
|
+
|
|
495
|
+
# create order
|
|
496
|
+
order = event_cols
|
|
497
|
+
|
|
498
|
+
if include_set_pieces:
|
|
499
|
+
# add kpis
|
|
500
|
+
order = order + set_piece_cols
|
|
501
|
+
|
|
502
|
+
if include_kpis:
|
|
503
|
+
# get list of kpi columns
|
|
504
|
+
kpi_cols = kpis["name"].tolist()
|
|
505
|
+
|
|
506
|
+
# add kpis
|
|
507
|
+
order = order + kpi_cols
|
|
508
|
+
|
|
509
|
+
# reorder data
|
|
510
|
+
events = events[order]
|
|
511
|
+
|
|
512
|
+
# reorder rows
|
|
513
|
+
events = events.sort_values(["matchId", "eventNumber"])
|
|
514
|
+
|
|
515
|
+
# return events
|
|
516
|
+
return events
|