在Pandas中计算两个列之间的时间差,以小时和分钟为单位

163

我有一个数据框,其中有两列fromdatetodate

import pandas as pd

data = {'todate': [pd.Timestamp('2014-01-24 13:03:12.050000'), pd.Timestamp('2014-01-27 11:57:18.240000'), pd.Timestamp('2014-01-23 10:07:47.660000')],
        'fromdate': [pd.Timestamp('2014-01-26 23:41:21.870000'), pd.Timestamp('2014-01-27 15:38:22.540000'), pd.Timestamp('2014-01-23 18:50:41.420000')]}

df = pd.DataFrame(data)

我添加了一列新的diff,用于计算两个日期之间的差异。

df['diff'] = df['fromdate'] - df['todate']

我得到了diff列,但其中包含days,当超过24小时时。

                   todate                 fromdate                    diff
0 2014-01-24 13:03:12.050  2014-01-26 23:41:21.870  2 days 10:38:09.820000
1 2014-01-27 11:57:18.240  2014-01-27 15:38:22.540  0 days 03:41:04.300000
2 2014-01-23 10:07:47.660  2014-01-23 18:50:41.420  0 days 08:42:53.760000

如何将我的结果转换为仅以小时和分钟表示(即将天数转换为小时)?

4个回答

198

Pandas时间戳之间的差异将返回一个datetime.timedelta对象。可以使用*as_type*方法轻松地将其转换为小时,如下所示

import pandas
df = pandas.DataFrame(columns=['to','fr','ans'])
df.to = [pandas.Timestamp('2014-01-24 13:03:12.050000'), pandas.Timestamp('2014-01-27 11:57:18.240000'), pandas.Timestamp('2014-01-23 10:07:47.660000')]
df.fr = [pandas.Timestamp('2014-01-26 23:41:21.870000'), pandas.Timestamp('2014-01-27 15:38:22.540000'), pandas.Timestamp('2014-01-23 18:50:41.420000')]
(df.fr-df.to).astype('timedelta64[h]')

产出,

0    58
1     3
2     8
dtype: float64

1
astype的解决方法虽然有效,但对于包含大量数据(50万行)的文件而言速度太慢了。还有其他建议吗? - student1

78

对我来说,上面提到的.astype()解决方案无效,这让我感到很疑惑。但是我找到了另一种方法。我没有计时或其他任何东西,但可能适用于其他人:

t1 = pd.to_datetime('1/1/2015 01:00')
t2 = pd.to_datetime('1/1/2015 03:30')

print pd.Timedelta(t2 - t1).seconds / 3600.0

...如果您需要小时。或者:

print pd.Timedelta(t2 - t1).seconds / 60.0

如果您想要分钟,请使用.minutes

更新:曾经有一条有用的评论提到了跨越多天的时间段应使用.total_seconds()。由于该评论已经消失,我已经更新了答案。


64
  • 如何将结果转换为仅包含小时和分钟?
    • 被接受的答案只返回天数+小时数分钟没有被包括在内。
  • 如果要提供一个带有小时和分钟的列,格式为hh:mmx 小时 y 分钟,需要进行额外的计算和字符串格式化。
  • 本答案展示了如何使用timedelta数学获取总小时数或总分钟数的浮点数,并且比使用.astype('timedelta64[h]')更快。
    • 根据pandas v2.0.0的说明,.astype('timedelta64[h]')不被允许。
  • Pandas时间差异用户指南
  • Pandas时间序列/日期功能用户指南
  • python timedelta对象:查看支持的操作。
  • 以下示例数据已经是datetime64[ns] dtype。所有相关列都需要使用pandas.to_datetime()进行转换。
  • python 3.11.2pandas 2.0.1numpy 1.24.3中进行了测试
import pandas as pd

# test data from OP, with values already in a datetime format
data = {'to_date': [pd.Timestamp('2014-01-24 13:03:12.050000'), pd.Timestamp('2014-01-27 11:57:18.240000'), pd.Timestamp('2014-01-23 10:07:47.660000')],
        'from_date': [pd.Timestamp('2014-01-26 23:41:21.870000'), pd.Timestamp('2014-01-27 15:38:22.540000'), pd.Timestamp('2014-01-23 18:50:41.420000')]}

# test dataframe; the columns must be in a datetime format; use pandas.to_datetime if needed
df = pd.DataFrame(data)

# add a timedelta column if wanted. It's added here for information only
# df['time_delta_with_sub'] = df.from_date.sub(df.to_date)  # also works
df['time_delta'] = (df.from_date - df.to_date)

# create a column with timedelta as total hours, as a float type
df['tot_hour_diff'] = (df.from_date - df.to_date) / pd.Timedelta(hours=1)

# create a colume with timedelta as total minutes, as a float type
df['tot_mins_diff'] = (df.from_date - df.to_date) / pd.Timedelta(minutes=1)

# display(df)
                  to_date               from_date             time_delta  tot_hour_diff  tot_mins_diff
0 2014-01-24 13:03:12.050 2014-01-26 23:41:21.870 2 days 10:38:09.820000      58.636061    3518.163667
1 2014-01-27 11:57:18.240 2014-01-27 15:38:22.540 0 days 03:41:04.300000       3.684528     221.071667
2 2014-01-23 10:07:47.660 2014-01-23 18:50:41.420 0 days 08:42:53.760000       8.714933     522.896000

其他方法

  • 在其他资源的播客中,需要注意的一点是,在核心开发人员度假期间添加和合并了.total_seconds(),否则不会被批准。
    • 这也是为什么没有其他.total_xx方法的原因。
# convert the entire timedelta to seconds
# this is the same as td / timedelta(seconds=1)
(df.from_date - df.to_date).dt.total_seconds()
[out]:
0    211089.82
1     13264.30
2     31373.76
dtype: float64

# get the number of days
(df.from_date - df.to_date).dt.days
[out]:
0    2
1    0
2    0
dtype: int64

# get the seconds for hours + minutes + seconds, but not days
# note the difference from total_seconds
(df.from_date - df.to_date).dt.seconds
[out]:
0    38289
1    13264
2    31373
dtype: int64

其他资源

%%timeit 测试

import pandas as pd

# dataframe with 2M rows
data = {'to_date': [pd.Timestamp('2014-01-24 13:03:12.050000'), pd.Timestamp('2014-01-27 11:57:18.240000')], 'from_date': [pd.Timestamp('2014-01-26 23:41:21.870000'), pd.Timestamp('2014-01-27 15:38:22.540000')]}
df = pd.DataFrame(data)
df = pd.concat([df] * 1000000).reset_index(drop=True)

%timeit (df.from_date - df.to_date) / pd.Timedelta(hours=1)
[out]:
24.2 ms ± 2.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit (df.from_date - df.to_date).astype('timedelta64[h]')
[out]:
ValueError: Cannot convert from timedelta64[ns] to timedelta64[D]. Supported resolutions are 's', 'ms', 'us', 'ns'

2

默认情况下,pandas中的时间差以纳秒分辨率表示,即timedelta64[ns],因此将其转换为秒/分钟/小时等的一种方法是通过将其纳秒表示除以10**9转换为秒,通过60*10**9转换为分钟等。这种方法比本页上建议的其他方法快至少3倍。1

df['diff_in_seconds'] = df['from_date'].sub(df['to_date']).view('int64') // 10**9
df['diff_in_minutes'] = df['from_date'].sub(df['to_date']).view('int64') // (60*10**9)
df['diff_in_hours'] = df['from_date'].sub(df['to_date']).view('int64') // (3600*10**9)

PS:上述代码假定您想要整秒、分钟、小时等的差异,因此使用整数除法(//),但如果您也想要小数部分,则应改用真实除法(/)。话虽如此,如果您想要精确的差异,那么请考虑将差异转换为更高的分辨率(毫秒/微秒等),而不是分数秒/分钟/小时。


1 使用Trenton McKinney的设置进行一些基准测试:

data = {'to_date': [pd.Timestamp('2014-01-24 13:03:12.050000'), pd.Timestamp('2014-01-27 11:57:18.240000')]*1000000, 
        'from_date': [pd.Timestamp('2014-01-26 23:41:21.870000'), pd.Timestamp('2014-01-27 15:38:22.540000')]*1000000}
df = pd.DataFrame(data)
df['Diff'] = df['from_date'] - df['to_date']

%timeit df['Diff'].view('int64') // (3600*10**9)
# 11 ms ± 271 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df['Diff'] // pd.Timedelta(hours=1)
# 36.7 ms ± 2.99 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df['Diff'].astype('timedelta64[h]')
# 46.5 ms ± 865 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df['Diff'].dt.total_seconds() // 3600
# 169 ms ± 7.71 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接