本文目录导读:

在足球数据分析中,“长短传比例”通常指短传(成功率较高、距离较近)与长传(距离远、转移或发动进攻)的分布情况,由于不同数据源(如Opta、StatsBomb)对“长传”的定义不同(通常以距离为标准,如超过30米/35码,或传球性质为标准),这里我提供一个通用且可自定义阈值的Python统计脚本。
该脚本将自动完成以下功能:
- 读取包含传球数据的CSV/Excel文件。
- 根据你设定的距离阈值(或自定义规则)将传球分为“短传”和“长传”。
- 统计总占比、成功/失败比例,以及不同区域(前场/后场)的分布。
- 生成可视化图表(堆叠柱状图)。
准备工作
请确保你的传球数据表包含以下列(名称可自定义,脚本中会提示修改):
- player:球员名(可选)
- x, y:传球起点坐标
- end_x, end_y:传球终点坐标
- outcome:传球结果(如 “Complete” 或 “Incomplete”)
如果没有坐标,只有“传球距离”和“结果”列,也可以直接使用。
脚本代码(Python + Pandas + Matplotlib)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# ================== 配置区 ==================
FILE_PATH = 'passes.csv' # 文件路径
LONG_PASS_DISTANCE = 30.0 # 长传定义:距离(米)超过此值即算长传
# 如果你的数据是英制(码),请调整此值,25码 = 22.86米
# 坐标比例尺:如果坐标是0-100的百分比坐标,通常1单位=1米;如果是0-1,则乘以100
# 如果坐标单位是米,保持为1;如果是0-1,设置为100
COORD_SCALE = 1.0
# 列名映射(如果你的列名不同,请修改)
COL_X = 'x'
COL_Y = 'y'
COL_END_X = 'end_x'
COL_END_Y = 'end_y'
COL_OUTCOME = 'outcome'
SUCCESS_LABEL = 'Complete' # 表示成功传球的标记
# ================== 配置结束 ==================
print("正在读取数据...")
df = pd.read_csv(FILE_PATH)
# 数据预处理:确保坐标是数值
for col in [COL_X, COL_Y, COL_END_X, COL_END_Y]:
df[col] = pd.to_numeric(df[col], errors='coerce')
# 删除缺少坐标的数据
df = df.dropna(subset=[COL_X, COL_Y, COL_END_X, COL_END_Y])
# 计算传球距离(欧氏距离)
df['pass_distance'] = np.sqrt(
(df[COL_END_X] - df[COL_X])**2 +
(df[COL_END_Y] - df[COL_Y])**2
) * COORD_SCALE
# 根据距离分类
df['pass_type'] = np.where(df['pass_distance'] > LONG_PASS_DISTANCE, '长传', '短传')
# 判断成功与否(标准化:成功标记设为1)
df['is_success'] = (df[COL_OUTCOME] == SUCCESS_LABEL).astype(int)
print(f"总传球次数: {len(df)}")
print(f"平均传球距离: {df['pass_distance'].mean():.2f} 米")
# ========== 统计总体分布 ==========
print("\n===== 长短传总体分布 =====")
type_counts = df['pass_type'].value_counts()
type_percent = df['pass_type'].value_counts(normalize=True) * 100
for t in ['短传', '长传']:
if t in type_counts.index:
print(f"{t}: {type_counts[t]}次 ({type_percent[t]:.1f}%)")
else:
print(f"{t}: 0次 (0.0%)")
# ========== 统计成功率 ==========
print("\n===== 长短传成功率 =====")
success_stats = df.groupby('pass_type')['is_success'].agg(['mean', 'count'])
success_stats['mean'] = success_stats['mean'] * 100
success_stats.columns = ['成功率(%)', '总次数']
print(success_stats)
# ========== 按区域统计(前后场) ==========
print("\n===== 按球场区域分布(以x轴中线为界) =====")
# 假设x为横坐标,0为后场,100为前场
mid_line = 50.0
df['zone'] = np.where(df[COL_X] > mid_line, '前场', '后场')
# 分组统计
zone_type_stats = df.groupby(['zone', 'pass_type']).size().unstack(fill_value=0)
zone_type_stats['总数'] = zone_type_stats.sum(axis=1)
# 计算比例
zone_type_stats['短传占比'] = zone_type_stats.get('短传', 0) / zone_type_stats['总数'] * 100
zone_type_stats['长传占比'] = zone_type_stats.get('长传', 0) / zone_type_stats['总数'] * 100
print(zone_type_stats[['总数', '短传占比', '长传占比']])
# ========== 可视化 ==========
plt.rcParams['font.sans-serif'] = ['SimHei'] # 支持中文
plt.rcParams['axes.unicode_minus'] = False
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 图1:长短传数量及成功失败堆叠柱状图
plot_df = df.groupby(['pass_type', 'is_success']).size().unstack(fill_value=0)
plot_df.columns = ['失败', '成功']
plot_df = plot_df.reindex(['短传', '长传'])
plot_df.plot(kind='bar', stacked=True, ax=axes[0], color=['#d62728', '#2ca02c'])
axes[0].set_title('长短传成功/失败分布')
axes[0].set_ylabel('传球次数')
axes[0].set_xlabel('传球类型')
axes[0].legend(title='结果')
axes[0].tick_params(axis='x', rotation=0)
# 图2:前后场长短传占比堆积图
zone_chart_data = zone_type_stats[['短传占比', '长传占比']]
zone_chart_data.plot(kind='bar', stacked=True, ax=axes[1], color=['#1f77b4', '#ff7f0e'])
axes[1].set_title('前后场长短传占比分布')
axes[1].set_ylabel('占比 (%)')
axes[1].set_xlabel('球场区域')
axes[1].legend(title='传球类型')
axes[1].tick_params(axis='x', rotation=0)
plt.tight_layout()
plt.show()
# ========== 附加:进阶分析(可选) ==========
# 输出每个球员的长短传比例(如果有player列)
if 'player' in df.columns:
print("\n===== 球员长短传比例 (前10) =====")
player_stats = df.groupby(['player', 'pass_type']).size().unstack(fill_value=0)
player_stats['总数'] = player_stats.sum(axis=1)
player_stats['短传占比'] = player_stats.get('短传', 0) / player_stats['总数'] * 100
player_stats['长传占比'] = player_stats.get('长传', 0) / player_stats['总数'] * 100
# 过滤至少尝试10次传球的球员
player_stats = player_stats[player_stats['总数'] >= 10]
top_players = player_stats.sort_values('长传占比', ascending=False).head(10)
print(top_players[['总数', '短传占比', '长传占比']])
使用方法
-
安装依赖:
pip install pandas matplotlib seaborn numpy
-
准备数据:将你的传球数据保存为
passes.csv,确保包含上述列。 -
调整参数:根据你的数据源单位,修改
LONG_PASS_DISTANCE和COORD_SCALE。
常见数据格式适配
| 数据源 | 坐标范围 | 脚本调整 |
|---|---|---|
| StatsBomb | 0-120 (x), 0-80 (y) | COORD_SCALE = 1,阈值设为 30 (米) |
| Opta | 0-100 | COORD_SCALE = 1,阈值设为 25~30,视打法而定 |
| 事件数据(无坐标) | 只有“distance”列 | 可将脚本中计算距离的行替换为 df['distance'] |
如果你想直接用传球距离(distance列)而非坐标,替换这段代码:
# 原来的坐标计算 df['pass_distance'] = df['distance'] # 直接用现成的
输出示例
运行后终端会输出:
总传球次数: 2456
平均传球距离: 18.32 米
===== 长短传总体分布 =====
短传: 1820次 (74.1%)
长传: 636次 (25.9%)
===== 长短传成功率 =====
成功率(%) 总次数
pass_type
长传 56.2 636
短传 91.3 1820
以及两张图表:
- 左图:红绿堆叠柱状图,显示短传/长传的成功与失败次数。
- 右图:前后场长短传占比,直观看出球队在进攻三区是否更倾向于短传渗透。
进阶玩法
- 定义复杂长传:如果长传不仅是距离,还有“传中”、“转移”等属性,可在数据中加入
type列,df['pass_type'] = df.apply(lambda x: '长传' if (x['distance']>30 and x['type']=='cross') else '短传', axis=1)。 - 时间序列:按分钟分组,看比赛不同阶段的传接球策略变化。
- 方向分布:结合传球终点x坐标,判断是向前传球还是横传/回传。
这个脚本可完全按需修改,适应不同的统计口径,如果数据格式有特定要求,欢迎提供样例,我可以进一步优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播