diff --git a/ETL/L3_Builder.py b/ETL/L3_Builder.py index 9f6a705..3071f0e 100644 --- a/ETL/L3_Builder.py +++ b/ETL/L3_Builder.py @@ -48,6 +48,18 @@ def init_db(): "rd_phase_death_early_share": "REAL", "rd_phase_death_mid_share": "REAL", "rd_phase_death_late_share": "REAL", + "rd_phase_kill_early_share_t": "REAL", + "rd_phase_kill_mid_share_t": "REAL", + "rd_phase_kill_late_share_t": "REAL", + "rd_phase_kill_early_share_ct": "REAL", + "rd_phase_kill_mid_share_ct": "REAL", + "rd_phase_kill_late_share_ct": "REAL", + "rd_phase_death_early_share_t": "REAL", + "rd_phase_death_mid_share_t": "REAL", + "rd_phase_death_late_share_t": "REAL", + "rd_phase_death_early_share_ct": "REAL", + "rd_phase_death_mid_share_ct": "REAL", + "rd_phase_death_late_share_ct": "REAL", "rd_firstdeath_team_first_death_rounds": "INTEGER", "rd_firstdeath_team_first_death_win_rate": "REAL", "rd_invalid_death_rounds": "INTEGER", diff --git a/database/L1A/L1A.sqlite b/database/L1A/L1A.sqlite index 0f441f4..0b1e98f 100644 Binary files a/database/L1A/L1A.sqlite and b/database/L1A/L1A.sqlite differ diff --git a/database/L2/L2_Main.sqlite b/database/L2/L2_Main.sqlite index e56ee38..f157555 100644 Binary files a/database/L2/L2_Main.sqlite and b/database/L2/L2_Main.sqlite differ diff --git a/database/L3/L3_Features.sqlite b/database/L3/L3_Features.sqlite index 3789a43..bea49ca 100644 Binary files a/database/L3/L3_Features.sqlite and b/database/L3/L3_Features.sqlite differ diff --git a/database/L3/schema.sql b/database/L3/schema.sql index d35db0d..588389e 100644 --- a/database/L3/schema.sql +++ b/database/L3/schema.sql @@ -204,6 +204,18 @@ CREATE TABLE IF NOT EXISTS dm_player_features ( rd_phase_death_early_share REAL, rd_phase_death_mid_share REAL, rd_phase_death_late_share REAL, + rd_phase_kill_early_share_t REAL, + rd_phase_kill_mid_share_t REAL, + rd_phase_kill_late_share_t REAL, + rd_phase_kill_early_share_ct REAL, + rd_phase_kill_mid_share_ct REAL, + rd_phase_kill_late_share_ct REAL, + rd_phase_death_early_share_t REAL, + rd_phase_death_mid_share_t REAL, + rd_phase_death_late_share_t REAL, + rd_phase_death_early_share_ct REAL, + rd_phase_death_mid_share_ct REAL, + rd_phase_death_late_share_ct REAL, rd_firstdeath_team_first_death_rounds INTEGER, rd_firstdeath_team_first_death_win_rate REAL, rd_invalid_death_rounds INTEGER, diff --git a/database/Web/Web_App.sqlite b/database/Web/Web_App.sqlite index 1d393c6..5695a31 100644 Binary files a/database/Web/Web_App.sqlite and b/database/Web/Web_App.sqlite differ diff --git a/web/services/feature_service.py b/web/services/feature_service.py index a49540f..a052b71 100644 --- a/web/services/feature_service.py +++ b/web/services/feature_service.py @@ -1202,6 +1202,18 @@ class FeatureService: df['rd_phase_death_early_share'] = 0.0 df['rd_phase_death_mid_share'] = 0.0 df['rd_phase_death_late_share'] = 0.0 + df['rd_phase_kill_early_share_t'] = 0.0 + df['rd_phase_kill_mid_share_t'] = 0.0 + df['rd_phase_kill_late_share_t'] = 0.0 + df['rd_phase_kill_early_share_ct'] = 0.0 + df['rd_phase_kill_mid_share_ct'] = 0.0 + df['rd_phase_kill_late_share_ct'] = 0.0 + df['rd_phase_death_early_share_t'] = 0.0 + df['rd_phase_death_mid_share_t'] = 0.0 + df['rd_phase_death_late_share_t'] = 0.0 + df['rd_phase_death_early_share_ct'] = 0.0 + df['rd_phase_death_mid_share_ct'] = 0.0 + df['rd_phase_death_late_share_ct'] = 0.0 df['rd_firstdeath_team_first_death_rounds'] = 0 df['rd_firstdeath_team_first_death_win_rate'] = 0.0 df['rd_invalid_death_rounds'] = 0 @@ -1268,6 +1280,62 @@ class FeatureService: df[c] = df[f'{c}_calc'].fillna(df[c]) df.drop(columns=[f'{c}_calc'], inplace=True) + if 'attacker_side' in df_events.columns: + k_side = df_events[df_events['attacker_side'].isin(['CT', 'T'])].copy() + if not k_side.empty: + k_cnt_side = k_side.groupby(['attacker_steam_id', 'attacker_side', 'phase_bucket']).size().reset_index(name='cnt') + k_piv = k_cnt_side.pivot_table(index=['attacker_steam_id', 'attacker_side'], columns='phase_bucket', values='cnt', fill_value=0) + k_piv['tot'] = k_piv.sum(axis=1).replace(0, 1) + k_piv = k_piv.div(k_piv['tot'], axis=0).drop(columns=['tot']) + k_piv = k_piv.reset_index().rename(columns={'attacker_steam_id': 'steam_id_64'}) + + for side, suffix in [('T', '_t'), ('CT', '_ct')]: + tmp = k_piv[k_piv['attacker_side'] == side].copy() + if not tmp.empty: + tmp = tmp.rename(columns={ + 'early': f'rd_phase_kill_early_share{suffix}', + 'mid': f'rd_phase_kill_mid_share{suffix}', + 'late': f'rd_phase_kill_late_share{suffix}', + }) + df = df.merge( + tmp[['steam_id_64', f'rd_phase_kill_early_share{suffix}', f'rd_phase_kill_mid_share{suffix}', f'rd_phase_kill_late_share{suffix}']], + on='steam_id_64', + how='left', + suffixes=('', '_calc') + ) + for c in [f'rd_phase_kill_early_share{suffix}', f'rd_phase_kill_mid_share{suffix}', f'rd_phase_kill_late_share{suffix}']: + if f'{c}_calc' in df.columns: + df[c] = df[f'{c}_calc'].fillna(df[c]) + df.drop(columns=[f'{c}_calc'], inplace=True) + + if 'victim_side' in df_events.columns: + d_side = df_events[df_events['victim_side'].isin(['CT', 'T'])].copy() + if not d_side.empty: + d_cnt_side = d_side.groupby(['victim_steam_id', 'victim_side', 'phase_bucket']).size().reset_index(name='cnt') + d_piv = d_cnt_side.pivot_table(index=['victim_steam_id', 'victim_side'], columns='phase_bucket', values='cnt', fill_value=0) + d_piv['tot'] = d_piv.sum(axis=1).replace(0, 1) + d_piv = d_piv.div(d_piv['tot'], axis=0).drop(columns=['tot']) + d_piv = d_piv.reset_index().rename(columns={'victim_steam_id': 'steam_id_64'}) + + for side, suffix in [('T', '_t'), ('CT', '_ct')]: + tmp = d_piv[d_piv['victim_side'] == side].copy() + if not tmp.empty: + tmp = tmp.rename(columns={ + 'early': f'rd_phase_death_early_share{suffix}', + 'mid': f'rd_phase_death_mid_share{suffix}', + 'late': f'rd_phase_death_late_share{suffix}', + }) + df = df.merge( + tmp[['steam_id_64', f'rd_phase_death_early_share{suffix}', f'rd_phase_death_mid_share{suffix}', f'rd_phase_death_late_share{suffix}']], + on='steam_id_64', + how='left', + suffixes=('', '_calc') + ) + for c in [f'rd_phase_death_early_share{suffix}', f'rd_phase_death_mid_share{suffix}', f'rd_phase_death_late_share{suffix}']: + if f'{c}_calc' in df.columns: + df[c] = df[f'{c}_calc'].fillna(df[c]) + df.drop(columns=[f'{c}_calc'], inplace=True) + if 'victim_side' in df_events.columns and 'winner_side' in df_events.columns: death_rows = df_events[['match_id', 'round_num', 'event_time', 'victim_steam_id', 'victim_side', 'winner_side']].copy() death_rows = death_rows[death_rows['victim_side'].isin(['CT', 'T']) & death_rows['winner_side'].isin(['CT', 'T'])] diff --git a/web/services/stats_service.py b/web/services/stats_service.py index de17852..ee1bb72 100644 --- a/web/services/stats_service.py +++ b/web/services/stats_service.py @@ -749,6 +749,10 @@ class StatsService: # New: ROUND (Round Dynamics) 'rd_phase_kill_early_share', 'rd_phase_kill_mid_share', 'rd_phase_kill_late_share', 'rd_phase_death_early_share', 'rd_phase_death_mid_share', 'rd_phase_death_late_share', + 'rd_phase_kill_early_share_t', 'rd_phase_kill_mid_share_t', 'rd_phase_kill_late_share_t', + 'rd_phase_kill_early_share_ct', 'rd_phase_kill_mid_share_ct', 'rd_phase_kill_late_share_ct', + 'rd_phase_death_early_share_t', 'rd_phase_death_mid_share_t', 'rd_phase_death_late_share_t', + 'rd_phase_death_early_share_ct', 'rd_phase_death_mid_share_ct', 'rd_phase_death_late_share_ct', 'rd_firstdeath_team_first_death_win_rate', 'rd_invalid_death_rate', 'rd_pressure_kpr_ratio', 'rd_matchpoint_kpr_ratio', 'rd_trade_response_10s_rate', 'rd_pressure_perf_ratio', 'rd_matchpoint_perf_ratio', diff --git a/web/templates/players/profile.html b/web/templates/players/profile.html index d917c0d..6a9fd70 100644 --- a/web/templates/players/profile.html +++ b/web/templates/players/profile.html @@ -349,8 +349,99 @@