统计长短传比例分布的实用脚本
下面给你一套可落地的脚本方案,适用于日志/埋点/交易流水中的"长传/短传"(如足球传球、网络数据包、消息体大小等)比例统计与分布分析,假设你的数据表中已有 pass_type(或 length)字段。

核心统计口径先明确
| 指标 | 定义 |
|---|---|
| 长传数 | length >= threshold(如足球 30m,网络包 1500B) |
| 短传数 | length < threshold |
| 长短传比例 | long_count / short_count(或占比 long/(long+short)) |
| 分布 | 按时间窗口 / 球员 / 会话 / 分桶统计比例分布(直方图、分位数) |
SQL 版(最通用,适合数仓/MySQL/ClickHouse)
整体长短传比例
SELECT
SUM(CASE WHEN length >= 30 THEN 1 ELSE 0 END) AS long_cnt,
SUM(CASE WHEN length < 30 THEN 1 ELSE 0 END) AS short_cnt,
ROUND(SUM(CASE WHEN length >= 30 THEN 1 ELSE 0 END) * 1.0
/ NULLIF(SUM(CASE WHEN length < 30 THEN 1 ELSE 0 END), 0), 3) AS long_short_ratio,
ROUND(SUM(CASE WHEN length >= 30 THEN 1 ELSE 0 END) * 1.0
/ COUNT(*), 3) AS long_ratio
FROM passes
WHERE dt = '2024-01-01';
按球员/维度分组
SELECT
player_id,
COUNT(*) AS total,
SUM(length >= 30) AS long_cnt, -- PostgreSQL/ClickHouse 布尔求和
SUM(length < 30) AS short_cnt,
ROUND(SUM(length >= 30)::numeric / NULLIF(SUM(length < 30), 0), 3) AS ratio
FROM passes
GROUP BY player_id
ORDER BY ratio DESC;
比例分布(直方图,按每场/每会话算比例再分桶)
WITH per_game AS (
SELECT
game_id,
SUM(CASE WHEN length >= 30 THEN 1 ELSE 0 END) * 1.0
/ COUNT(*) AS long_ratio
FROM passes
GROUP BY game_id
)
SELECT
CASE
WHEN long_ratio < 0.1 THEN '0-10%'
WHEN long_ratio < 0.2 THEN '10-20%'
WHEN long_ratio < 0.3 THEN '20-30%'
WHEN long_ratio < 0.5 THEN '30-50%'
ELSE '50%+'
END AS bucket,
COUNT(*) AS games,
ROUND(COUNT(*) * 1.0 / SUM(COUNT(*)) OVER (), 3) AS pct
FROM per_game
GROUP BY bucket
ORDER BY bucket;
Python 版(pandas,适合离线分析 + 画图)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 假设 df 有列: player_id, game_id, length
THRESHOLD = 30
df = pd.read_csv("passes.csv")
df["is_long"] = (df["length"] >= THRESHOLD).astype(int)
# 1) 整体比例
total = len(df)
long_cnt = df["is_long"].sum()
short_cnt = total - long_cnt
print(f"长传 {long_cnt} / 短传 {short_cnt} = 比例 {long_cnt/short_cnt:.3f}")
print(f"长传占比 {long_cnt/total:.3f}")
# 2) 按球员
by_player = df.groupby("player_id")["is_long"].agg(
total="size", long_cnt="sum"
)
by_player["short_cnt"] = by_player["total"] - by_player["long_cnt"]
by_player["ratio"] = by_player["long_cnt"] / by_player["short_cnt"].replace(0, np.nan)
by_player["long_pct"] = by_player["long_cnt"] / by_player["total"]
print(by_player.sort_values("ratio", ascending=False).head(10))
# 3) 比例分布(每场长传占比直方图)
per_game = df.groupby("game_id")["is_long"].mean()
per_game.hist(bins=[0, .1, .2, .3, .5, .7, 1], edgecolor="k")
plt.xlabel("长传占比"); plt.ylabel("场次数"); plt.title("长短传比例分布")
plt.savefig("ratio_dist.png", dpi=150)
# 4) 分位数输出
print(per_game.quantile([.1, .25, .5, .75, .9]))
常见坑与建议
- 阈值统一:长传阈值必须集中配置(建议写入配置表或常量),避免同一次分析口径不一致。
- 除零处理:
NULLIF/replace(0, np.nan)必须加,否则短传为 0 时比值爆掉。 - 比例 vs 比率:
long/short在短传少时会极端放大,报告里最好同时给"长传占比"(long/total),更稳健。 - 分布优于均值:长短传比例通常不是正态分布,用直方图 + 分位数(P10/P50/P90)比看均值更真实。
- 时间窗口对齐:按场/按天统计时注意窗口完整性(比赛未结束、小时未满),否则比例分布会偏移。
- 样本量过滤:分组统计时过滤
total < N(如少于 20 次传球)的样本,避免小样本噪声。
如果你告诉我数据源类型(MySQL/ClickHouse/CSV/ES)和"长/短传"的实际含义(足球距离?网络包大小?消息长度?),我可以把脚本改成直接能跑的版本,并补上按时间序列的趋势统计。