pandas to_datetime解析%M:%S.%f时间报错及seaborn绘图异常问题
问题根因
- 触发
ValueError的直接原因:你的时间字符串存在隐藏前导空格(报错信息里的待匹配值为' 7:42.3',数字7前多了1个空格),和你写的%M:%S.%f格式规则不匹配;另外pd.to_datetime本身是用来解析具体日期时间点的,会默认补全年月日信息,并不适配你这种只有分、秒、毫秒的完赛时长类数据。 - 触发seaborn绘图
TypeError的原因:你的时间列始终是object(字符串/混合类型),没有转成seaborn可识别的数值类型,当你把分类类型的运动员姓名放到坐标轴上时,另一轴的非数值数据无法被识别计算,就会抛出变量非数值的错误。
解决步骤
你有多列同格式的时间数据,可以用统一函数批量处理,全程不用逐列手写转换逻辑:
- 先写通用转换逻辑,把分秒毫秒格式的时长转成总秒数(纯浮点数值,兼容所有排序、计算、绘图场景,对新手最友好,不会出类型兼容问题)
import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter # 把所有同格式的时间列名放到这个列表里,多列直接逗号分隔追加即可 time_columns = ["CRS1"] # 时长转换函数:自动处理前后空格、空值,输出数值类型的总秒数 def time_str_to_seconds(time_val): # 空值直接返回保留 if pd.isna(time_val): return pd.NA # 去掉字符串前后所有隐藏空格,解决前导空格导致的格式匹配失败问题 clean_str = str(time_val).strip() # 拆分分、秒、毫秒部分 minute_part, sec_ms_part = clean_str.split(":") sec_part, ms_part = sec_ms_part.split(".") # 累加计算总秒数:你的毫秒是1位,对应0.1秒精度 total_seconds = int(minute_part) * 60 + int(sec_part) + int(ms_part)/10 return total_seconds # 批量转换所有时间列 for col in time_columns: df[col] = df[col].apply(time_str_to_seconds)
转换完成后你可以执行print(df["CRS1"].dtype)验证,列类型会变成float,空值保留为NaN,前几行值会是462.3、471.9这类数值,完全符合数值类绘图要求
- 直接绘图即可,不会再触发类型错误,示例代码:
plt.figure(figsize=(10, 6)) # 替换成你自己表中运动员姓名对应的列名即可 sns.barplot(data=df, x="CRS1", y="athlete_name") plt.xlabel("完赛时间") plt.ylabel("运动员姓名") plt.show()
- (可选)如果想让坐标轴显示回
分:秒.毫秒的易读格式,不需要修改原始数据,只要加个刻度格式化函数就行:
# 把数值秒数转回分秒格式用于坐标轴显示 def sec_to_time_str(x, pos): mins = int(x // 60) secs = x % 60 return f"{mins}:{secs:.1f}" # 给x轴应用格式化规则 plt.gca().xaxis.set_major_formatter(FuncFormatter(sec_to_time_str))
不要把时间数据再转回字符串类型做坐标轴,否则会再次触发seaborn的非数值类型报错,格式化显示只需要修改刻度渲染规则即可,原始数据保持数值类型是最稳妥的方案。
内容的提问来源于stack exchange,提问作者Hansson
相关产品推荐
相关产品推荐

