Python实战:用11秒08的短跑成绩带你学会数据分析与可视化

发布时间:2026/9/4 9:04:01
Python实战:用11秒08的短跑成绩带你学会数据分析与可视化 最近看到全国田径锦标赛女子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用于绘制对比图表。osPython标准库处理文件路径。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。建议使用 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.08wind风速米/秒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.6def 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_distance100, split_distance10): 按匀速模型计算每段用时单位秒 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.pydata目录存放原始数据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, encodingutf-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_m100): 在原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_pathNone): 绘制平均速度对比柱状图 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}, hacenter, vabottom ) ax.set_ylabel(平均速度km/h) ax.set_title(女子100米短跑平均速度对比) ax.grid(axisy, alpha0.3) if save_path: plt.savefig(save_path, dpi120, bbox_inchestight) 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, encodingutf-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_m100): 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_pathNone): 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}, hacenter, vabottom ) ax.set_ylabel(平均速度km/h) ax.set_title(女子100米短跑平均速度对比) ax.grid(axisy, alpha0.3) if save_path: plt.savefig(save_path, dpi120, bbox_inchestight) 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_okTrue) df load_data(csv_path) print(原始数据) print(df) df add_metrics(df, distance_m100) 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_pathchart_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使用encodingutf-8-sig读取或统一保存为UTF-8matplotlib输出图表中文乱码系统缺少中文字体或未配置字体设置plt.rcParams[font.sans-serif]读取CSV报FileNotFoundError路径拼接错误或文件不存在检查目录结构优先使用os.path.join构建路径计算时报错字符串不能减字符串时间列是文本类型使用pd.to_numeric()或清理特殊字符图表保存后图片空白show()执行后图形被重置先savefig再show5.2 详细排查时间列类型问题假设你从某系统导出的CSV中成绩列的值长这样1108也就是用了单引号代替小数点。此时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系统一般能用SimHeimacOS 可以用PingFang SCLinux 建议安装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_m200。6.3 图表输出与报告生成图表保存时建议指定dpi和bbox_inchestightplt.savefig(chart_path, dpi120, bbox_inchestight)dpi120能保证图片清晰bbox_inchestight可以避免坐标轴标签被截断。如果分析结果要发给教练组可以把整理后的DataFrame导出为CSV或Exceldf.to_csv(analysis_result.csv, indexFalse, encodingutf-8-sig)需要输出Excel时可以安装openpyxlpip install openpyxl然后df.to_excel(analysis_result.xlsx, indexFalse)6.4 数据准确性与边界处理短跑成绩分析虽然公式简单但在真实场景中要注意几点风速字段不能忽略国际田联对顺风风速超过2米/秒的成绩有特殊规定。时间精度通常保留两位小数计算中间量不要提前四舍五入最后展示再格式化。如果成绩列存在空值要先决定是删除还是填充不要带着NaN直接计算。如果数据来自多人录入要统一单位格式避免出现11.08和11.080混用的问题。7. 后续可以继续学习的方向文章篇幅有限但顺着这个案例你可以继续深入的方向其实很多。第一个方向是“分段速度分析”。如果有一天你拿到了每10米的分段时间就可以绘制速度曲线分析运动员是否在某个阶段掉速这对训练调整很有价值。第二个方向是“成绩预测”。你可以收集同一运动员多次比赛成绩使用线性回归或时间序列模型预测下一场比赛的目标区间。当然体育比赛受状态、天气、赛道等因素影响很大预测结果只能作为参考。第三个方向是“自动化报告”。把脚本接入定时任务每次比赛结束后自动读取成绩、生成图表、发送邮件或钉钉消息就是一套轻量级的运动数据工作流。数据分析的真正价值不在于把一个数字换成另一个数字而在于把零散的记录转化为可理解、可对比、可决策的信息。如果你也有自己关心的运动成绩数据建议从今天开始建一个简单的CSV文件用这套流程跑一遍。遇到问题不要急按照“先检查数据再检查代码最后检查环境”的顺序排查大部分情况都能定位到原因。希望这篇文章能帮你少走一点弯路。