当前位置: 首页 > news >正文

为什么pandas读Excel日期列全是浮点数字?

上个月接了个活,要把客户发来的几十个Excel报表汇总到一个数据库里。数据量不大,逻辑也简单,我心想pandas几行代码就搞定。

结果read_excel一跑出来,日期列全是44562.0、44601.5这种数字。Excel里明明显示的是"2025-03-15",怎么到pandas嘴里就变味了?

这个问题我前前后后折腾了两天,试了好几种方法,最后才理清楚到底怎么回事。写下来给同样被这坑绊过的朋友参考。

为什么日期变成了浮点数

这不是pandas的bug,是Excel自己存日期的方式。

Excel内部用浮点数表示日期和时间。整数部分是从1900年1月1日起的天数(有个著名的1900年闰年bug,2月29日其实是虚拟的),小数部分是当天的时间比例。所以44562就代表从1900年1月1日往后数44562天,对应2025年3月15日。

你在Excel里看到的"2025-03-15"只是单元格格式化后的显示效果,底层存的还是那个浮点数。

pandas默认读Excel的时候,如果单元格格式信息没有被正确传递,它就只拿到了底层的浮点数。尤其是用xlrd引擎读xls文件,或者Excel里日期列的格式被手动改过,这种情况特别容易出现。

方法一:read_excel的parse_dates参数

最直接的办法是在read_excel的时候告诉pandas哪些列是日期:

importpandasaspd df=pd.read_excel('sales_report.xlsx',parse_dates=['order_date','delivery_date'])

如果列名不确定,也可以用列索引:

df=pd.read_excel('sales_report.xlsx',parse_dates=[2,5]# 第3列和第6列是日期)

这个方法大部分时候管用。但有个前提:Excel文件里这些列的格式必须是"日期"类型。如果有人把格式改成了"常规"或者"文本",parse_dates就不起作用了,读出来照样是浮点数。

我碰到的第一个坑就是这个,客户的报表里,有人在日期列上点了"清除格式",结果日期格式信息全丢了。

方法二:浮点数手动转日期

当parse_dates不生效,或者数据已经读出来了,可以手动把浮点数转成日期。

Excel的浮点数日期有个基准:从1899年12月30日开始算(那个1900闰年bug导致偏移了一天)。转换公式:

fromdatetimeimportdatetime,timedeltadefexcel_float_to_date(f):# Excel基准日期是1899-12-30(因为1900闰年bug)base=datetime(1899,12,30)returnbase+timedelta(days=f)# 测试print(excel_float_to_date(44562))# 2025-03-15 00:00:00print(excel_float_to_date(44601.5))# 2025-03-15 12:00:00 + 39.5天

批量转换整列:

df['order_date']=df['order_date'].apply(lambdax:datetime(1899,12,30)+timedelta(days=x)ifpd.notna(x)andisinstance(x,(int,float))elsex)

这里有两个容易忽略的点:

第一,列里可能混着浮点数和正常字符串。有的人在Excel里既敲了"2025-03-15"这样的文本,又敲了格式化的日期,读出来就一半是float一半是str。apply里要加类型判断,别把字符串也当浮点数处理了。

第二,空单元格读出来是NaN,NaN是float类型,isinstance(x, float)会匹配到它。所以必须先判断pd.notna(x)。

方法三:换引擎读Excel

pandas读Excel支持两种引擎:xlrd(只支持xls)和openpyxl(支持xlsx)。不同引擎对日期格式的识别能力不一样。

openpyxl引擎在读取xlsx时,会尝试根据单元格的格式信息来判断类型。如果你的文件是xlsx格式,指定openpyxl引擎往往能更好地保留日期类型:

df=pd.read_excel('report.xlsx',engine='openpyxl')

但openpyxl也有问题,它读大文件比较慢,而且如果单元格格式是"自定义"(比如"yyyy年mm月dd日"这种中文格式),有时候也识别不出来。

xlrd引擎在老版本里读xlsx是默认行为,但新版xlrd(2.0以后)只支持xls了。所以如果你用xlrd读xlsx会直接报错,得用openpyxl。

对于xls文件,xlrd是唯一选择,而且它确实更容易出现日期变浮点数的问题。一个解决办法是用xlrd单独打开文件,先检测哪些列是日期类型:

importxlrd book=xlrd.open_workbook('old_report.xls')sheet=book.sheet_by_index(0)# 找出日期列date_cols=[]forcol_idxinrange(sheet.ncols):# 检查前几行的单元格类型forrow_idxinrange(min(5,sheet.nrows)):cell=sheet.cell(row_idx,col_idx)ifcell.ctype==xlrd.XL_CELL_DATE:date_cols.append(col_idx)breakprint(f"日期列索引:{date_cols}")# 然后用pandas读取,指定这些列为日期df=pd.read_excel('old_report.xls',parse_dates=date_cols)

xlrd.XL_CELL_DATE的值是3,代表单元格类型是日期。这个检测方法虽然多写几行,但比瞎猜parse_dates靠谱得多。

几个我踩过的边角坑

同一列里日期格式不一致。比如前100行是日期格式,后面有人手敲了"3月15日"这种文本。pandas读出来列类型就变成mixed,parse_dates直接失效。解决办法是先在Excel里统一格式,或者读出来后分两段处理。

时区问题。Excel日期没有时区概念,转成pandas的datetime后也是naive的。如果你要做跨时区对比,记得手动加时区:

df['order_date']=pd.to_datetime(df['order_date']).dt.tz_localize('Asia/Shanghai')

时间精度丢失。44601.5这种小数代表某个时刻,但如果你只取日期部分(用.dt.date),时间信息就丢了。如果业务需要精确到小时,转的时候注意保留小数部分。

pandas的to_datetime也能处理浮点数。如果你不想写手动转换函数,其实pd.to_datetime对某些浮点数也能处理,但它默认把浮点数当Unix时间戳来解析(从1970年开始算),跟Excel的基准(1899年)完全不同。所以千万别直接对Excel浮点数用to_datetime,出来的日期会差一百多年。

# 错误示范,这会把44562解释成1970年以后的44562秒pd.to_datetime(44562,unit='s')# 1970-01-01 + 44562秒 ≈ 1970-01-02# 正确做法:先转成Excel基准的datetime,再让pandas识别df['date']=df['date'].apply(excel_float_to_date)

我的最终方案

折腾一圈后,我最后稳定用的流程:

  1. 先用openpyxl引擎读取xlsx文件,配合parse_dates参数
  2. 读完后检查日期列的类型,如果还是float,就用excel_float_to_date手动转换
  3. 对于xls文件,先用xlrd检测日期列索引,再传给parse_dates
  4. 最后统一用pd.to_datetime做一轮格式校验,确保没有遗漏的str类型日期
importpandasaspdfromdatetimeimportdatetime,timedeltadefexcel_float_to_date(f):base=datetime(1899,12,30)returnbase+timedelta(days=float(f))defclean_date_column(col):"""统一处理日期列中的混合类型"""result=[]forvalincol:ifpd.isna(val):result.append(pd.NaT)elifisinstance(val,(int,float)):result.append(excel_float_to_date(val))else:result.append(pd.to_datetime(val,errors='coerce'))returnpd.Series(result)# 主流程df=pd.read_excel('sales_report.xlsx',engine='openpyxl',parse_dates=['order_date'])# 检查:如果日期列没被正确解析ifdf['order_date'].dtype!='datetime64[ns]':df['order_date']=clean_date_column(df['order_date'])

这套流程跑下来,几十个报表没再出日期问题。虽然多写了些代码,但省了后续反复修数据的时间。

碰到日期变浮点数怎么办

说到底,Excel把格式和值分开存,内部没问题,跨工具读就麻烦了。pandas只能尽力去识别格式,识别不了就给你原始值。

下次碰到日期列全是浮点数,大概率不是代码写错了,是Excel格式信息丢了。parse_dates不行就手动转,手动转注意NaN和混合类型,基本都能搞定。

如果你自己生成Excel,一开始就把日期列设成明确的日期格式,别用"常规"或"文本"。这样后面不管谁读,都不会再踩这个坑。

http://www.jsqmd.com/news/1078380/

相关文章:

  • 2轴舵机控制板
  • LLM Evaluation 论文盘点:从静态榜单到动态、抗污染、任务化评测
  • Linux命令:zsh
  • Roblox帧率解锁终极指南:如何免费突破60FPS限制获得流畅游戏体验
  • MonetaMarkets的账户协同感够不够清楚?
  • 后端工程师转型AI第一课--Ollama与私有化大模型实战
  • 从手动配置到预设即代码
  • 激动的心颤抖的手 真的领到了8元
  • T140 风扇噪音大 竟然电池原因
  • 第5篇:《DC-DC电感啸叫排查:饱和电流选小,满载电流波形畸变》
  • 1.全面理解Mysql架构
  • go: Push Pull Pattern
  • 从任务积压到文件队列:Prometheus业务指标监控与告警指南
  • 2026企业协作网盘推荐:5款企业文档协作平台对比与选型指南
  • 神经算子与GRU-STONe在航空辐射监测中的应用
  • DCU深度技术报告_下篇_性能复盘与研发经验总结
  • PDFSlideshow使用教程,PDF转幻灯片演示工具绿色版下载
  • llamafactory gradient_checkpointing 梯度检查点 通俗完整讲解
  • STM32WB55入门教程(二)
  • 简道云智能助手实测:工单派发→报工→质检→入库,全自动流转到底靠不靠谱?
  • 状态空间模型安全风险剖析:频谱攻击、后门植入与状态饱和的攻防实践
  • NannyML无标签模型监控:实现端到端MLOps性能闭环
  • Docker网络这5种模式,你真的都搞明白了吗?
  • 从CTF EasySQL题解析SQL注入攻防:核心原理与实战绕过技巧
  • 5分钟打造万能启动盘:Ventoy彻底告别重复格式化时代
  • HDFS javaAPI-windows的IDEA中java文件在linux中的hadoop平台运行
  • P89LPC92x1中断与I/O配置实战:从原理到避坑指南
  • 脉冲神经网络多级脉冲设计与能效优化
  • HTTPS 性能优化完全指南:从原理、硬件到架构的全链路调优实战
  • 手动构造链表和二叉树