从 Pandas 的 TimeDelta 转换为浮点数天数

32

我有一个TimeDelta列,其值看起来像这样:

2天21:54:00.000000000

我想要一个代表天数的小数,比如在这里是2 + 21/24 = 2.875(忽略分钟)。是否有简单的方法实现这一点?我看到有一个答案建议

res['Ecart_lacher_collecte'].apply(lambda x: float(x.item().days+x.item().hours/24.))

但我收到了“AttributeError:'str'对象没有属性'item'”错误。

Numpy版本为'1.10.4',Pandas版本为u'0.17.1'。

列最初是通过以下方式获取的:

lac['DateHeureLacher'] = pd.to_datetime(lac['Date lacher']+' '+lac['Heure lacher'],format='%d/%m/%Y %H:%M:%S')
cap['DateCollecte'] = pd.to_datetime(cap['Date de collecte']+' '+cap['Heure de collecte'],format='%d/%m/%Y %H:%M:%S')

在第一个脚本中编写。然后在第二个脚本中:

res = pd.merge(lac, cap, how='inner', on=['Loc'])
res['DateHeureLacher']  = pd.to_datetime(res['DateHeureLacher'],format='%Y-%m-%d %H:%M:%S')
res['DateCollecte']  = pd.to_datetime(res['DateCollecte'],format='%Y-%m-%d %H:%M:%S')
res['Ecart_lacher_collecte'] = res['DateCollecte'] - res['DateHeureLacher']

也许将其保存到csv文件会将它们的类型更改为字符串?我尝试进行的转换是在第三个脚本中完成的。

Sexe_x  PiegeLacher latL    longL   Loc Col_x   DateHeureLacher Nb envolees PiegeCapture    latC    longC   Col_y   Sexe_y  Effectif    DateCollecte    DatePose    Ecart_lacher_collecte   Dist_m
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-002  1629238 237877  Rouge   M   1   2011-02-07 15:09:00 2011-02-07 12:14:00 2 days 21:54:00.000000000   0
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-002  1629238 237877  Rouge   M   4   2011-02-07 12:14:00 2011-02-07 09:42:00 2 days 18:59:00.000000000   0
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-003  1629244 237950  Rouge   M   1   2011-02-07 15:10:00 2011-02-07 12:16:00 2 days 21:55:00.000000000   75

res.info():

Sexe_x                   922 non-null object
PiegeLacher              922 non-null object
latL                     922 non-null int64
longL                    922 non-null int64
Loc                      922 non-null object
Col_x                    922 non-null object
DateHeureLacher          922 non-null object
Nb envolees              922 non-null int64
PiegeCapture             922 non-null object
latC                     922 non-null int64
longC                    922 non-null int64
Col_y                    922 non-null object
Sexe_y                   922 non-null object
Effectif                 922 non-null int64
DateCollecte             922 non-null object
DatePose                 922 non-null object
Ecart_lacher_collecte    922 non-null object
Dist_m                   922 non-null int64

2
如果我理解正确的话,您可以执行res['columnname'].dt.total_seconds()/ (24 * 60 * 60) - EdChum
@AntonProtopopov 我得到了“AttributeError:只能在datetime-like值上使用.dt访问器”的错误。 - alpagarou
1
@alpagarou 首先,您需要将列转换为“datetime”对象。您可以使用“pd.to_datetime”来完成这个操作。 - Anton Protopopov
你是说你的列是一系列时间差吗? - EdChum
好的,我刚试了一下,发现无法在read_csv中指定dtype以将其解析为timedelta64,所以你有几个选择。使用astype将timedelta64转换为int64,然后在读取时使用自定义转换器进行转换,或者在读取回来后可以用astype(np.timedelta64),这样也可以起作用。 - EdChum
显示剩余10条评论
3个回答

32
你可以使用 pd.to_timedeltanp.timedelta64 来定义时间间隔,并通过它来进行除法运算:
# set up as per @EdChum
df['total_days_td'] = df['time_delta'] / pd.to_timedelta(1, unit='D')
df['total_days_td'] = df['time_delta'] / np.timedelta64(1, 'D')

你也可以使用datetime模块,例如datetime.timedelta(days=1)。 - MarMat

10
你可以使用 dt.total_seconds,然后将其除以一天的总秒数,例如:
In [25]:
df = pd.DataFrame({'dates':pd.date_range(dt.datetime(2016,1,1, 12,15,3), periods=10)})
df

Out[25]:
                dates
0 2016-01-01 12:15:03
1 2016-01-02 12:15:03
2 2016-01-03 12:15:03
3 2016-01-04 12:15:03
4 2016-01-05 12:15:03
5 2016-01-06 12:15:03
6 2016-01-07 12:15:03
7 2016-01-08 12:15:03
8 2016-01-09 12:15:03
9 2016-01-10 12:15:03

In [26]:
df['time_delta'] = df['dates'] - pd.datetime(2015,11,6,8,10)
df

Out[26]:
                dates       time_delta
0 2016-01-01 12:15:03 56 days 04:05:03
1 2016-01-02 12:15:03 57 days 04:05:03
2 2016-01-03 12:15:03 58 days 04:05:03
3 2016-01-04 12:15:03 59 days 04:05:03
4 2016-01-05 12:15:03 60 days 04:05:03
5 2016-01-06 12:15:03 61 days 04:05:03
6 2016-01-07 12:15:03 62 days 04:05:03
7 2016-01-08 12:15:03 63 days 04:05:03
8 2016-01-09 12:15:03 64 days 04:05:03
9 2016-01-10 12:15:03 65 days 04:05:03

In [27]:
df['total_days_td'] = df['time_delta'].dt.total_seconds() / (24 * 60 * 60)
df

Out[27]:
                dates       time_delta  total_days_td
0 2016-01-01 12:15:03 56 days 04:05:03      56.170174
1 2016-01-02 12:15:03 57 days 04:05:03      57.170174
2 2016-01-03 12:15:03 58 days 04:05:03      58.170174
3 2016-01-04 12:15:03 59 days 04:05:03      59.170174
4 2016-01-05 12:15:03 60 days 04:05:03      60.170174
5 2016-01-06 12:15:03 61 days 04:05:03      61.170174
6 2016-01-07 12:15:03 62 days 04:05:03      62.170174
7 2016-01-08 12:15:03 63 days 04:05:03      63.170174
8 2016-01-09 12:15:03 64 days 04:05:03      64.170174
9 2016-01-10 12:15:03 65 days 04:05:03      65.170174

3
是的,它可以工作,但我很震惊他们没有 dt.total_days() 或 dt.total_hours 可用... - Jingwei Yu
1
@LedgerYu,因为这不是timedelta的标准方法,此外,自己计算这个时间间隔很容易,但是如果我们按照你的逻辑去做,那么我们还需要编写total_days、total_hours、total_quarters、total_years、total_milliseconds等方法。 - EdChum
你可以直接使用 dt.days,不需要加括号。这对我来说有效! - Guillochon
1
@Guillochon dt.days 只会给你天数,并且会丢弃所有时间信息。 - Taylor

0

你尝试过使用这个吗?

res['Ecart_lacher_collecte'].apply(lambda x: (x.total_seconds()//(3600*24)) + (x.total_seconds()%(3600*24)//3600)/24))

第一个术语是日期(在您的情况下为2) 第二个术语是忽略分钟的小时比率(在您的情况下为21/24)

如果您不想忽略分钟和秒数据,而是需要考虑一天中所有秒数的比率,则代码如下所示:

res['Ecart_lacher_collecte'].apply(lambda x: (x.total_seconds()/(3600*24))

为什么要将除法和加法分开这样做?与 x.total_seconds()/(3600*24) 的唯一区别在于不计算不完整的小时,我怀疑这不是期望的行为。 - Elias Hasle
是的,没错。不完整的小时不计算在内。 这是因为原帖作者想要这样: “我想要一个浮点数来表示天数,比如说这里的2+21/24=2.875,忽略分钟。” - sharinganSawant
1
@EliasHasle 我已经编辑了答案,其中提到了为什么不计算不完整的小时数。 - sharinganSawant

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