rustat-python-api 0.5.2__tar.gz → 0.5.4__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.
Files changed (18) hide show
  1. {rustat-python-api-0.5.2/rustat_python_api.egg-info → rustat-python-api-0.5.4}/PKG-INFO +1 -1
  2. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/parser.py +74 -0
  3. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/pitch_control.py +34 -17
  4. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/urls.py +1 -0
  5. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4/rustat_python_api.egg-info}/PKG-INFO +1 -1
  6. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/setup.py +1 -1
  7. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/LICENSE +0 -0
  8. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/README.md +0 -0
  9. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/pyproject.toml +0 -0
  10. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/__init__.py +0 -0
  11. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/config.py +0 -0
  12. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/models_api.py +0 -0
  13. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api/processing.py +0 -0
  14. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api.egg-info/SOURCES.txt +0 -0
  15. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api.egg-info/dependency_links.txt +0 -0
  16. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api.egg-info/requires.txt +0 -0
  17. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/rustat_python_api.egg-info/top_level.txt +0 -0
  18. {rustat-python-api-0.5.2 → rustat-python-api-0.5.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rustat-python-api
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: A Python wrapper for RuStat API
5
5
  Home-page: https://github.com/dailydaniel/rustat-python-api
6
6
  Author: Daniel Zholkovsky
@@ -173,6 +173,80 @@ class RuStatParser:
173
173
 
174
174
  return df
175
175
 
176
+ def get_tracking_30fps(
177
+ self,
178
+ match_id: int, half: int, lang_id: int = 0,
179
+ referee_data: int = 0, ball_data: int = 1
180
+ ) -> pd.DataFrame | tuple | None:
181
+ data = self.resp2data(
182
+ self.urls["tracking_30fps"].format(
183
+ user=self.user,
184
+ password=self.password,
185
+ match_id=match_id,
186
+ half=half,
187
+ lang_id=lang_id,
188
+ referee_data=referee_data,
189
+ ball_data=ball_data
190
+ )
191
+ )
192
+
193
+ if not data:
194
+ return None
195
+
196
+ teams_data = data['data']['teams']
197
+ if 'ball' in data['data']:
198
+ ball_data = data['data']['ball']
199
+ if 'referee' in data['data']:
200
+ referee_data = data['data']['referee']
201
+ del data
202
+
203
+ tracking = pd.DataFrame(columns=[
204
+ "half", "second", "pos_x", "pos_y", "team_id", "player_id", "player_name", "distance", "speed"
205
+ ])
206
+
207
+ for team_data in tqdm(teams_data):
208
+ team_id = team_data["id"]
209
+ side_1h = team_data["gate_position_half_1"]
210
+
211
+ for player_data in team_data["players"]:
212
+ player_id = player_data["id"]
213
+ player_name = player_data["name"]
214
+
215
+ cur_df = pd.json_normalize(player_data["rows"])
216
+ cur_df = cur_df.apply(pd.to_numeric, errors='coerce')
217
+ cur_df["team_id"] = team_id
218
+ cur_df["player_id"] = player_id
219
+ cur_df["player_name"] = player_name
220
+ cur_df["side_1h"] = side_1h
221
+
222
+ tracking = pd.concat([tracking, cur_df], ignore_index=True)
223
+
224
+ tracking['second'] = tracking['second'].astype(float)
225
+ tracking = tracking.sort_values(by=['half', 'second', 'team_id', 'player_id']).reset_index(drop=True)
226
+ tracking['pos_x'] = tracking['pos_x'] + 105/2
227
+ tracking['team_id'] = tracking['team_id'].astype(str)
228
+
229
+ if referee_data:
230
+ referee = pd.json_normalize(referee_data['rows'])
231
+ referee['second'] = referee['second'].astype(float)
232
+ referee = referee.sort_values(by=['half', 'second']).reset_index(drop=True)
233
+ referee['pos_x'] = referee['pos_x'] + 105/2
234
+
235
+ if ball_data:
236
+ ball = pd.json_normalize(ball_data['rows'])
237
+ ball['second'] = ball['second'].astype(float)
238
+ ball = ball.sort_values(by=['half', 'second']).reset_index(drop=True)
239
+ ball['pos_x'] = ball['pos_x'] + 105/2
240
+
241
+ if referee_data and ball_data:
242
+ return tracking, referee, ball
243
+ elif referee_data:
244
+ return tracking, referee
245
+ elif ball_data:
246
+ return tracking, ball
247
+ else:
248
+ return tracking
249
+
176
250
  def get_match_stats(self, match_id: int) -> dict:
177
251
  data = self.resp2data(
178
252
  self.urls["match_stats"].format(
@@ -7,7 +7,7 @@ import matplotsoccer as mpl
7
7
 
8
8
 
9
9
  class PitchControl:
10
- def __init__(self, tracking: pd.DataFrame, events: pd.DataFrame):
10
+ def __init__(self, tracking: pd.DataFrame, events: pd.DataFrame, ball_data: pd.DataFrame = None):
11
11
  self.team_ids = tracking['team_id'].unique()
12
12
  sides = tracking.groupby('team_id')['side_1h'].unique()
13
13
  side_by_team = dict(zip(self.team_ids, sides[self.team_ids].apply(lambda x: x[0])))
@@ -22,10 +22,11 @@ class PitchControl:
22
22
 
23
23
  self.locs_home, self.locs_away, self.locs_ball, self.t = self.get_locs(
24
24
  tracking,
25
- events
25
+ events,
26
+ ball_data
26
27
  )
27
28
 
28
- def get_locs(self, tracking: pd.DataFrame, events: pd.DataFrame) -> tuple:
29
+ def get_locs(self, tracking: pd.DataFrame, events: pd.DataFrame, ball_data: pd.DataFrame | None) -> tuple:
29
30
  events = events[[
30
31
  'possession_number', 'team_id', 'possession_team_id',
31
32
  'half', 'second', 'pos_x', 'pos_y'
@@ -38,10 +39,11 @@ class PitchControl:
38
39
  lambda x: self.swap_coords(x, 'y'), axis=1
39
40
  )
40
41
 
41
- ball_data = self.interpolate_ball_data(
42
- events[['half', 'second', 'pos_x', 'pos_y']],
43
- tracking
44
- )
42
+ if ball_data is None:
43
+ ball_data = self.interpolate_ball_data(
44
+ events[['half', 'second', 'pos_x', 'pos_y']],
45
+ tracking
46
+ )
45
47
 
46
48
  locs_home = {
47
49
  half: {
@@ -124,10 +126,21 @@ class PitchControl:
124
126
 
125
127
  @staticmethod
126
128
  def get_player_data(player_id, half, tracking):
127
- return tracking[
129
+ timestamps = tracking[tracking['half'] == half]['second'].unique()
130
+ player_data = tracking[
128
131
  (tracking['player_id'] == player_id)
129
132
  & (tracking['half'] == half)
130
- ][['pos_x', 'pos_y']].values
133
+ ][['second', 'pos_x', 'pos_y']]
134
+
135
+ player_data_full = pd.DataFrame({'second': timestamps})
136
+ player_data_full = player_data_full.merge(player_data, on='second', how='left')
137
+
138
+ return player_data_full[['pos_x', 'pos_y']].values
139
+
140
+ # return tracking[
141
+ # (tracking['player_id'] == player_id)
142
+ # & (tracking['half'] == half)
143
+ # ][['pos_x', 'pos_y']].values
131
144
 
132
145
  def influence_function(
133
146
  self,
@@ -195,11 +208,11 @@ class PitchControl:
195
208
  locations = np.c_[xx.flatten(),yy.flatten()]
196
209
 
197
210
  for k in self.locs_home[half].keys():
198
- if len(self.locs_home[half][k]) >= tp:
199
- Zh += self.influence_function(k, locations, tp, 'h', half)
211
+ # if len(self.locs_home[half][k]) >= tp:
212
+ Zh += self.influence_function(k, locations, tp, 'h', half)
200
213
  for k in self.locs_away[half].keys():
201
- if len(self.locs_away[half][k]) >= tp:
202
- Za += self.influence_function(k, locations, tp, 'a', half)
214
+ # if len(self.locs_away[half][k]) >= tp:
215
+ Za += self.influence_function(k, locations, tp, 'a', half)
203
216
 
204
217
  Zh = Zh.reshape((dt, dt))
205
218
  Za = Za.reshape((dt, dt))
@@ -225,7 +238,8 @@ class PitchControl:
225
238
  plt.contourf(xx, yy, pitch_control)
226
239
 
227
240
  for k in self.locs_home[half].keys():
228
- if len(self.locs_home[half][k]) >= tp:
241
+ # if len(self.locs_home[half][k]) >= tp:
242
+ if np.isfinite(self.locs_home[half][k][tp, :]).all():
229
243
  plt.scatter(
230
244
  self.locs_home[half][k][tp, 0],
231
245
  self.locs_home[half][k][tp, 1],
@@ -233,7 +247,8 @@ class PitchControl:
233
247
  )
234
248
 
235
249
  for k in self.locs_away[half].keys():
236
- if len(self.locs_away[half][k]) >= tp:
250
+ # if len(self.locs_away[half][k]) >= tp:
251
+ if np.isfinite(self.locs_away[half][k][tp, :]).all():
237
252
  plt.scatter(
238
253
  self.locs_away[half][k][tp, 0],
239
254
  self.locs_away[half][k][tp, 1], color='black'
@@ -274,14 +289,16 @@ class PitchControl:
274
289
  plt.contourf(xx, yy, pitch_control)
275
290
 
276
291
  for k in self.locs_home[half].keys():
277
- if len(self.locs_home[half][k]) >= fr:
292
+ # if len(self.locs_home[half][k]) >= fr:
293
+ if np.isfinite(self.locs_home[half][k][fr, :]).all():
278
294
  plt.scatter(
279
295
  self.locs_home[half][k][fr, 0],
280
296
  self.locs_home[half][k][fr, 1],
281
297
  color='darkgrey'
282
298
  )
283
299
  for k in self.locs_away[half].keys():
284
- if len(self.locs_away[half][k]) >= fr:
300
+ # if len(self.locs_away[half][k]) >= fr:
301
+ if np.isfinite(self.locs_away[half][k][fr, :]).all():
285
302
  plt.scatter(
286
303
  self.locs_away[half][k][fr, 0],
287
304
  self.locs_away[half][k][fr, 1],
@@ -4,5 +4,6 @@ URLs = {
4
4
  "match_stats": "http://feeds.rustatsport.ru/?tpl=207&user={user}&key={password}&match_id={match_id}&lang_id=1&format=json",
5
5
  "tournament_teams": "http://feeds.rustatsport.ru/?tpl=32&user={user}&key={password}&tournament_id=2&season_id={season_id}&date_start=&date_end=&lang_id=1&format=json",
6
6
  "tracking": "https://feeds.rustatsport.ru/?tpl=274&user={user}&key={password}&match_id={match_id}&lang_id=0&format=json",
7
+ "tracking_30fps": "https://feeds.rustatsport.ru/?tpl=1013&user={user}&key={password}&match_id={match_id}&referee_data={referee_data}&ball_data={ball_data}&half={half}&lang_id={lang_id}&format=json",
7
8
  "player_match_stats": "http://feeds.rustatsport.ru/?tpl=227&user={user}&key={password}&match_id={match_id}&lang_id=1&format=json"
8
9
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rustat-python-api
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: A Python wrapper for RuStat API
5
5
  Home-page: https://github.com/dailydaniel/rustat-python-api
6
6
  Author: Daniel Zholkovsky
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='rustat-python-api',
5
- version='0.5.2',
5
+ version='0.5.4',
6
6
  description='A Python wrapper for RuStat API',
7
7
  long_description=open('README.md').read(),
8
8
  long_description_content_type='text/markdown',