Python于LOLM针对伊泽瑞尔的数据分析

以下是一个分析框架。它用 pandas 建立了一套针对"使用伊泽瑞尔的玩家"的对手画像、团队指标、英雄池、BP、对线期、团战期、眼位、视野、核心对位、资源效率、胜利贡献值等分析模块。代码内置了模拟数据,可以替换为真实 CSV 数据。

```python

import pandas as pd

import numpy as np

from dataclasses import dataclass

@dataclass

class LoLData:

matches: pd.DataFrame

player_stats: pd.DataFrame

ward_events: pd.DataFrame

bp_events: pd.DataFrame

class EzrealOpponentAnalyzer:

def init(self, data: LoLData):

self.matches = data.matches

self.ps = data.player_stats.copy()

self.wards = data.ward_events.copy()

self.bp = data.bp_events.copy()

self._prepare()

def _prepare(self):

衍生指标

self.ps'damage_per_gold' = self.ps'damage_to_champs' / self.ps'gold'.replace(0, np.nan)

self.ps'kp' = (self.ps'kills' + self.ps'assists') / \

self.ps.groupby('match_id', 'team')'kills'.transform('sum').replace(0, np.nan)

self.ps'cc_hit_rate' = self.ps'cc_hits' / self.ps'cc_attempts'.replace(0, np.nan)

self.ps'skillshot_hit_rate' = self.ps'skillshots_hits' / self.ps'skillshots_attempts'.replace(0, np.nan)

self.ps'survive_rate' = self.ps'teamfight_survived' / self.ps'teamfights'.replace(0, np.nan)

self.ps'ineffective_death_rate' = self.ps'ineffective_deaths' / self.ps'deaths'.replace(0, np.nan)

self.ps'free_kill_rate' = self.ps'free_kills' / self.ps'kills'.replace(0, np.nan)

胜利贡献值 VC(简化加权模型:伤害、击杀、经济、对线)

self.ps'VC' = (

0.35 * self.ps'damage_to_champs' / self.ps.groupby('match_id', 'team')'damage_to_champs'.transform('sum') +

0.25 * self.ps'kills' / self.ps.groupby('match_id', 'team')'kills'.transform('sum') +

0.20 * self.ps'gold' / self.ps.groupby('match_id', 'team')'gold'.transform('sum') +

0.20 * self.ps'gd15' / 1000

)

合并比赛信息

self.ps = self.ps.merge(

self.matches[['match_id', 'team', 'result', 'first_blood_team',

'duration', 'dragons', 'heralds']],

on='match_id', 'team', how='left'

)

self.ps'is_ezreal' = self.ps'champion' == 'Ezreal'

1. 对手风格画像

def opponent_style(self, target_champion='Ezreal'):

ez = self.psself.ps\['champion' == target_champion]\['match_id', 'team'].drop_duplicates()

opp = self.ps.merge(ez.rename(columns={'team': 'ez_team'}), on='match_id')

opp = oppopp\['team' != opp'ez_team']

team_match = opp.groupby('match_id', 'team').agg(

kills=('kills', 'sum'), deaths=('deaths', 'sum'),

gold=('gold', 'sum'), damage=('damage_to_champs', 'sum')

).reset_index()

team_match = team_match.merge(

self.matches\['match_id', 'team', 'first_blood_team', 'dragons', 'heralds', 'result'],

on='match_id', 'team'

)

team_match'first_blood' = (team_match'team' == team_match'first_blood_team').astype(int)

team_match'win' = (team_match'result' == 'win').astype(int)

资源倾斜:每场每选手经济/伤害/补刀占比

totals = opp.groupby('match_id', 'team')\['gold', 'damage_to_champs', 'cs'].transform('sum')

opp = opp.assign(

gold_share=opp'gold' / totals'gold',

damage_share=opp'damage_to_champs' / totals'damage_to_champs',

cs_share=opp'cs' / totals'cs'

)

bias = opp.groupby('match_id', 'team').agg(

max_gold_share=('gold_share', 'max'),

max_damage_share=('damage_share', 'max'),

gold_share_std=('gold_share', 'std')

).reset_index()

team_match = team_match.merge(bias, on='match_id', 'team')

style = team_match.groupby('team').agg(

matches=('match_id', 'nunique'),

avg_kills=('kills', 'mean'),

avg_deaths=('deaths', 'mean'),

first_blood_rate=('first_blood', 'mean'),

avg_dragons=('dragons', 'mean'),

avg_heralds=('heralds', 'mean'),

win_rate=('win', 'mean'),

avg_max_gold_share=('max_gold_share', 'mean'),

avg_max_damage_share=('max_damage_share', 'mean'),

gold_share_std=('gold_share_std', 'mean')

).reset_index()

style'style' = np.where(style'avg_kills' > style'avg_kills'.median(), '激进', '保守')

style'resource_bias' = np.where(style'avg_max_gold_share' > 0.35, '资源倾斜', '资源均衡')

return style.sort_values('matches', ascending=False)

2. 团队整体指标

def team_metrics(self):

team_match = self.ps.groupby('match_id', 'team').agg(

kills=('kills', 'sum'), deaths=('deaths', 'sum')

).reset_index()

team_match = team_match.merge(

self.matches[['match_id', 'team', 'first_blood_team', 'dragons',

'heralds', 'result', 'duration']],

on='match_id', 'team'

)

team_match'first_blood' = (team_match'team' == team_match'first_blood_team').astype(int)

team_match'win' = (team_match'result' == 'win').astype(int)

bins = 0, 1500, 1800, 2100, 2400, np.inf

labels = '\<25min', '25-30min', '30-35min', '35-40min', '\>40min'

team_match'time_bucket' = pd.cut(team_match'duration', bins=bins, labels=labels)

time_win = team_match.groupby('team', 'time_bucket', observed=True)'win'.mean().unstack(fill_value=np.nan)

overall = team_match.groupby('team').agg(

matches=('match_id', 'nunique'),

avg_kills=('kills', 'mean'),

avg_deaths=('deaths', 'mean'),

first_blood_rate=('first_blood', 'mean'),

avg_dragons=('dragons', 'mean'),

avg_heralds=('heralds', 'mean'),

win_rate=('win', 'mean')

).reset_index()

return overall.merge(time_win, on='team')

3. 英雄池与胜率

def champion_pool(self, player=None):

df = self.ps.copy()

if player:

df = dfdf\['player' == player]

pool = df.groupby('player', 'champion').agg(

games=('match_id', 'nunique'),

wins=('result', lambda x: (x == 'win').sum()),

kills=('kills', 'mean'),

deaths=('deaths', 'mean'),

assists=('assists', 'mean'),

gd15=('gd15', 'mean'),

xpd15=('xpd15', 'mean'),

cspm=('cspm', 'mean'),

dpm=('dpm', 'mean'),

damage_per_gold=('damage_per_gold', 'mean')

).reset_index()

pool'win_rate' = pool'wins' / pool'games'

pool'kda' = (pool'kills' + pool'assists') / pool'deaths'.replace(0, np.nan)

return pool.sort_values('player', 'games', ascending=True, False)

4. BP 趋势与协同

def bp_trends(self):

total_matches = self.matches'match_id'.nunique()

bp = self.bp.copy()

bp'is_ban' = bp'action' == 'ban'

bp'is_pick' = bp'action' == 'pick'

champ = bp.groupby('champion').agg(

bans=('is_ban', 'sum'),

picks=('is_pick', 'sum')

).reset_index()

champ'ban_rate' = champ'bans' / total_matches

champ'pick_rate' = champ'picks' / total_matches

中野组合

mid_jug = self.psself.ps\['role'.isin('MID', 'JUG')].pivot_table(

index='match_id', 'team', columns='role', values='champion', aggfunc='first'

).reset_index().dropna(subset='MID', 'JUG')

mid_jug = mid_jug.merge(self.matches\['match_id', 'team', 'result'], on='match_id', 'team')

mid_jug'combo' = mid_jug'MID' + ' + ' + mid_jug'JUG'

mid_jug_win = mid_jug.groupby('combo').agg(

games=('match_id', 'nunique'),

win_rate=('result', lambda x: (x == 'win').mean())

).reset_index()

下路双人组

bot_sup = self.psself.ps\['role'.isin('ADC', 'SUP')].pivot_table(

index='match_id', 'team', columns='role', values='champion', aggfunc='first'

).reset_index().dropna(subset='ADC', 'SUP')

bot_sup = bot_sup.merge(self.matches\['match_id', 'team', 'result'], on='match_id', 'team')

bot_sup'combo' = bot_sup'ADC' + ' + ' + bot_sup'SUP'

bot_sup_win = bot_sup.groupby('combo').agg(

games=('match_id', 'nunique'),

win_rate=('result', lambda x: (x == 'win').mean())

).reset_index()

return champ, mid_jug_win, bot_sup_win

5. 对线期

def laning_phase(self):

return self.ps.groupby('player', 'champion').agg(

games=('match_id', 'nunique'),

gd15=('gd15', 'mean'),

xpd15=('xpd15', 'mean'),

cspm=('cspm', 'mean'),

solo_kills=('solo_kills', 'sum'),

solo_deaths=('solo_deaths', 'sum'),

gpm=('gpm', 'mean'),

dpm=('dpm', 'mean')

).reset_index()

6. 团战期

def teamfight_phase(self):

return self.ps.groupby('player', 'champion').agg(

cc_hit_rate=('cc_hit_rate', 'mean'),

skillshot_hit_rate=('skillshot_hit_rate', 'mean'),

survive_rate=('survive_rate', 'mean'),

damage_to_champs=('damage_to_champs', 'mean'),

damage_to_towers=('damage_to_towers', 'mean'),

damage_to_epics=('damage_to_epics', 'mean')

).reset_index()

7. 眼位深度分析

def ward_analysis(self):

w = self.wards.copy()

w'is_trinket' = w'ward_type' == 'trinket'

w'is_control' = w'ward_type' == 'control'

ward_stats = w.groupby('player').agg(

wards_placed=('ward_id', 'count'),

avg_duration=('duration', 'mean'),

killed_rate=('killed', 'mean'),

trinket_ratio=('is_trinket', 'mean'),

control_ratio=('is_control', 'mean')

).reset_index()

first_trinket = ww\['is_trinket'].sort_values('timestamp').groupby('match_id', 'player').first().reset_index()

first_pos = first_trinket.groupby('player').agg(

avg_x=('x', 'mean'), avg_y=('y', 'mean'), sample=('x', 'count')

).reset_index()

return ward_stats, first_pos

8. 视野控制效率

def vision_control(self):

return self.ps.groupby('player', 'champion').agg(

vision_score=('vision_score', 'mean'),

wards_killed=('wards_killed', 'mean'),

control_wards=('control_wards', 'mean')

).reset_index()

9. 核心对位指标

def core_matchup(self):

return self.ps.groupby('player', 'champion').agg(

gd15=('gd15', 'mean'),

xpd15=('xpd15', 'mean'),

cspm=('cspm', 'mean'),

gpm=('gpm', 'mean'),

dpm=('dpm', 'mean'),

kp=('kp', 'mean')

).reset_index()

10. 进阶效率与影响力

def advanced_efficiency(self):

return self.ps.groupby('player', 'champion').agg(

damage_per_gold=('damage_per_gold', 'mean'),

VC=('VC', 'mean'),

ineffective_death_rate=('ineffective_death_rate', 'mean'),

free_kill_rate=('free_kill_rate', 'mean')

).reset_index()

---------------- 模拟数据生成 ----------------

def make_demo_data(n_matches=60, seed=42):

rng = np.random.default_rng(seed)

teams = f'Team{i}' for i in range(1, 9)

players = f'P{i}' for i in range(1, 41)

champions = ['Ezreal', 'KaiSa', 'Jinx', 'Aphelios', 'Leona', 'Nautilus',

'LeeSin', 'Viego', 'Ahri', 'Sylas', 'Ornn', 'Gnar']

roles = 'TOP', 'JUG', 'MID', 'ADC', 'SUP'

matches, player_stats, ward_events, bp_events = \[\], \[\], \[\], \[\]

for m in range(n_matches):

match_id = f'M{m:04d}'

t1, t2 = rng.choice(teams, 2, replace=False)

duration = int(rng.normal(1800, 300))

winner = rng.choice(t1, t2)

first_blood_team = rng.choice(t1, t2)

for team, opp in (t1, t2), (t2, t1):

matches.append(dict(

match_id=match_id, team=team, opponent_team=opp,

result='win' if winner == team else 'loss',

first_blood_team=first_blood_team, duration=duration,

dragons=int(rng.integers(0, 5)), heralds=int(rng.integers(0, 3))

))

for role in roles:

player = rng.choice(players)

champ = rng.choice(champions)

if role == 'ADC' and rng.random() < 0.4:

champ = 'Ezreal'

kills = int(rng.poisson(4))

deaths = int(rng.poisson(4))

assists = int(rng.poisson(7))

gold = float(rng.normal(12000, 3000))

cs = float(rng.normal(220, 50))

damage = float(rng.normal(20000, 8000))

gd15 = float(rng.normal(0, 500))

xpd15 = float(rng.normal(0, 500))

player_stats.append(dict(

match_id=match_id, team=team, opponent_team=opp,

player=player, champion=champ, role=role,

kills=kills, deaths=deaths, assists=assists,

gold=gold, cs=cs, damage_to_champs=damage,

damage_to_towers=float(rng.normal(3000, 1000)),

damage_to_epics=float(rng.normal(1000, 500)),

vision_score=float(rng.normal(30, 10)),

wards_killed=int(rng.poisson(5)),

control_wards=int(rng.poisson(3)),

gd15=gd15, xpd15=xpd15,

cspm=cs / (duration / 60),

gpm=gold / (duration / 60),

dpm=damage / (duration / 60),

kp=float(rng.uniform(0.4, 0.9)),

solo_kills=int(rng.poisson(0.5)),

solo_deaths=int(rng.poisson(0.5)),

cc_hits=int(rng.poisson(8)),

cc_attempts=int(rng.poisson(12)),

skillshots_hits=int(rng.poisson(20)),

skillshots_attempts=int(rng.poisson(30)),

teamfight_survived=int(rng.poisson(3)),

teamfights=int(rng.poisson(5)),

ineffective_deaths=int(rng.poisson(1)),

free_kills=int(rng.poisson(1))

))

眼位事件

for i in range(10):

ward_events.append(dict(

ward_id=f'W{m}_{i}', match_id=match_id,

player=rng.choice(players), team=rng.choice(t1, t2),

timestamp=int(rng.integers(60, duration)),

x=float(rng.normal(7500, 2000)),

y=float(rng.normal(7500, 2000)),

ward_type=rng.choice('trinket', 'control', 'yellow'),

duration=float(rng.normal(90, 30)),

killed=int(rng.random() < 0.4),

before_objective=int(rng.random() < 0.3)

))

BP 事件

for i in range(10):

bp_events.append(dict(

match_id=match_id, team=rng.choice(t1, t2),

champion=rng.choice(champions),

action=rng.choice('ban', 'pick'),

order=int(rng.integers(1, 11)),

role=rng.choice(roles)

))

return LoLData(

pd.DataFrame(matches),

pd.DataFrame(player_stats),

pd.DataFrame(ward_events),

pd.DataFrame(bp_events)

)

if name == 'main':

data = make_demo_data()

analyzer = EzrealOpponentAnalyzer(data)

print("=== 对手风格画像 ===")

print(analyzer.opponent_style('Ezreal').head())

print("\n=== 团队整体指标 ===")

print(analyzer.team_metrics().head())

print("\n=== 英雄池与胜率 ===")

print(analyzer.champion_pool().head())

print("\n=== BP 趋势 ===")

champ, mid_jug, bot_sup = analyzer.bp_trends()

print(champ.head())

print(mid_jug.head())

print(bot_sup.head())

print("\n=== 对线期 ===")

print(analyzer.laning_phase().head())

print("\n=== 团战期 ===")

print(analyzer.teamfight_phase().head())

print("\n=== 眼位分析 ===")

ward_stats, first_pos = analyzer.ward_analysis()

print(ward_stats.head())

print(first_pos.head())

print("\n=== 视野控制效率 ===")

print(analyzer.vision_control().head())

print("\n=== 核心对位指标 ===")

print(analyzer.core_matchup().head())

print("\n=== 进阶效率与影响力 ===")

print(analyzer.advanced_efficiency().head())

```

这段代码会生成模拟比赛数据,并输出各项分析结果。理论上实际使用时,只需把 make_demo_data() 替换为从 CSV 读取真实数据,并保证字段与代码中的列名一致即可。

禁止一切转载或商用。

相关推荐
日耗10亿Token被老板赶出门7 小时前
大模型接口返回 200,为什么程序还是报错?
python
小小张说故事7 小时前
Python 的 __new__ 到底管什么?和 __init__ 的分工一次讲透Python后端python知识整理日新计划
python
ESDWAN7 小时前
跨境网络延迟、抖动和丢包有什么区别?如何判断业务影响
开发语言·网络·计算机网络·php
Code_Solitude7 小时前
C语言分支结构练习题(华师版)
c语言·开发语言
青少儿编程课堂7 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会C/C++考级:并查集连通块与朋友圈问题解析
c++·python·算法·bfs·信息学竞赛
外收内放7 小时前
数据分析(数据排序)
python·学习·数据分析
data analyse 4567 小时前
SourceMap 上传版本与报错版本对不上?版本校验与错误还原设计
大数据·前端·人工智能·数据分析
从小白到软工7 小时前
4-1 指针
c语言·开发语言·数据结构·经验分享·ai编程
Pniubi7 小时前
agent处理封号怎么解决误封问题
开发语言·python
.Hypocritical.7 小时前
【Java 进阶】泛型深度解析|泛型类、泛型方法、通配符、类型擦除、PECS 原则
java·开发语言·泛型