☰
Python实战:用11秒08的短跑成绩带你学会数据分析与可视化
2026/9/25 7:25:15 网站建设 项目流程

最近看到全国田径锦标赛女子100米决赛的消息,17岁小将陈妤颉跑出11秒08的成绩夺冠,赛后的那句“感谢大家来到现场观看比赛”也让人印象很深。看到成绩数据的时候,我第一反应不是单纯感叹“好快”,而是想到另一个问题:如果我们用数据分析的视角去看这个成绩,11秒08到底意味着什么?运动员的平均速度是多少?换算成普通人熟悉的配速又是什么概念?这类问题其实很适合用Python做一次实战分析。

所以这篇文章就不只聊体育新闻,而是把“陈妤颉11秒08夺冠”这个真实成绩作为案例,带大家从零搭建一个短跑成绩数据分析小工具,包含数据读取、速度与配速计算、可视化对比、常见报错排查等完整流程。无论你是刚开始学Python的数据爱好者,还是想给运动队做成绩管理的学生,这套流程都能直接参考。

1. 背景与核心概念

1.1 为什么关注100米短跑成绩

100米短跑是田径运动中衡量“绝对速度”的经典项目,比赛结果以秒为单位,小数点后两位的差距就可能决定排名。运动员的100米成绩不仅反映起跑反应、途中加速、最大速度维持等能力,也是评判短距离爆发力的重要指标。

从数据分析角度来看,100米成绩非常适合做量化分析,因为它的数据模型很简单:距离固定为100米,成绩就是时间,速度等于距离除以时间。

所以在运动训练和赛事复盘里,教练团队经常要处理大量成绩数据,比如历次比赛成绩、起跑反应时、分段计时、风速数据等。手动用Excel处理当然可以,但一旦数据量变大,或者需要批量生成报告、做图表对比时,Python的优势就体现出来了。

1.2 11秒08这个成绩意味着什么

“11秒08”不是随便一个数字。我们可以通过最基础的物理公式来做一次拆解。

100米11秒08的平均速度约为:

[ v = \frac{100}{11.08} \approx 9.03 \text{ m/s} ]

换算成更直观的单位:

[ 9.03 \times 3.6 \approx 32.49 \text{ km/h} ]

如果换算成跑步爱好者熟悉的“每公里配速”:

[ \frac{1000}{9.03} \approx 110.8 \text{ 秒/km} ]

也就是说,如果这个速度能按恒定速度维持一公里,大概1分51秒/公里。这和普通大众跑者的配速完全不是一个量级。

需要注意的是,上述计算只是“平均速度”。实际100米比赛中,运动员不可能全程匀速,起跑阶段速度较低,途中跑达到最大速度,最后阶段还有速度保持的问题。因此平均速度只能作为整体参考,要分析更精细的加速曲线,需要依靠高速计时设备或运动捕捉系统。

1.3 运动数据分析的常见应用场景

运动数据分析并不是什么高深概念,实际工作中经常用到,常见的场景包括:

  • 成绩记录与排名管理:把运动员多次比赛成绩整理成结构化数据,方便查询和比较。
  • 训练效果评估:对比不同阶段的成绩变化,判断训练计划是否有效。
  • 比赛复盘:结合起跑反应、分段计时等数据分析胜负差距。
  • 可视化汇报:用柱状图、折线图直观展示运动员表现,给教练或管理层看。
  • 成绩预测:基于历史成绩建立简单回归模型,预测下一阶段目标成绩。

本文的核心目标,是先解决最基础的数据处理和分析问题,包括读取数据、计算指标、生成图表,把这些流程打通后,后续扩展到预测和建模就比较自然了。

2. 环境准备与数据设计

2.1 运行环境与依赖

本文示例以 Python 环境为例,主要依赖以下库:

  • pandas:用于读取和处理CSV数据。
  • matplotlib:用于绘制对比图表。
  • os:Python标准库,处理文件路径。

版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。建议使用 Python 3.8 及以上版本。

如果还没有安装依赖,可以在命令行中执行:

pip install pandas matplotlib

如果你的网络环境下载较慢,可以使用国内镜像源,比如清华源:

pip install pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,可以验证一下导入是否正常:

import pandas as pd import matplotlib.pyplot as plt print(pd.__version__)

如果这段代码能正常输出pandas版本号,说明环境已经准备好。

2.2 数据文件设计

在分析成绩之前,需要把数据整理成结构化文件。这里选择CSV格式,因为它简单、轻量、用Excel可以打开,也方便pandas直接读取。

CSV文件的列设计建议:

字段名含义示例
name运动员姓名陈妤颉
event赛事名称全国田径锦标赛
time_s100米成绩(秒)11.08
wind风速(米/秒)0.0

注意,风速对短跑成绩有实际影响,顺风超过一定限制的成绩不会被认可为有效纪录。虽然本文示例不深入风速修正,但数据结构里保留这个字段是有必要的。

2.3 示例数据说明

为了演示分析流程,除陈妤颉的成绩使用新闻公开数据外,其他选手成绩均为模拟数据,仅用于代码演示,不代表任何真实比赛结果。

示例CSV文件内容如下:

name,event,time_s,wind 陈妤颉,全国田径锦标赛,11.08,0.0 选手A,模拟赛,11.25,0.4 选手B,模拟赛,11.42,-0.2 选手C,模拟赛,11.50,0.3

为什么其他选手用“选手A、选手B、选手C”而不是真实姓名?因为本文只确定陈妤颉的成绩来自公开新闻,其余数据若使用真实姓名容易造成数据不准确或者误导。在真实业务中,你应当使用授权或公开可查的数据,并记录数据来源。

3. 成绩数据的核心计算方法

3.1 平均速度计算

平均速度是短跑成绩分析中最基础的指标。公式非常简单:

速度 = 距离 / 时间

在100米项目中,距离固定为100米,时间就是成绩。

用Python实现:

def avg_speed(distance_m, time_s): """ 计算平均速度,单位:米/秒 """ return distance_m / time_s

调用方式:

speed = avg_speed(100, 11.08) print(f"平均速度:{speed:.2f} 米/秒")

输出:

平均速度:9.03 米/秒

这里有个细节需要注意:11.08在Python中会被当作浮点数处理,不会出现整数除法导致结果为0的问题。但如果你从文本文件或Excel中读取时间字段,要确保列类型是数值型,否则后续计算会报错。

3.2 单位换算与配速

很多跑者习惯用“每公里配速”来衡量速度,这就需要对单位做换算。

米/秒转换为公里/小时,乘以3.6:

def speed_to_kmh(speed_ms): return speed_ms * 3.6

计算每公里配速(秒/公里):

def speed_to_pace(speed_ms): return 1000 / speed_ms

把秒数格式化成“分:秒”展示:

def format_pace(pace_sec): minutes = int(pace_sec // 60) seconds = int(pace_sec % 60) return f"{minutes}分{seconds}秒"

以11.08秒为例:

speed = 100 / 11.08 # 约9.03 kmh = speed * 3.6 # 约32.49 pace = 1000 / speed # 约110.8 print(f"速度:{kmh:.2f} km/h") print(f"配速:{format_pace(pace)}")

输出:

速度:32.49 km/h 配速:1分51秒

配速的单位是“秒/公里”,数值越小速度越快。

3.3 分段速度分析思路

如果想分析运动员是起跑快还是冲刺快,单靠总成绩是不够的,需要分段计时数据。比如每10米一个计时点,然后计算各段的平均速度。

在只有总成绩的情况下,可以做一个理论上的匀速分段模型:

def calc_uniform_split(total_time, total_distance=100, split_distance=10): """ 按匀速模型计算每段用时,单位:秒 """ segment_count = total_distance // split_distance segment_time = total_time / segment_count return [segment_time] * segment_count

调用:

splits = calc_uniform_split(11.08) print(splits)

输出:

[1.108, 1.108, 1.108, 1.108, 1.108, 1.108, 1.108, 1.108, 1.108, 1.108]

这个结果只是基于匀速假设的理论值。实际100米比赛的速度曲线通常是“起跑慢、途中快、冲刺略有下降”,所以分段分析必须依赖现场计时数据,不能直接用总成绩平均。

在项目落地时,建议先明确分析目标:如果你只有总成绩,就只做整体指标计算;如果你有分段数据,再考虑分段速度分析。不要用理论模型过度推测真实表现。

3.4 常见误区

先总结几个容易踩的坑,后文会在实战中进一步验证:

  • 忘记处理空值和文本格式:CSV中成绩列可能混入“11’08”之类的文本,需要清洗。
  • 直接使用原始列表计算:数据量大时性能差,也不便于筛选和分组计算。
  • 图表中文乱码:matplotlib默认字体不支持中文,需要配置中文字体。
  • 混淆“风”速字段:顺风可能提高成绩,需要在盘点数据源时注意是否已包含风速修正。

4. 完整实战案例:短跑成绩数据分析小工具

下面进入本文的核心部分。我们将从零搭建一个短跑成绩分析小工具,完成数据读取、指标计算、可视化对比的完整流程。

4.1 创建项目结构

先创建如下目录结构:

sprint-analysis/ ├── data/ │ └── sprint_data.csv ├── output/ └── sprint_analysis.py

data目录存放原始数据,output目录存放生成的图表或报告,sprint_analysis.py是主脚本。

在data/sprint_data.csv中写入上文的示例数据:

name,event,time_s,wind 陈妤颉,全国田径锦标赛,11.08,0.0 选手A,模拟赛,11.25,0.4 选手B,模拟赛,11.42,-0.2 选手C,模拟赛,11.50,0.3

注意CSV的编码建议使用 UTF-8 或 UTF-8-SIG,避免中文乱码。Windows 环境下使用 Excel 打开时,UTF-8-SIG 兼容性更好。

4.2 编写数据读取与检查代码

在sprint_analysis.py中写入以下代码:

# -*- coding: utf-8 -*- """ 短跑成绩数据分析小工具 """ import os import pandas as pd def load_data(csv_path): """ 读取CSV文件,返回DataFrame """ df = pd.read_csv(csv_path, encoding='utf-8-sig') return df def main(): base_dir = os.path.dirname(__file__) csv_path = os.path.join(base_dir, 'data', 'sprint_data.csv') df = load_data(csv_path) print('原始数据:') print(df) print(f'\n数据形状:{df.shape}') print(f'成绩列类型:{df["time_s"].dtype}') if __name__ == '__main__': main()

这段代码先定位CSV文件路径,然后读取数据,并打印基本信息和时间列类型。df.shape显示数据是几行几列,dtype用来确认成绩列是否为数值类型。

运行方式:

python sprint_analysis.py

预期输出:

原始数据: name event time_s wind 0 陈妤颉 全国田径锦标赛 11.08 0.0 1 选手A 模拟赛 11.25 0.4 2 选手B 模拟赛 11.42 -0.2 3 选手C 模拟赛 11.50 0.3 数据形状:(4, 4) 成绩列类型:float64

如果这一步正常,说明数据读取成功。

4.3 编写速度与配速计算函数

接下来增加计算逻辑。这里把每个功能封装成独立函数,方便复用和扩展。

def avg_speed(distance_m, time_s): """ 计算平均速度,单位:米/秒 """ return distance_m / time_s def speed_to_kmh(speed_ms): """ 米/秒 转 公里/小时 """ return speed_ms * 3.6 def speed_to_pace(speed_ms): """ 根据米/秒转换为每公里配速,单位:秒/公里 """ return 1000 / speed_ms def format_pace(pace_sec): """ 将配速格式化为 分:秒 """ minutes = int(pace_sec // 60) seconds = int(pace_sec % 60) return f"{minutes}分{seconds}秒" def add_metrics(df, distance_m=100): """ 在原DataFrame上新增速度、配速等指标列 """ df = df.copy() df['speed_ms'] = df['time_s'].apply(lambda t: avg_speed(distance_m, t)) df['speed_kmh'] = df['speed_ms'].apply(speed_to_kmh) df['pace_sec_per_km'] = df['speed_ms'].apply(speed_to_pace) df['pace_str'] = df['pace_sec_per_km'].apply(format_pace) return df

这段代码新增了四列:

  • speed_ms:平均速度,单位米/秒。
  • speed_kmh:平均速度,单位公里/小时。
  • pace_sec_per_km:每公里配速,单位秒/公里。
  • pace_str:配速的友好展示。

使用df.copy()是为了避免修改原始DataFrame,这是一种好的编码习惯。

4.4 多成绩对比与可视化

为了直观对比陈妤颉与其他模拟选手的成绩差异,使用matplotlib绘制柱状图。

完整代码:

import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC'] plt.rcParams['axes.unicode_minus'] = False def plot_speed(df, save_path=None): """ 绘制平均速度对比柱状图 """ fig, ax = plt.subplots(figsize=(8, 5)) bars = ax.bar(df['name'], df['speed_kmh'], color='#4C72B0') for bar, v in zip(bars, df['speed_kmh']): ax.text( bar.get_x() + bar.get_width() / 2, v + 0.15, f"{v:.2f}", ha='center', va='bottom' ) ax.set_ylabel('平均速度(km/h)') ax.set_title('女子100米短跑平均速度对比') ax.grid(axis='y', alpha=0.3) if save_path: plt.savefig(save_path, dpi=120, bbox_inches='tight') plt.show()

这里有一个细节:执行plt.show()前先调用plt.savefig()保存图片。如果保存路径的目录不存在,需要先创建目录。

4.5 完整代码与运行结果

把上面所有函数汇总到一起,并增加输出表格的功能。

# -*- coding: utf-8 -*- """ 短跑成绩数据分析小工具 功能:读取短跑成绩数据,计算平均速度、配速,并生成对比图 """ import os import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC'] plt.rcParams['axes.unicode_minus'] = False def load_data(csv_path): df = pd.read_csv(csv_path, encoding='utf-8-sig') return df def avg_speed(distance_m, time_s): return distance_m / time_s def speed_to_kmh(speed_ms): return speed_ms * 3.6 def speed_to_pace(speed_ms): return 1000 / speed_ms def format_pace(pace_sec): minutes = int(pace_sec // 60) seconds = int(pace_sec % 60) return f"{minutes}分{seconds}秒" def add_metrics(df, distance_m=100): df = df.copy() df['speed_ms'] = df['time_s'].apply(lambda t: avg_speed(distance_m, t)) df['speed_kmh'] = df['speed_ms'].apply(speed_to_kmh) df['pace_sec_per_km'] = df['speed_ms'].apply(speed_to_pace) df['pace_str'] = df['pace_sec_per_km'].apply(format_pace) return df def plot_speed(df, save_path=None): fig, ax = plt.subplots(figsize=(8, 5)) bars = ax.bar(df['name'], df['speed_kmh'], color='#4C72B0') for bar, v in zip(bars, df['speed_kmh']): ax.text( bar.get_x() + bar.get_width() / 2, v + 0.15, f"{v:.2f}", ha='center', va='bottom' ) ax.set_ylabel('平均速度(km/h)') ax.set_title('女子100米短跑平均速度对比') ax.grid(axis='y', alpha=0.3) if save_path: plt.savefig(save_path, dpi=120, bbox_inches='tight') plt.show() def main(): base_dir = os.path.dirname(__file__) csv_path = os.path.join(base_dir, 'data', 'sprint_data.csv') output_dir = os.path.join(base_dir, 'output') os.makedirs(output_dir, exist_ok=True) df = load_data(csv_path) print('原始数据:') print(df) df = add_metrics(df, distance_m=100) print('\n计算后的数据:') print(df[['name', 'time_s', 'speed_ms', 'speed_kmh', 'pace_str']]) chart_path = os.path.join(output_dir, 'speed_compare.png') plot_speed(df, save_path=chart_path) print(f'\n图表已保存到:{chart_path}') if __name__ == '__main__': main()

运行:

python sprint_analysis.py

预期控制台输出:

原始数据: name event time_s wind 0 陈妤颉 全国田径锦标赛 11.08 0.0 1 选手A 模拟赛 11.25 0.4 2 选手B 模拟赛 11.42 -0.2 3 选手C 模拟赛 11.50 0.3 计算后的数据: name time_s speed_ms speed_kmh pace_str 0 陈妤颉 11.08 9.025271 32.490974 1分51秒 1 选手A 11.25 8.888889 32.000000 1分53秒 2 选手B 11.42 8.756568 31.523645 1分54秒 3 选手C 11.50 8.695652 31.304348 1分55秒 图表已保存到:.../output/speed_compare.png

这样,完整的分析流程就打通了。从数据读取到指标计算再到可视化输出,全程只需要一个Python脚本。

5. 常见问题与排查思路

5.1 常见问题速查表

问题现象常见原因解决思路
中文文字读取后出现乱码CSV编码不是UTF-8使用encoding='utf-8-sig'读取,或统一保存为UTF-8
matplotlib输出图表中文乱码系统缺少中文字体或未配置字体设置plt.rcParams['font.sans-serif']
读取CSV报FileNotFoundError路径拼接错误或文件不存在检查目录结构,优先使用os.path.join构建路径
计算时报错:字符串不能减字符串时间列是文本类型使用pd.to_numeric()或清理特殊字符
图表保存后图片空白show()执行后图形被重置先savefig再show

5.2 详细排查:时间列类型问题

假设你从某系统导出的CSV中,成绩列的值长这样:11'08,也就是用了单引号代替小数点。此时pandas读取后该列可能是字符串类型,计算会直接报错。

推荐的处理方式:

df['time_s'] = df['time_s'].astype(str).str.replace("'", ".").astype(float)

这里先强制转成字符串,再替换单引号为小数点,最后转成浮点数。实际清洗时,建议先打印df.dtypes确认各列类型。

5.3 详细排查:CSV路径找不到

路径报错很多时候是“当前工作目录”和“脚本所在目录”不一致导致的。最稳妥的方法是用os.path.dirname(__file__)获取脚本所在目录,再拼接相对路径。

base_dir = os.path.dirname(__file__) csv_path = os.path.join(base_dir, 'data', 'sprint_data.csv')

这样不管你在哪个目录下运行脚本,都能正确定位文件。

5.4 详细排查:matplotlib中文乱码

Windows系统一般能用SimHei,macOS 可以用PingFang SC,Linux 建议安装WenQuanYi字体。如果所有字体都无效,可以先用字体管理器查看系统已安装字体:

import matplotlib.font_manager as fm fonts = [f.name for f in fm.fontManager.ttflist] print(fonts)

然后从列表里选择一个合适的中文字体,再设置。

6. 最佳实践与工程建议

6.1 数据文件规范

成绩数据的CSV文件建议遵循以下规范:

  • 文件名用英文小写加下划线,例如sprint_data.csv,避免中文文件名在部分环境下出现编码问题。
  • 表头统一使用英文小写,字段名见名知意。
  • 每行代表一条独立记录,不要在一个单元格里写多个成绩。
  • 原始数据单独保存,不要在清洗过程中覆盖原文件。

比如可以增加一列source记录数据来源,方便追溯:

name,event,time_s,wind,source 陈妤颉,全国田径锦标赛,11.08,0.0,公开新闻数据

这在真实项目中几乎是最重要的习惯:数据来源可追溯,分析结果才能被信任。

6.2 代码封装与复用

不要把所有逻辑都堆在main()函数里。建议每个功能拆成独立函数,比如load_data、add_metrics、plot_speed。这样如果后续要增加“按赛事筛选”或“成绩预测”功能,只需要新增对应函数,不影响已有代码。

另外,距离distance_m这种参数可以设计成函数参数,默认值是100。如果将来自动化分析200米成绩,只需要调用时传入distance_m=200。

6.3 图表输出与报告生成

图表保存时,建议指定dpi和bbox_inches='tight':

plt.savefig(chart_path, dpi=120, bbox_inches='tight')

dpi=120能保证图片清晰,bbox_inches='tight'可以避免坐标轴标签被截断。

如果分析结果要发给教练组,可以把整理后的DataFrame导出为CSV或Excel:

df.to_csv('analysis_result.csv', index=False, encoding='utf-8-sig')

需要输出Excel时,可以安装openpyxl:

pip install openpyxl

然后:

df.to_excel('analysis_result.xlsx', index=False)

6.4 数据准确性与边界处理

短跑成绩分析虽然公式简单,但在真实场景中要注意几点:

  • 风速字段不能忽略,国际田联对顺风风速超过2米/秒的成绩有特殊规定。
  • 时间精度通常保留两位小数,计算中间量不要提前四舍五入,最后展示再格式化。
  • 如果成绩列存在空值,要先决定是删除还是填充,不要带着NaN直接计算。
  • 如果数据来自多人录入,要统一单位格式,避免出现11.08和11.080混用的问题。

7. 后续可以继续学习的方向

文章篇幅有限,但顺着这个案例,你可以继续深入的方向其实很多。

第一个方向是“分段速度分析”。如果有一天你拿到了每10米的分段时间,就可以绘制速度曲线,分析运动员是否在某个阶段掉速,这对训练调整很有价值。

第二个方向是“成绩预测”。你可以收集同一运动员多次比赛成绩,使用线性回归或时间序列模型,预测下一场比赛的目标区间。当然,体育比赛受状态、天气、赛道等因素影响很大,预测结果只能作为参考。

第三个方向是“自动化报告”。把脚本接入定时任务,每次比赛结束后自动读取成绩、生成图表、发送邮件或钉钉消息,就是一套轻量级的运动数据工作流。

数据分析的真正价值,不在于把一个数字换成另一个数字,而在于把零散的记录转化为可理解、可对比、可决策的信息。如果你也有自己关心的运动成绩数据,建议从今天开始建一个简单的CSV文件,用这套流程跑一遍。

遇到问题不要急,按照“先检查数据,再检查代码,最后检查环境”的顺序排查,大部分情况都能定位到原因。希望这篇文章能帮你少走一点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询