pd. read_csv解析日期格式字符串parse_dates参数,以及pd.to_datetime()、dateutil.parser.parse()与datetime.strptime()比较


在对San Fransisco的Police Department Incident Reports 历史数据整理时发现,2018年5月之前的数据中将日期和时间分开为了Date和Time两个列,而2018年之后的数据则只有Datetime一列,需要进行格式统一。


源数据文件中Date日期与Time时间分为了两列

read_csv命令中的parse_dates参数详解

由于csv文件中日期和时间被分为了两列,pd.read_csv命令读取文件时,需指定parse_dates = [ ['Date', 'Time'] ],亦即将[ ['Date', 'Time'] ]两列的字符串先合并后解析方可。合并后的新列会以下划线'_'连接原列名命名,本例中列名为'Date_Time'。解析得到的日期格式列会作为DataFrame的第一列,在index_col指定表格中的第几列作为Index时需要小心。如本例中,指定参数index_col = 0,则此时会以新生曾的Date_Time列而不是IncidntNum作为Index。因此保险的方法是指定列名,如index_col = 'IncidntNum'。

read_csv指定parse_dates = [ ['Date', 'Time'] ]运行结果

如果写成了parse_dates = ['Date', 'Time'] ,pd. read_csv()会分别对'Date', 'Time'进行字符串转日期,此外还会造成一个小小的麻烦。由于本例中的Time时间列格式为‘HH:MM’,parse_dates 默认调用dateutil.parser.parse解析为Datetime时,在解析Time这一列时,会自作主张在前面加上一个当前日期。

read_csv指定parse_dates = ['Date', 'Time']运行结果

而且,read_csv指定parse_dates会使得读取csv文件的时间大大增加。该文件共220.5万条数据,不进行日期时间的解析时仅需不到10秒时间读取并转为DataFrame,读取同时解析日期竟则需要接近10分钟。仔细查阅了read_csv文档,发现当指定infer_datetime_format = True时, pandas会推荐日期字符串的格式从而使得解析速度加速5-10倍(原文如下)。

infer_datetime_format : bool, default False
If True and parse_dates is enabled, pandas will attempt to infer the format of the datetime strings in the columns, and if it can be inferred, switch to a faster method of parsing them. In some cases this can increase the parsing speed by 5-10x.

实际运行下来加速效果明显,由原来的超过9分钟减少到了38.4秒。
此外,keep_date_col 参数则是用了指定解析为日期格式的列是否保留。文档中提到dayfirst 用来区分类似‘10/11/12’的字符串表示的到底是'dd/mm/yy'还是'mm/dd/yy',美国习惯'MM/DD/YYYY',但国际通用更常用的是'DD/MM/YYYY'。如果涉及到时区等,还需指定date_parser为'pandas.to_datetime() 并指定'utc=True',具体参见Parsing a CSV with mixed timezones,此处不赘述。

infer_datetime_format = True可显著减少read_csv命令日期解析时间

pd.to_datetime()、dateutil.parser.parse()与datetime.strptime()比较

datetime.strptime()
datetime是Python处理日期和时间的标准库。datetime.strptime()是由字符串格式转化为日期格式的函数。

datetime.strptime()

函数执行需指定字符串的日期表示的格式,如下图所示。如果格式不匹配,如按照‘%d %m %y’格式解析‘21/04/2018’会报错(ValueError: time data '21/04/2018' does not match format '%d %m %Y')

Directive Description Example Output
%a Weekday as locale’s abbreviated name. Sun, Mon, …, Sat
%A Weekday as locale’s full name. Sunday, Monday, …, Saturday
%w Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. 0, 1, 2, 3, 4, 5, 6
%d Day of the month as a zero-padded decimal number. 01, 02, …, 31
%b Month as locale’s abbreviated name. Jan, Feb, …, Dec
%B Month as locale’s full name. January, February, …, December
%m Month as a zero-padded decimal number. 01, 02 … 12
%y Year without century as a zero-padded decimal number. 01, 02, … 99
%Y Year with century as a decimal number. 0001, 0002, … , 9999
%H Hour (24-hour clock) as a zero-padded decimal number. 01, 02, … , 23
%I Hour (12-hour clock) as a zero-padded decimal number. 01, 02, … , 12
%p Locale’s equivalent of either AM or PM. AM, PM
%M Minute as a zero-padded decimal number. 01, 02, … , 59
%S Second as a zero-padded decimal number. 01, 02, … , 59
%f Microsecond as a decimal number, zero-padded on the left. 000000, 000001, …, 999999
%z UTC offset in the form ±HHMM[SS] (empty string if the object is naive). (empty), +0000, -0400, +1030
%Z Time zone name (empty string if the object is naive). (empty), UTC, IST, CST
%j Day of the year as a zero-padded decimal number. 001, 002, …, 366
%U Week number of the year (Sunday as the first day of the week) as a zero padded decimal number.All days in a new year preceding the first Sunday are considered to be in week 0. 00, 01, …, 53
%W Week number of the year (Monday as the first day of the week) as a decimal number.All days in a new year preceding the first Monday are considered to be in week 0. 00, 01, …, 53
%c Locale’s appropriate date and time representation. Tue Aug 16 21:30:00 1988
%x Locale’s appropriate date representation. 08/16/1988
%X Locale’s appropriate time representation. 21:30:00
%% A literal ‘%’ character. %

datetime.strptime()要求严格且格式繁多,日期时间格式中没有指定的的部分会自动用‘1月1日 零时零分’补齐。发现个很好玩的。用两位数的年份数'%y'执行下来,当数字大于68后,就会被程序认为是‘19xx’年。但为什么是68作为分水岭呢?
dateutil.parser.parse()
parse来自第三方包dateutil,也是read_csv默认的日期解析程序。Wes McKinney在《Python for Data Analysis, 2nd Edition》说parse可以解析几乎所有人类可以理解的日期表示形式。parse灵活性强,不用专门去指定字符串的格式,甚至可以从类似'Today is January 1, 2047 at 8:21:00AM'的这样一段文本中将日期和时间解析出来。对仅有2位或4位的年份时,parse()自动添加的是当前日期,与datetime.strptime()不同,此外parse('00')会报错。

dateutil.parser.parse()运行结果

pd.to_datetime()
pd.to_datetime()也可以自动解析多种格式的日期,但比parse()谨慎,或者可以说,pd.to_datetime()的灵活性介于dateutil.parser.parse()与datetime.strptime()之间。除常规的'dayfirst'、'yearfirst'来区分类似'10/11/12'这类字符串中'年月日'的排列顺序之外,pd.to_datetime()执行时可通过'format'参数来指定具体的某一日期格式。'exact'参数则是指定是否强行查找出符合'format'格式的字符串来进行识别。

pd.to_datetime运行结果

运行速度对比
还是利用csv文件中Date、Time两列数据,先将字符串合并,然后分别用datetime.strptime()、dateutil.parser.parse()与pd.to_datetime()解析,结果如图所示。最快的datetime.strptime()只用了1分14秒完成了220.5万条数据的日期解析;dateutil.parser.parse()用了3分56秒,pd.to_datetime()居然用了18分34秒之多。pd.to_datetime()指定'format'参数后用时有所改善,但仍然用了10分36秒才完成。pd.to_datetime()参数中有一个与read_csv()命令相同的参数'infer_datetime_format',但在这里指定infer_datetime_format = True似乎对运行速度没有影响。换个时间再试运行时间会有差异,但三者的速度排名不变。而且,这样看来最高效的方式反而是在read_csv()时就将日期解析完成。

datetime.strptime()、dateutil.parser.parse()与pd.to_datetime()速度对比

结论

  • pd.read_csv读入文件同时,如需利用parse_dates解析日期,尝试指定'infer_datetime_format' = True可能是个不错的选项;
  • datetime.strptime()运行速度更快,但须编写日期格式定义。dateutil.parser.parse()会自动判断日期格式,使用更为灵活。对于'20'这样的字符串,parse()会自动解析为'2020-xx-xx 00:00'(xx-xx为当前日期),可能会造成不必要的困扰。
  • pd.to_datetime()也能够自动判断日期格式,但较dateutil.parser.parse()谨慎。对于容易引起歧义的字符串,如'20',pd.to_datetime()会报错。不过,它也是三者之中运行速度最慢的。
  • 综上所述,在需要字符串转日期时,dateutil.parser.parse()值得优先选用。也难怪它会被pd.read_csv()指定为默认的日期解析程序了。
参考资料
  1. pandas.read_csv
  2. Parsing a CSV with mixed timezones
  3. Python for Data Analysis, 2nd Edition
  4. parser — dateutil 2.8.0 documentation
  5. pandas.to_datetime — pandas 0.25.1 documentation
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,428评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,024评论 3 413
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,285评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,548评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,328评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,878评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,971评论 3 439
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,098评论 0 286
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,616评论 1 331
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,554评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,725评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,243评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,971评论 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,361评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,613评论 1 280
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,339评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,695评论 2 370

推荐阅读更多精彩内容