来自MongoDB的Pandas DatetimeIndex ISODate

时间:2016-07-05 10:57:04

标签: python mongodb datetime pandas time

我在使用时间/时区时遇到一些困难。我有

形式的原始JSON数据
{
  "Date": "28 Sep 2009 00:00:00",
  ....
}

然后将此数据加载到MongoDB中,并将此日期的字符串表示形式转换为JavaScript Date object。此转换为UTC时间会产生以下日期

{
  "_id": ObjectId("577a788f4439e17afd4e21f7"),
  "Date": ISODate("2009-09-27T23:00:00Z")
}

它"看起来"好像日期实际上已经向前移动了一天,我假设(可能不正确)这是因为我的机器设置为Irish Standard Time

然后我从MongoDB读取这些数据并用它来创建一个pandas DatetimeIndex

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')

给了我

enter image description here

这是不正确的,因为时间尚未从UTC正确转换回本地时间。所以我按照this answer

中给出的解决方案
idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
frame = DataFrame(test_docs, index=idx)
frame = frame.drop('Date', 1)

给了我正确的一天

enter image description here

然后normalize DatetimeIndex,以便删除小时数,允许我按天分组所有条目。

frame.groupby(idx).sum()
然而,在这一点上,发生了一些奇怪的事情。日期最终分组如下

enter image description here

但这并不反映框架中的日期

enter image description here

任何人都可以了解我可能出错的地方吗?

对@ptrj的响应

明确地将我的时区用作字符串

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz='Europe/Dublin')
idx = idx.normalize()
frame = DataFrame(test_docs, index=idx)
...
...
aggregate = frame.groupby(idx).sum()
aggregate.plot()

这对我不起作用,导致以下情节

enter image description here

由于某种原因,groupby未在2014年正确分组,如下所示

enter image description here

如果相反,我使用

idx = idx.tz_convert(tz.gettz('Europe/Dublin'))

我遇到同样的问题

转换为对象

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
idx = idx.normalize()
frame = DataFrame(test_docs, index=idx)
aggregate = frame.groupby(idx.astype(object)).sum()

这种方法似乎对我有效

enter image description here

2 个答案:

答案 0 :(得分:2)

我能够使用以下数据重现错误:

idx0 = pd.date_range('2011-11-11', periods=4)
idx1 = idx0.tz_localize(tz.tzutc())
idx2 = idx1.tz_convert(tz.tzlocal())
df = pd.DataFrame([1, 2, 3, 4])

df.groupby(idx2).sum()
Out[20]: 
                           0
1970-01-01 00:00:00-05:00  9
2011-11-10 19:00:00-05:00  1

这是pandas代码中的一个深层错误,与tz.tzlocal()完全相关。它也表现在:

idx2.tz_localize(None)
Out[27]: 
DatetimeIndex(['2011-11-10 19:00:00', '1970-01-01 00:00:00',
               '1970-01-01 00:00:00', '1970-01-01 00:00:00'],
              dtype='datetime64[ns]', freq='D')

您可以使用以下任何解决方案:

  • 明确地将您的时区用作字符串:

    idx2 = idx1.tz_convert(tz='Europe/Dublin')
    df.groupby(idx2).sum()
    Out[29]: 
                               0
    2011-11-11 00:00:00+00:00  1
    2011-11-12 00:00:00+00:00  2
    2011-11-13 00:00:00+00:00  3
    2011-11-14 00:00:00+00:00  4
    

    或者如果它不起作用:

    idx2 = idx1.tz_convert(tz.gettz('Europe/Dublin'))
    
  • 将其转换为对象:

    df.groupby(idx2.astype(object)).sum()
    Out[32]: 
                               0
    2011-11-10 19:00:00-05:00  1
    2011-11-11 19:00:00-05:00  2
    2011-11-12 19:00:00-05:00  3
    2011-11-13 19:00:00-05:00  4
    

基本上,使用tz=tz.local()转换为DatetimeIndex以外的任何内容都应该有效。

编辑:bug已在pandas github上修复。该修复程序将在pandas 0.19发布中提供。

答案 1 :(得分:0)

我已经设法通过将groupby更改为以下

来解决此问题
frame.groupby([pd.DatetimeIndex([x.date() for x in frame.index])]).sum()

所以我最初尝试groupby

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
frame.groupby(idx).sum()

我现在在执行date操作之前在索引的每个元素上调用groupby方法。

如果没有人回复,我会将此作为答案发布,但我希望有人回答并解释正在发生的事情,因为我的'解决方案'对我的口味来说似乎过于苛刻。< / p>