让熊猫NaT像NaN一样传播

时间:2018-11-02 17:02:33

标签: python pandas numpy datetime

我正试图从NaT的角度获取几个包含datetime64数据的Pandas Series对象的最小值和最大值。如果dtype是float64,np.minimum和np.maximum会按照我想要的方式工作。也就是说,一旦比较中的任何元素为NaN,NaN就是该比较的结果。例如:

>>> s1
0    0.0
1    1.8
2    3.6
3    5.4
dtype: float64
>>> s2
0    10.0
1    17.0
2     NaN
3    14.0
dtype: float64
>>> np.maximum(s1, s2)
0    10.0
1    17.0
2     NaN
3    14.0
dtype: float64
>>> np.minimum(s1, s2)
0    0.0
1    1.8
2    NaN
3    5.4
dtype: float64

如果s1和s2是datetime64对象,则此方法不起作用:

>>> s1
0   2199-12-31
1   2199-12-31
2   2199-12-31
3   2199-12-31
dtype: datetime64[ns]
>>> s2
0          NaT
1   2018-10-30 
2          NaT
3          NaT
dtype: datetime64[ns]
>>> np.maximum(s1, s2)
0   2199-12-31
1   2199-12-31
2   2199-12-31
3   2199-12-31
dtype: datetime64[ns]
>>> np.minimum(s1, s2)
0   2199-12-31
1   2018-10-30
2   2199-12-31
3   2199-12-31
dtype: datetime64[ns]

我希望无论计算最小值还是最大值,索引0、2和3都会显示为NaT。 (我意识到numpy的功能可能不是最佳选择,但我没有成功找到合适的Pandas类似物。)

稍作阅读后,我发现NaT大约等于NaN,后者具有适当的浮点表示形式。进一步的阅读表明,没有简单的方法可以使NaT“污染”这些比较。 NaN在浮点上下文中以最小/最大比较的方式传播NaT的正确方法是什么?也许有相当于numpy的Pandas等效物。{maximum,minimum}可以识别NaT吗?

3 个答案:

答案 0 :(得分:1)

pd.Series.mask似乎是一种不放弃向量化的解决方案:

s1 = pd.Series([pd.datetime(2099, 12, 31)]*4)
s2 = pd.Series([pd.NaT, pd.datetime(2018, 10, 30), pd.NaT, pd.NaT])

null_check = s1.isnull() | s2.isnull()
res_max = np.maximum(s1, s2).mask(null_check, np.nan)
res_min = np.minimum(s1, s2).mask(null_check, np.nan)

print(res_max)
print(res_min)

0          NaT
1   2099-12-31
2          NaT
3          NaT
dtype: datetime64[ns]
0          NaT
1   2018-10-30
2          NaT
3          NaT
dtype: datetime64[ns]

如您所发现的,您看到的行为的原因pd.NaT具有关联的int值,该值用于比较操作:

print(pd.to_numeric(pd.Series([pd.NaT])))

0   -9223372036854775808
dtype: int64

答案 1 :(得分:0)

不确定这是最好的方法,但是如果您使用object将s1和s2的类型更改为astype,则使用np.minimumnp.maximum仍然可以得到一系列datetime64[ns],例如:

print (np.maximum(s1.astype(object), s2.astype(object)))
0          NaT
1   2199-12-31
2          NaT
3          NaT
Name: 1, dtype: datetime64[ns]

答案 2 :(得分:0)

我相信我已经弄清楚了。 (好吧,至少我想出了一种给猫做皮的方法。)它虽然不是很漂亮,但是比我最初将所有逻辑嵌入apply()的解决方案要快得多。简要地说,该解决方案涉及将datetime元素转换为int,将pd.NaT的int版本映射到np.nan,应用np.minimum / np.maximum,然后再转换回datetime64。 apply()仍然涉及,但是逻辑比我原来的要多得多。 (毫无疑问,它仍然可以改进。我对Pandas / NumPy的了解并不多...)

>>> s1 = pd.Series([pd.NaT, pd.datetime(2018, 10, 30), pd.NaT, pd.NaT])
>>> s1
0          NaT
1   2018-10-30
2          NaT
3          NaT
dtype: datetime64[ns]
>>> nanish = int(pd.NaT)
>>> nanish
-9223372036854775808
>>> s2 = pd.to_numeric(s1)
>>> s2
0   -9223372036854775808
1    1540857600000000000
2   -9223372036854775808
3   -9223372036854775808
dtype: int64
>>> s3 = s2.apply(lambda x: np.nan if x == nanish else x)
>>> s3
0             NaN
1    1.540858e+18
2             NaN
3             NaN
dtype: float64
>>> s5 = np.maximum(s3, s4)
>>> s5
0             NaN
1    1.540858e+18
2             NaN
3             NaN
dtype: float64
>>> s6 = pd.to_datetime(s5)
>>> s6
0          NaT
1   2018-10-30
2          NaT
3          NaT
dtype: datetime64[ns]