2017-07-14 109 views
2

我有两栏:“问”和“回答”,但“回答”是一个对象,而“询问”是datetime64 [ns],所以我将'已回答'转换为日期时间:将负日期时间转换为NaT

df['answered'] = pd.to_datetime(df['answered']) 

index, asked, answered 
0  2016-07-04 07/07/2016 
1  2016-07-03 07/01/2016 
2  2016-07-05 07/09/2016 
3  NaT   NaN 

于是,我做了一个第三列,让我在两者之间的时间差:

df['Days']= df['answered'] - df['asked'] 

index,  asked, answered, Days  
    0  2016-07-04 07/07/2016 3 days 
    1  2016-07-03 07/01/2016 -2 days 
    2  2016-07-05 07/09/2016 4 
    3  NaT   NaN   NaT 

随着@piRSquared的帮助下,我试图把负天进一个NAT,但当我这样做时什么都没有发生:

df.update(df[['Days']].mask(df < 0)) 

我该如何将负面日子转换为NaT?

回答

1

对我来说作品comapre Series(列)由0 Timedelta,然后通过Series.maskloc创建NaT

mask = df['Days'] < pd.Timedelta(0) 
df['Days'] = df['Days'].mask(mask) 
print (df) 
     asked answered Days 
0 2016-07-04 2016-07-07 3 days 
1 2016-07-03 2016-07-01 NaT 
2 2016-07-05 2016-07-09 4 days 
3  NaT  NaT NaT 

或者:

mask = df['Days'] < pd.Timedelta(0) 
df.loc[mask, 'Days'] = np.nan 
print (df) 
     asked answered Days 
0 2016-07-04 2016-07-07 3 days 
1 2016-07-03 2016-07-01 NaT 
2 2016-07-05 2016-07-09 4 days 
3  NaT  NaT NaT 

但是,如果与DataFrame比较0 Timedelta它是越野车:

print (df) 
     asked answered Days Days2 
0 2016-07-04 2016-07-07 3 days 3 days 
1 2016-07-03 2016-07-01 -2 days -2 days 
2 2016-07-05 2016-07-09 4 days 4 days 
3  NaT  NaT  NaT  NaT 

df1 = df.select_dtypes([np.timedelta64]) 

#return wrong mask 
m1 = df1 < pd.Timedelta(0) 
print (m1) 
    Days Days2 
0 False False 
1 False False 
2 False False 
3 True True 

#if comapre with apply by Series it works 
m2 = df1.apply(lambda x: x < pd.Timedelta(0)) 
print (m2) 
    Days Days2 
0 False False 
1 True True 
2 False False 
3 False False 

#compare numpy array works but warning 
m3 = df1.values < np.array(0, dtype=np.timedelta64) 
print (m3) 
[[False False] 
[ True True] 
[False False] 
[ True True]] 

FutureWarning:未来,'NAT < x'和'x < NAT'将始终为False。

df[df1.columns] = df1.mask(m2) 
print (df) 
     asked answered Days Days2 
0 2016-07-04 2016-07-07 3 days 3 days 
1 2016-07-03 2016-07-01 NaT NaT 
2 2016-07-05 2016-07-09 4 days 4 days 
3  NaT  NaT NaT NaT 
+0

谢谢@jezrael我一直在为此工作数小时。你的解决方案和详细的解释真的帮了我。 –

4

使用mask

df.mask(df < 0) 

     Days col2 
index     
0  20 days  NaT 
1  61 days 78 days 
2   NaT 10 days 

pd.DataFrame.mask需要标识的位置,以掩盖布尔值的阵列。如果传递了可选的第二个参数,它将用可选参数中指定的值替换已标识位置中的值。如果该参数没有通过,就像在我提出的解决方案中一样,那么标识值将被替换为空值。由于这些列的dtypestimedelta,因此适当的空值将是NaT


假设您的数据框由许多列组成。您可以专注于您关心的特定列。

df[['Days', 'col2']].mask(df < 0) 

然后你就可以更新

df.update(df[['Days', 'col2']].mask(df < 0)) 

DF就地假设你想抓住那名中的所有列timedelta

df.select_dtypes([np.timedelta]).mask(df < 0) 

和更新

df.update(df.select_dtypes([np.timedelta64]).mask(df < 0)) 
+0

我得到“无效类型比较”。是因为我有更多的专栏,只有这两个我放在这里?我尝试过,但无济于事:df。mask(df ['Days'] <0) –

+0

@AdamSchroeder我更新了我的帖子。由于某种原因, – piRSquared

+0

仍然无法工作。当我打印出否定日期时,我会得到:“-5天+00:00:00”。您提供的解决方案不会引发错误,但不会将负面变为NaT。有没有其他解决方案可以解决这个问题? –