rustat-python-api 0.2.0__tar.gz → 0.3.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.
- {rustat-python-api-0.2.0/rustat_python_api.egg-info → rustat-python-api-0.3.1}/PKG-INFO +2 -2
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/README.md +1 -1
- rustat-python-api-0.3.1/rustat_python_api/config.py +37 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api/parser.py +9 -10
- rustat-python-api-0.3.1/rustat_python_api/processing.py +82 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1/rustat_python_api.egg-info}/PKG-INFO +2 -2
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/SOURCES.txt +2 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/setup.py +1 -1
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/LICENSE +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/pyproject.toml +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api/__init__.py +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api/urls.py +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/dependency_links.txt +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/requires.txt +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/top_level.txt +0 -0
- {rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rustat-python-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -38,7 +38,7 @@ schedule = parser.get_schedule(team_id, season_id)
|
|
|
38
38
|
keys = list(schedule.keys())
|
|
39
39
|
match_id = keys[-1]
|
|
40
40
|
|
|
41
|
-
events = parser.get_events(match_id)
|
|
41
|
+
events = parser.get_events(match_id, process=True)
|
|
42
42
|
|
|
43
43
|
stats = parser.get_match_stats(match_id)
|
|
44
44
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
columns = [
|
|
2
|
+
'player_name', 'team_name', 'half', 'second', 'action_id', 'action_name',
|
|
3
|
+
'position_name', 'possession_number', 'pos_x', 'pos_y', 'pos_dest_x', 'pos_dest_y',
|
|
4
|
+
'player_id', 'number', 'team_id', 'standart_name', 'possession_time',
|
|
5
|
+
'opponent_id', 'opponent_name', 'opponent_team_id', 'opponent_team_name',
|
|
6
|
+
'opponent_position_name', 'zone_name', 'zone_dest_name', 'len',
|
|
7
|
+
'possession_team_id', 'possession_team_name', 'possession_name',
|
|
8
|
+
'attack_status_name', 'attack_type_name', 'attack_flang_name',
|
|
9
|
+
'attack_team_id', 'attack_team_name', 'attack_number',
|
|
10
|
+
'body_name', 'gate_x', 'gate_y', 'assistant_id',
|
|
11
|
+
'assistant_name', 'shot_type', 'touches', 'xg',
|
|
12
|
+
'shot_handling', 'match_id', 'receiver_id', 'receiver_name'
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
numeric_columns = [
|
|
16
|
+
'id', 'number', 'player_id', 'team_id', 'half', 'second',
|
|
17
|
+
'pos_x', 'pos_y', 'pos_dest_x', 'pos_dest_y', 'len', 'possession_id', 'possession_team_id',
|
|
18
|
+
'opponent_id', 'opponent_team_id', 'zone_id', 'zone_dest_id',
|
|
19
|
+
'possession_number', 'attack_status_id', 'attack_team_id', 'assistant_id', 'touches', 'xg'
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
id2type = {
|
|
23
|
+
1: 'pass', 2: 'duel', 3: 'foul',
|
|
24
|
+
4: 'shot', 5: 'free kick', 6: 'interception',
|
|
25
|
+
7: 'rebound', 8: 'goal', 9: 'clearance',
|
|
26
|
+
10: 'bad ball control', 11: 'control', 12: 'attack',
|
|
27
|
+
13: 'keeper', 14: 'substitution', 15: 'formation',
|
|
28
|
+
16: 'player position', 17: 'ball off', 18: 'match status',
|
|
29
|
+
19: 'mistake', 20: 'translation problem', 21: 'carry',
|
|
30
|
+
22: 'receive', 23: 'goal attack involvement', 24: 'rating',
|
|
31
|
+
25: 'average position', 26: 'cross', 27: 'ball out',
|
|
32
|
+
28: 'other', 29: 'video', 30: 'bad mistake',
|
|
33
|
+
31: 'bad keeper mistake', 32: 'goal moment', 33: 'team pressing',
|
|
34
|
+
34: 'line up', 35: 'sync', 36: 'referee',
|
|
35
|
+
37: 'insurance', 38: 'injury',
|
|
36
|
+
128: 'staff', 161: 'sub player'
|
|
37
|
+
}
|
|
@@ -5,6 +5,8 @@ from tqdm import tqdm
|
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
7
|
from .urls import URLs
|
|
8
|
+
from .config import numeric_columns
|
|
9
|
+
from .processing import processing
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class RuStatParser:
|
|
@@ -15,13 +17,6 @@ class RuStatParser:
|
|
|
15
17
|
urls: dict = URLs,
|
|
16
18
|
sleep: int = -1
|
|
17
19
|
):
|
|
18
|
-
self.numeric_columns = [
|
|
19
|
-
'id', 'number', 'player_id', 'team_id', 'half', 'second',
|
|
20
|
-
'pos_x', 'pos_y', 'pos_dest_x', 'pos_dest_y', 'len', 'possession_id', 'possession_team_id',
|
|
21
|
-
'opponent_id', 'opponent_team_id', 'zone_id', 'zone_dest_id',
|
|
22
|
-
'possession_number', 'attack_status_id', 'attack_team_id', 'assistant_id', 'touches', 'xg'
|
|
23
|
-
]
|
|
24
|
-
|
|
25
20
|
self.user = user
|
|
26
21
|
self.password = password
|
|
27
22
|
self.urls = urls
|
|
@@ -95,7 +90,7 @@ class RuStatParser:
|
|
|
95
90
|
for row in data["data"]["row"]
|
|
96
91
|
}
|
|
97
92
|
|
|
98
|
-
def get_events(self, match_id: int) -> pd.DataFrame | None:
|
|
93
|
+
def get_events(self, match_id: int, process: bool = True) -> pd.DataFrame | None:
|
|
99
94
|
data = self.resp2data(
|
|
100
95
|
self.urls["events"].format(
|
|
101
96
|
user=self.user,
|
|
@@ -109,8 +104,12 @@ class RuStatParser:
|
|
|
109
104
|
|
|
110
105
|
df = pd.json_normalize(data["data"]["row"])
|
|
111
106
|
|
|
112
|
-
|
|
113
|
-
df[
|
|
107
|
+
current_numeric_columns = [column for column in numeric_columns if column in df.columns]
|
|
108
|
+
df[current_numeric_columns] = df[current_numeric_columns].apply(pd.to_numeric, errors='coerce')
|
|
109
|
+
|
|
110
|
+
if process:
|
|
111
|
+
df['match_id'] = match_id
|
|
112
|
+
df = processing(df)
|
|
114
113
|
|
|
115
114
|
return df
|
|
116
115
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from .config import columns, id2type
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def process_list(x: pd.Series):
|
|
8
|
+
lst = x.dropna().unique().tolist()
|
|
9
|
+
if len(lst) == 1:
|
|
10
|
+
return lst[0]
|
|
11
|
+
elif len(lst) == 0:
|
|
12
|
+
return np.nan
|
|
13
|
+
else:
|
|
14
|
+
return lst
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def gluing(df: pd.DataFrame) -> pd.DataFrame:
|
|
18
|
+
cols = ['player_id', 'half', 'second', 'pos_x', 'pos_y']
|
|
19
|
+
|
|
20
|
+
df_gb = df.groupby(cols).agg(process_list).reset_index()
|
|
21
|
+
df_gb['possession_number'] = df_gb['possession_number'].apply(
|
|
22
|
+
lambda x: max(x) if isinstance(x, list) else x
|
|
23
|
+
)
|
|
24
|
+
df_gb = df_gb.sort_values(by=['half', 'second', 'possession_number']).reset_index(drop=True)
|
|
25
|
+
return df_gb
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def add_reciever(glued_df: pd.DataFrame) -> pd.DataFrame:
|
|
29
|
+
df = glued_df.copy()
|
|
30
|
+
df['receiver_id'] = df['player_id'].shift(1)
|
|
31
|
+
df['receiver_name'] = df['player_name'].shift(1)
|
|
32
|
+
|
|
33
|
+
mask = (
|
|
34
|
+
(df['action_name'] == 'Ball receiving')
|
|
35
|
+
& (df['pos_x'] == df['pos_dest_x'].shift(1))
|
|
36
|
+
& (df['pos_y'] == df['pos_dest_y'].shift(1))
|
|
37
|
+
& (df['team_id'] == df['team_id'].shift(1))
|
|
38
|
+
& (df['player_id'] != df['player_id'].shift(1))
|
|
39
|
+
& (df['possession_number'] == df['possession_number'].shift(1))
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
idx = df[mask].index
|
|
43
|
+
remaining_idx = df.drop(idx-1).index
|
|
44
|
+
|
|
45
|
+
df.loc[remaining_idx, 'receiver_id'] = np.nan
|
|
46
|
+
df.loc[remaining_idx, 'receiver_name'] = np.nan
|
|
47
|
+
|
|
48
|
+
df = df[df['action_name'] != 'Ball receiving'].reset_index(drop=True)
|
|
49
|
+
|
|
50
|
+
return df
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def filter_data(df: pd.DataFrame) -> pd.DataFrame:
|
|
54
|
+
for column in columns:
|
|
55
|
+
if column not in df.columns:
|
|
56
|
+
df[column] = np.nan
|
|
57
|
+
|
|
58
|
+
return df[(~df['possession_number'].isna()) | (df['second'] != 0)][columns].reset_index(drop=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def tagging(df: pd.DataFrame) -> pd.DataFrame:
|
|
62
|
+
df = df.rename(columns={'action_name': 'sub_tags', 'action_id': 'sub_tags_ids'})
|
|
63
|
+
df['sub_tags'] = df['sub_tags'].apply(lambda x: x if isinstance(x, list) else [x])
|
|
64
|
+
df['sub_tags_ids'] = df['sub_tags_ids'].apply(
|
|
65
|
+
lambda x:
|
|
66
|
+
list(set([int(t) // 1000 for t in x]))
|
|
67
|
+
if isinstance(x, list)
|
|
68
|
+
else [int(x) // 1000]
|
|
69
|
+
)
|
|
70
|
+
df['sub_tags_ids'] = df['sub_tags_ids'].apply(lambda x: [id2type[t] for t in x])
|
|
71
|
+
df = df.rename(columns={'sub_tags_ids': 'tags'})
|
|
72
|
+
|
|
73
|
+
return df
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def processing(df: pd.DataFrame) -> pd.DataFrame:
|
|
77
|
+
df = gluing(df)
|
|
78
|
+
df = add_reciever(df)
|
|
79
|
+
df = filter_data(df)
|
|
80
|
+
df = tagging(df)
|
|
81
|
+
|
|
82
|
+
return df
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rustat-python-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -38,7 +38,7 @@ schedule = parser.get_schedule(team_id, season_id)
|
|
|
38
38
|
keys = list(schedule.keys())
|
|
39
39
|
match_id = keys[-1]
|
|
40
40
|
|
|
41
|
-
events = parser.get_events(match_id)
|
|
41
|
+
events = parser.get_events(match_id, process=True)
|
|
42
42
|
|
|
43
43
|
stats = parser.get_match_stats(match_id)
|
|
44
44
|
|
|
@@ -3,7 +3,9 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
setup.py
|
|
5
5
|
rustat_python_api/__init__.py
|
|
6
|
+
rustat_python_api/config.py
|
|
6
7
|
rustat_python_api/parser.py
|
|
8
|
+
rustat_python_api/processing.py
|
|
7
9
|
rustat_python_api/urls.py
|
|
8
10
|
rustat_python_api.egg-info/PKG-INFO
|
|
9
11
|
rustat_python_api.egg-info/SOURCES.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{rustat-python-api-0.2.0 → rustat-python-api-0.3.1}/rustat_python_api.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|