本文目录导读:

在足球数据分析(或其他球类运动)中,统计“长短传比例”通常涉及对传球数据的分类,由于“长传”和“短传”没有统一的国际标准,通常需要人为定义阈值(如距离超过25米或30米算长传)。
以下提供三种不同场景的实用脚本方案,从最直接的Python数据处理到可视化,你可以根据数据格式选择。
基于Python + Pandas(最通用)
适用场景:你有一份包含传球距离的表格数据(CSV/Excel),字段类似 passer, receiver, distance。
核心逻辑:设定阈值(threshold = 25),将距离分为“长传”和“短传”,然后统计组内比例。
import pandas as pd
import numpy as np
# --- 配置参数 ---
THRESHOLD = 25 # 单位:米,大于等于此值为长传
LONG_LABEL = '长传'
SHORT_LABEL = '短传'
# --- 加载数据(假设CSV有 distance 列) ---
# df = pd.read_csv('passing_data.csv')
# 为演示,创建模拟数据:
np.random.seed(42)
df = pd.DataFrame({
'player': np.random.choice(['A', 'B', 'C'], 100),
'distance': np.random.uniform(5, 50, 100) # 距离5-50米
})
# --- 分类函数 ---
def classify_pass(distance):
return LONG_LABEL if distance >= THRESHOLD else SHORT_LABEL
df['pass_type'] = df['distance'].apply(classify_pass)
# --- 统计全局比例 ---
global_counts = df['pass_type'].value_counts()
global_percent = df['pass_type'].value_counts(normalize=True) * 100
print("=== 全局长短传比例 ===")
print(f"总传球数: {len(df)}")
for label in [LONG_LABEL, SHORT_LABEL]:
if label in global_percent.index:
print(f"{label}: {global_counts[label]}次 ({global_percent[label]:.1f}%)")
# --- 统计每个球员的比例(按球员分组) ---
print("\n=== 各球员长短传分布 ===")
player_stats = df.groupby(['player', 'pass_type']).size().unstack(fill_value=0)
player_stats['总计'] = player_stats.sum(axis=1)
# 计算百分比(针对每行)
player_stats[LONG_LABEL + '_%'] = (player_stats[LONG_LABEL] / player_stats['总计'] * 100).round(1)
player_stats[SHORT_LABEL + '_%'] = (player_stats[SHORT_LABEL] / player_stats['总计'] * 100).round(1)
print(player_stats[[LONG_LABEL, SHORT_LABEL, LONG_LABEL+'_%', SHORT_LABEL+'_%']])
基于Python + Matplotlib(可视化分布)
适用场景:你想直观查看“距离”的连续分布,并自动用颜色划分长短传区域。
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 假设 df 已有 distance 列
# 此处直接用上面的模拟数据
# ...
# --- 绘制直方图 ---
plt.figure(figsize=(10, 5))
# 按分类分色绘图
plt.hist(df[df['pass_type'] == '长传']['distance'],
bins=15, alpha=0.7, color='red', label='长传 (>=25m)')
plt.hist(df[df['pass_type'] == '短传']['distance'],
bins=15, alpha=0.7, color='blue', label='短传 (<25m)')
# 画阈值线
plt.axvline(x=THRESHOLD, color='k', linestyle='--', linewidth=1,
label=f'阈值: {THRESHOLD}m')
plt.xlabel('传球距离 (米)')
plt.ylabel('传球次数')f'传球距离分布 (长短传比例: {global_percent[LONG_LABEL]:.1f}% vs {global_percent[SHORT_LABEL]:.1f}%)')
plt.legend()
plt.grid(axis='y', alpha=0.3)
plt.show()
纯SQL(针对数据库表)
适用场景:数据存在MySQL/PostgreSQL中,表结构为 passes(id, player_id, distance)。
-- 1. 设置阈值(如果你不想改代码,可以临时用WITH定义)
WITH params AS (SELECT 25 AS threshold)
-- 2. 计算全局统计
SELECT
CASE WHEN distance >= (SELECT threshold FROM params) THEN '长传' ELSE '短传' END AS pass_type,
COUNT(*) AS total_passes,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 1) AS percentage
FROM passes
GROUP BY pass_type;
-- 3. 如果需要按球员分组统计:
SELECT
player_id,
SUM(CASE WHEN distance >= 25 THEN 1 ELSE 0 END) AS long_pass_count,
SUM(CASE WHEN distance < 25 THEN 1 ELSE 0 END) AS short_pass_count,
ROUND(
100.0 * SUM(CASE WHEN distance >= 25 THEN 1 ELSE 0 END)
/ COUNT(*), 1
) AS long_pct
FROM passes
GROUP BY player_id
ORDER BY long_pct DESC;
附加建议:如何定义“长短传”?
如果你没有标准距离阈值,可以尝试以下两种动态方法:
- 基于百分位数:将传球距离按从大到小排序,前20%视为“长传”,后80%视为“短传”(利用
df['distance'].quantile(0.8))。 - 基于具体战术:将进入对方半场的传球算作长传,但需要额外的坐标数据(
x,y起始点和终点)。
# 替代方法:动态阈值(中位数或分位数) DYNAMIC_THRESHOLD = df['distance'].median() # 以中位数为界 # 或者按前80%分位: P80 = df['distance'].quantile(0.8)
运行说明
- 如果数据量大,建议使用
pandas的read_csv后直接运行方案一。 - 如果只需要一个比率,直接运行方案一打印结果即可。
- 如果数据是JSON格式(来自API),只需将
pd.read_csv替换为pd.DataFrame(data_list)。
如果提供具体的文件格式(如JSON、XML)或字段名,我可以进一步修改脚本。