将时间字符串 (Hour:Min:Sec.Millsecs) 快速转换为浮点数

Posted

技术标签:

【中文标题】将时间字符串 (Hour:Min:Sec.Millsecs) 快速转换为浮点数【英文标题】:Fast conversion of time string (Hour:Min:Sec.Millsecs) to float 【发布时间】:2014-05-26 04:21:42 【问题描述】:

我使用 pandas 导入一个 csv 文件(大约一百万行,5 列),其中包含一列时间戳(逐行增加),格式为 Hour:Min:Sec.Millsecs,例如

11:52:55.162

和其他一些带有浮点数的列。我需要将时间戳列转换为浮点数(例如以秒为单位)。到目前为止,我正在使用

pandas.read_csv  

获取数据帧df,然后将其转换为numpy数组

df=np.array(df)

以上所有功能都很好,而且速度非常快。但是,然后我使用 datetime.strptime (第 0 列是时间戳)

df[:,0]=[(datetime.strptime(str(d),'%H:%M:%S.%f')).total_seconds() for d in df[:,0]]

将时间戳转换为秒,不幸的是这变得非常缓慢。不是所有行的迭代都这么慢,而是

datetime.strptime 

是瓶颈。有没有更好的方法?

【问题讨论】:

您如何确定datetime.strptime 是瓶颈?我不确定df[:,0] 是否是切片副本(对 numpy 不够熟悉),但切片副本和列表理解对我来说似乎更好。 【参考方案1】:

我猜datetime 对象有很多开销 - 手动操作可能更容易:

def to_seconds(s):
    hr, min, sec = [float(x) for x in s.split(':')]
    return hr*3600 + min*60 + sec

【讨论】:

这将引发ValueError,因为您将在最后一位数字的十进制表示上调用int。根据 OP 的问题,输入的格式类似于“11:52:55.162”。 @Two-BitAlchemist 我已经纠正了,我把它改成了浮动。 哇非常感谢。是的,这要快得多。感谢您的快速回复!【参考方案2】:

使用sum()enumerate() -

>>> ts = '11:52:55.162'
>>> ts1 = map(float, ts.split(':'))
>>> ts1
[11.0, 52.0, 55.162]
>>> ts2 = [60**(2-i)*n for i, n in enumerate(ts1)]
>>> ts2
[39600.0, 3120.0, 55.162]
>>> ts3 = sum(ts2)
>>> ts3
42775.162
>>> seconds = sum(60**(2-i)*n for i, n in enumerate(map(float, ts.split(':'))))
>>> seconds
42775.162
>>> 

【讨论】:

我用两种方式(你的和我的)都做过,结果你的速度更快,即使是函数调用。【参考方案3】:

这里,使用时间增量

创建示例系列

In [21]: s = pd.to_timedelta(np.arange(100000),unit='s')

In [22]: s
Out[22]: 
0    00:00:00
1    00:00:01
2    00:00:02
3    00:00:03
4    00:00:04
5    00:00:05
6    00:00:06
7    00:00:07
8    00:00:08
9    00:00:09
10   00:00:10
11   00:00:11
12   00:00:12
13   00:00:13
14   00:00:14
...
99985   1 days, 03:46:25
99986   1 days, 03:46:26
99987   1 days, 03:46:27
99988   1 days, 03:46:28
99989   1 days, 03:46:29
99990   1 days, 03:46:30
99991   1 days, 03:46:31
99992   1 days, 03:46:32
99993   1 days, 03:46:33
99994   1 days, 03:46:34
99995   1 days, 03:46:35
99996   1 days, 03:46:36
99997   1 days, 03:46:37
99998   1 days, 03:46:38
99999   1 days, 03:46:39
Length: 100000, dtype: timedelta64[ns]

为了测试目的转换为字符串

In [23]: t = s.apply(pd.tslib.repr_timedelta64)

这些是字符串

In [24]: t.iloc[-1]
Out[24]: '1 days, 03:46:39'

除以 timedelta64 将其转换为秒

In [25]: pd.to_timedelta(t.iloc[-1])/np.timedelta64(1,'s')
Out[25]: 99999.0

目前这是使用正则表达式进行匹配,因此直接从字符串中不是很快。

In [27]: %timeit pd.to_timedelta(t)/np.timedelta64(1,'s')
1 loops, best of 3: 1.84 s per loop

这是一个基于日期时间戳的解决方案

由于日期时间已经存储为 int64,这非常容易快速

创建示例系列

In [7]: s = Series(date_range('20130101',periods=1000,freq='ms'))

In [8]: s
Out[8]: 
0           2013-01-01 00:00:00
1    2013-01-01 00:00:00.001000
2    2013-01-01 00:00:00.002000
3    2013-01-01 00:00:00.003000
4    2013-01-01 00:00:00.004000
5    2013-01-01 00:00:00.005000
6    2013-01-01 00:00:00.006000
7    2013-01-01 00:00:00.007000
8    2013-01-01 00:00:00.008000
9    2013-01-01 00:00:00.009000
10   2013-01-01 00:00:00.010000
11   2013-01-01 00:00:00.011000
12   2013-01-01 00:00:00.012000
13   2013-01-01 00:00:00.013000
14   2013-01-01 00:00:00.014000
...
985   2013-01-01 00:00:00.985000
986   2013-01-01 00:00:00.986000
987   2013-01-01 00:00:00.987000
988   2013-01-01 00:00:00.988000
989   2013-01-01 00:00:00.989000
990   2013-01-01 00:00:00.990000
991   2013-01-01 00:00:00.991000
992   2013-01-01 00:00:00.992000
993   2013-01-01 00:00:00.993000
994   2013-01-01 00:00:00.994000
995   2013-01-01 00:00:00.995000
996   2013-01-01 00:00:00.996000
997   2013-01-01 00:00:00.997000
998   2013-01-01 00:00:00.998000
999   2013-01-01 00:00:00.999000
Length: 1000, dtype: datetime64[ns]

自纪元以来转换为 ns / 除以获取自纪元以来的 ms(如果你想要秒, 除以 10**9)

In [9]: pd.DatetimeIndex(s).asi8/10**6
Out[9]: 
array([1356998400000, 1356998400001, 1356998400002, 1356998400003,
       1356998400004, 1356998400005, 1356998400006, 1356998400007,
       1356998400008, 1356998400009, 1356998400010, 1356998400011,
       ...
       1356998400992, 1356998400993, 1356998400994, 1356998400995,
       1356998400996, 1356998400997, 1356998400998, 1356998400999])

相当快

In [12]: s = Series(date_range('20130101',periods=1000000,freq='ms'))

In [13]: %timeit pd.DatetimeIndex(s).asi8/10**6
100 loops, best of 3: 11 ms per loop

【讨论】:

我认为这里的瓶颈是日期时间对象的创建,而不是总秒数的计算。

以上是关于将时间字符串 (Hour:Min:Sec.Millsecs) 快速转换为浮点数的主要内容,如果未能解决你的问题,请参考以下文章

将 ISO 8601 日期时间字符串转换为 **Date** 对象时,如何将日期时间重新定位到当前时区?

将字符串日期时间转换为熊猫日期时间

pandas将dataframe中日期字符串数据列和时间字符串数据列合并成完整时间字符串并使用to_datetime将字符串格式转化为时间格式

如何将本地时间字符串转换为 UTC?

将字符串转换为日期时间

将纪元时间转换为日期字符串