本文目录导读:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
足球传球长短传比例统计脚本
支持从CSV或纯文本数据中读取传球记录,统计短传/中传/长传的比例分布。
"""
import csv
import sys
import os
from collections import Counter
from typing import List, Dict, Tuple
# ==================== 配置参数 ====================
# 传球距离分类阈值(单位:码或米,请根据你的数据统一单位)
SHORT_PASS_MAX = 15 # 小于等于此距离为短传
LONG_PASS_MIN = 30 # 大于等于此距离为长传
# 中间范围(15 < 距离 < 30)自动归为中传
# ==================== 核心统计函数 ====================
def classify_pass(distance: float) -> str:
"""根据距离对传球进行分类"""
if distance <= SHORT_PASS_MAX:
return "短传"
elif distance >= LONG_PASS_MIN:
return "长传"
else:
return "中传"
def parse_csv(file_path: str) -> List[float]:
"""从CSV文件中读取传球距离数据(支持'distance'列或第一列)"""
distances = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
# 使用DictReader自动读取表头
reader = csv.DictReader(f)
# 如果CSV没有表头,则手动指定第一列为距离
if not reader.fieldnames:
f.seek(0)
reader = csv.reader(f)
for row in reader:
if row and row[0].strip():
try:
distances.append(float(row[0]))
except ValueError:
print(f"警告: 无法解析行 '{row}' 中的数据,已跳过")
else:
# 优先查找常见列名
distance_col = None
possible_names = ['distance', 'Distance', '距离', 'pass_length', 'length', 'dist']
for name in possible_names:
if name in reader.fieldnames:
distance_col = name
break
if not distance_col:
# 默认使用第一列
distance_col = reader.fieldnames[0]
print(f"提示: 未找到标准距离列名,默认使用 '{distance_col}' 列")
for row in reader:
try:
val = row[distance_col].strip()
if val:
distances.append(float(val))
except (ValueError, KeyError):
print(f"警告: 第 {reader.line_num} 行距离数据无效")
except FileNotFoundError:
print(f"错误: 文件 '{file_path}' 不存在")
sys.exit(1)
except Exception as e:
print(f"读取文件时发生错误: {e}")
sys.exit(1)
return distances
def parse_txt(file_path: str) -> List[float]:
"""从纯文本文件中读取数字(每行一个距离,或空格分隔)"""
distances = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
# 支持逗号或空格分隔的多值行
parts = line.replace(',', ' ').split()
for part in parts:
try:
distances.append(float(part))
except ValueError:
print(f"警告: 第 {line_num} 行 '{part}' 不是有效数字,已忽略")
except FileNotFoundError:
print(f"错误: 文件 '{file_path}' 不存在")
sys.exit(1)
return distances
def load_data(file_path: str) -> List[float]:
"""根据文件扩展名自动选择解析方法"""
ext = os.path.splitext(file_path)[1].lower()
if ext == '.csv':
return parse_csv(file_path)
else: # 默认按文本处理
return parse_txt(file_path)
def compute_distribution(distances: List[float]) -> Tuple[Dict[str, int], Dict[str, float]]:
"""计算长短传比例分布"""
if not distances:
print("错误: 没有有效的传球距离数据")
sys.exit(1)
# 统计分类
classification = Counter(classify_pass(d) for d in distances)
total = len(distances)
# 计算百分比
percentages = {k: (v / total) * 100 for k, v in classification.items()}
return classification, percentages
# ==================== 输出格式化 ====================
def print_report(distances: List[float], counts: Dict[str, int], percentages: Dict[str, float]):
"""打印清晰的分析报告"""
total = len(distances)
print("\n" + "="*50)
print(f"传球距离统计分析报告")
print("="*50)
print(f"总传球次数: {total}")
print(f"分类标准: 短传 ≤ {SHORT_PASS_MAX} | 中传 ({SHORT_PASS_MAX} < d < {LONG_PASS_MIN}) | 长传 ≥ {LONG_PASS_MIN}")
print("-"*50)
# 按顺序输出三类
for category in ['短传', '中传', '长传']:
count = counts.get(category, 0)
pct = percentages.get(category, 0)
bar_len = int(pct / 2) # 简化可视化,每'#'代表2%
print(f"{category}: {count:5d} 次 ({pct:5.1f}%) {'#' * bar_len}")
# 额外统计信息
print("-"*50)
if distances:
avg = sum(distances) / len(distances)
max_d = max(distances)
min_d = min(distances)
print(f"平均传球距离: {avg:.1f}")
print(f"最长传球: {max_d:.1f}")
print(f"最短传球: {min_d:.1f}")
# 长短传比值(短传/长传)
short = counts.get('短传', 0)
long = counts.get('长传', 0)
if long > 0:
ratio = short / long
print(f"短传/长传比例: {ratio:.2f} : 1")
else:
print("短传/长传比例: 无长传数据")
print("="*50)
# ==================== 主程序入口 ====================
def main():
# 命令行参数处理
if len(sys.argv) < 2:
print("用法: python pass_analysis.py <数据文件> [--thresholds 短传阈值 长传阈值]")
print("示例: python pass_analysis.py passes.csv")
print(" python pass_analysis.py passes.txt --thresholds 10 25")
sys.exit(1)
file_path = sys.argv[1]
# 可选阈值参数
global SHORT_PASS_MAX, LONG_PASS_MIN
if '--thresholds' in sys.argv:
idx = sys.argv.index('--thresholds')
try:
SHORT_PASS_MAX = float(sys.argv[idx+1])
LONG_PASS_MIN = float(sys.argv[idx+2])
except (IndexError, ValueError):
print("错误: --thresholds 需要两个数值参数")
sys.exit(1)
# 加载数据
print(f"正在读取文件: {file_path}")
distances = load_data(file_path)
print(f"成功读取 {len(distances)} 条传球数据")
# 统计与输出
counts, percentages = compute_distribution(distances)
print_report(distances, counts, percentages)
if __name__ == "__main__":
main()
使用方法
-
数据准备:将传球距离数据保存为 CSV 或纯文本文件
- CSV 格式示例(支持
distance列名或直接用第一列):distance 12.5 28.3 45.2 8.7
- 纯文本格式示例(每行一个数字,或空格分隔):
5 28.3 45.2 8.7 19.4 33.1
- CSV 格式示例(支持
-
运行脚本:
python pass_analysis.py 传球数据.csv
-
自定义阈值(可选):
python pass_analysis.py 传球数据.csv --thresholds 10 25
输出示例
==================================================
传球距离统计分析报告
==================================================
总传球次数: 156
分类标准: 短传 ≤ 15 | 中传 (15 < d < 30) | 长传 ≥ 30
--------------------------------------------------
短传: 78 次 ( 50.0%) #########################
中传: 52 次 ( 33.3%) #################
长传: 26 次 ( 16.7%) #########
--------------------------------------------------
平均传球距离: 21.4
最长传球: 67.2
最短传球: 3.1
短传/长传比例: 3.00 : 1
==================================================
功能特点
- 自动识别文件类型:根据扩展名选择解析方式
- 灵活阈值设置:通过命令行参数调整长短传分类标准
- 容错处理:自动跳过无效数据并给出警告
- 可视化输出:用条状图直观展示比例分布
- 额外统计:包含平均距离、最大/最小值和短长传比值
您可以根据实际数据格式调整 parse_csv 和 parse_txt 函数,或修改分类阈值来适配您的分析需求。