关于python:从numpy.datetime64转换为pandas.tslib.Timestamp错误?

Conversion from numpy.datetime64 to pandas.tslib.Timestamp bug?

我有一个python模块,它将数据直接加载到numpy.ndarray的dict中,以便在pandas.dataframe中使用。但是,我注意到了"na"值的一个问题。我的文件格式表示NA值A-9223372036854775808(boost::integer_traits::const_min)。我的非NA值按预期加载到pandas.dataframe中(使用正确的值)。我相信正在发生的是,我的模块加载到numpy.datetime64 ndarray中,然后将其转换为pandas.tslib.timestamp的列表。此转换似乎不保留"const_min"整数。尝试以下操作:

1
2
3
4
>>> pandas.tslib.Timestamp(-9223372036854775808)
NaT
>>> pandas.tslib.Timestamp(numpy.datetime64(-9223372036854775808))
<Timestamp: 1969-12-31 15:58:10.448384>

这是熊猫虫吗?我想在这种情况下,我可以让我的模块避免使用numpy.ndarray,并使用熊猫不喜欢的东西(可能预先分配tslib.timestamp本身的列表)。

下面是发生意外事件的另一个例子:

1
2
3
4
5
6
>>> npa = numpy.ndarray(1, dtype=numpy.datetime64)
>>> npa[0] = -9223372036854775808
>>> pandas.Series(npa)
0   NaT
>>> pandas.Series(npa)[0]
<Timestamp: 1969-12-31 15:58:10.448384>

下面是杰夫的评论,我有更多关于问题的信息。

1
2
3
4
5
6
7
8
9
>>> npa = numpy.ndarray(2, dtype=numpy.int64)
>>> npa[0] = -9223372036854775808
>>> npa[1] = 1326834000090451
>>> npa
array([-9223372036854775808,     1326834000090451])
>>> s_npa = pandas.Series(npa, dtype='M8[us]')
>>> s_npa
0                          NaT
1   2012-01-17 21:00:00.090451

哎呀!这个系列保留了NA和我的时间戳。但是,如果我试图从这个系列创建一个数据帧,那么NAT就消失了。

1
2
3
4
>>> pandas.DataFrame({'ts':s_npa})
                      ts
0 1969-12-31 15:58:10.448384
1 2012-01-17 21:00:00.090451

嗬哼。一时兴起,我试着把整数解释成过去一个时代的纳米秒。令我惊讶的是,数据帧工作正常:

1
2
3
4
5
6
7
8
s2_npa = pandas.Series(npa, dtype='M8[ns]')
>>> s2_npa
0                             NaT
1   1970-01-16 08:33:54.000090451
>>> pandas.DataFrame({"ts":s2_npa})
                             ts
0                           NaT
1 1970-01-16 08:33:54.000090451

当然,我的时间戳不正确。我的观点是pandas.dataframe在这里的行为不一致。为什么在使用dtype='m8[ns]'时保留nat,而在使用'm8[us]'时不保留?

我目前正在使用此变通方法来转换,这会使速度减慢很多,但会起作用:

1
2
3
4
5
>>> s = pandas.Series([1000*ts if ts != -9223372036854775808 else ts for ts in npa], dtype='M8[ns]')
>>> pandas.DataFrame({'ts':s})
                          ts
0                        NaT
1 2012-01-17 21:00:00.090451

(几个小时后……)

好吧,我有进步。我已经深入研究了代码,以认识到序列上的repr函数最终调用了"format"datetime64,它检查"isull",并打印出"nat",解释了这两者之间的区别。

1
2
3
4
>>> pandas.Series(npa)
0   NaT
>>> pandas.Series(npa)[0]
<Timestamp: 1969-12-31 15:58:10.448384>

前者似乎是为了向北美致敬,但只有在印刷时才会这样做。我想可能还有其他熊猫函数调用"isNull"并根据答案进行操作,在这种情况下,这可能在NA时间戳中部分起作用。但是,我知道由于元素0的类型,这个系列是不正确的。它是时间戳,但应该是nattype。我的下一步是深入到系列的构造器中,了解pandas在构建期间何时/如何使用nat值。大概,当我指定dtype='m8[us]'时,它缺少一个事例…(来得更多)。

根据安迪在评论中的建议,我尝试使用熊猫时间戳来解决这个问题。它不起作用。以下是这些结果的示例:

1
2
3
4
5
6
7
8
>>> npa = numpy.ndarray(1, dtype='i8')
>>> npa[0] = -9223372036854775808
>>> npa
array([-9223372036854775808])
>>> pandas.tslib.Timestamp(npa.view('M8[ns]')[0]).value
-9223372036854775808
>>> pandas.tslib.Timestamp(npa.view('M8[us]')[0]).value
-28909551616000


答:没有

从技术上讲,就是这样。我在Github上发布了这个bug,并在这里得到了响应:https://github.com/pydata/pandas/issues/2800_issuecomment-13161074

"索引等当前不支持纳秒以外的单位。应严格执行此操作。"

我用"ns"而不是"us"运行的所有测试都正常。我期待着将来的发行版。

对于任何感兴趣的人,我修改了我的C++ Python模块来迭代从磁盘加载的iN64个数组,除了Na值(Booo::整数字符::COSTROSTMIN)以外,乘以1000。我很担心性能,但装载时间的差异对我来说很小。(在python中这样做非常非常缓慢。)