来自 MongoDB ISODate 的 Pandas DatetimeIndex

Posted

技术标签:

【中文标题】来自 MongoDB ISODate 的 Pandas DatetimeIndex【英文标题】:Pandas DatetimeIndex from MongoDB ISODate 【发布时间】:2016-11-07 04:28:24 【问题描述】:

我在处理时间/时区时遇到了一些困难。我有表单的原始 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 中读取这些数据并使用它来创建熊猫 DatetimeIndex

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

这给了我

这是不正确的,因为时间尚未正确地从 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)

这给了我正确的一天

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

frame.groupby(idx).sum()

然而,此时发生了一些奇怪的事情。日期最终分组如下

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

任何人都可以阐明我可能出错的地方吗?


回复@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()

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

由于某种原因,2014 年的 groupby 没有正确分组,如下所示

如果相反,我使用

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()

这种方法似乎对我有效

【问题讨论】:

idxframe 中的列还是单独的索引?看起来 idxframe.index 不一致(idx[0] 与您粘贴的 frame 中的索引不匹配 - 如果它是真实数据)。如果您可以粘贴一个带有frameidx 的小示例,它会导致此错误,那将会很有帮助。 对不起,我应该说得更清楚。 idxframe 的索引。我更新了问题以反映这一点 嗯,时区的错误很奇怪。我无法重现它。也许这是另一个错误。 【参考方案1】:

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

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 版本中提供。

【讨论】:

【参考方案2】:

我现在已经设法通过将我的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()

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

我将此作为答案发布,以防没有人回复,但我希望有人回答并解释发生了什么,因为我的“解决方案”对我的口味来说似乎太老套了。

【讨论】:

以上是关于来自 MongoDB ISODate 的 Pandas DatetimeIndex的主要内容,如果未能解决你的问题,请参考以下文章

mongodb isodate怎么查询

Mongodb:基于 ISODate 格式的时间查询。我的查询有啥问题?

如何在 mongodb php 中以 ISODATE 格式存储当前日期和时间?

将 MongoDB 字段从字符串转换为数组中的 ISODate

golang解析mongodb中的ISODate类型

mongoDB对时间的处理ISODate与我们时区相差8小时