【问题标题】:Python - Group Dates by MonthPython - 按月分组日期
【发布时间】:2018-03-31 23:39:34
【问题描述】:

这是一个简单的问题,起初我认为它很简单。一个小时后,我不太确定!
所以,我有一个 Python datetime 对象列表,我想绘制它们。 x 值是年和月,y 值是该列表中本月发生的日期对象的数量。
也许一个例子会更好地证明这一点(dd/mm/yyyy):

[28/02/2018, 01/03/2018, 16/03/2018, 17/05/2018] 
-> ([02/2018, 03/2018, 04/2018, 05/2018], [1, 2, 0, 1])

我的第一次尝试尝试简单地按日期和年份分组,大致如下:

import itertools
group = itertools.groupby(dates, lambda date: date.strftime("%b/%Y"))
graph = zip(*[(k, len(list(v)) for k, v in group]) # format the data for graphing

您可能已经注意到,这将仅按列表中已经存在的日期进行分组。在我上面的例子中,没有一个日期发生在四月的事实会被忽略。

接下来,我尝试查找开始和结束日期,并循环查看它们之间的月份:

import datetime
data = [[], [],]
for year in range(min_date.year, max_date.year):
    for month in range(min_date.month, max_date.month):
        k = datetime.datetime(year=year, month=month, day=1).strftime("%b/%Y")
        v = sum([1 for date in dates if date.strftime("%b/%Y") == k])
        data[0].append(k)
        data[1].append(v)

当然,这仅在 min_date.month 小于 max_date.month 时才有效,如果它们跨越多年,则不一定如此。此外,它非常丑陋。

有没有一种优雅的方式来做到这一点?
在此先感谢

编辑:需要明确的是,日期是datetime 对象,而不是字符串。为了便于阅读,它们在这里看起来像字符串。

【问题讨论】:

标签: python datetime itertools


【解决方案1】:

我建议使用pandas:

import pandas as pd

dates = ['28/02/2018', '01/03/2018', '16/03/2018', '17/05/2018'] 

s = pd.to_datetime(pd.Series(dates), format='%d/%m/%Y')
s.index = s.dt.to_period('m')
s = s.groupby(level=0).size()

s = s.reindex(pd.period_range(s.index.min(), s.index.max(), freq='m'), fill_value=0)
print (s)
2018-02    1
2018-03    2
2018-04    0
2018-05    1
Freq: M, dtype: int64

s.plot.bar()

说明:

  1. 首先从dates 列表中创建Series 并转换to_datetimes。
  2. 由Series.dt.to_period 创建PeriodIndex
  3. groupby 按索引 (level=0) 并通过 GroupBy.size 获取计数
  4. Series.reindex 由索引的最大值和最小值创建的PeriodIndex 添加缺失的句点
  5. 最后一个情节,例如酒吧 - Series.plot.bar

【讨论】:

  • 感谢您的回答,但我认为仅针对这个问题,导入像 pandas 这样的大型库有点矫枉过正。计数器的想法似乎更合适。无论如何,谢谢。
  • @EriktheRed - 没问题,但如果将来需要使用类似的数据分析,pandas 会更好:)
  • @EriktheRed - 但我理解你,我开始使用 pandas 只是因为纯 python 不能处理大型 csv 文件:)。这个库真的很好,大大简化了解决方案。编码愉快!
  • 看来我要买熊猫了。计数器毕竟不是那么好......
【解决方案2】:

使用计数器

dates = list()
import random
import collections

for y in range(2015,2019):
  for m in range(1,13):
    for i in range(random.randint(1,4)):
      dates.append("{}/{}".format(m,y))

print(dates)
counter = collections.Counter(dates)
print(counter)

对于没有出现日期的问题,您可以使用 Counter 的 subtract 方法 生成一个包含所有日期范围的列表,每个日期只会出现在列表中一次,然后你可以使用减法 像这样

tmp_date_list = ["{}/{}".format(m,y) for y in range(2015,2019) for m in range(1,13)]
counter.subtract(tmp_date_list)

【讨论】:

  • 有什么方法可以将我的日期作为原生 datetime 对象保留在这里,而不是将它们转换为字符串并再次返回?
  • 另外,这不包括列表中没有出现的月份。这与我的groupby 尝试存在相同的问题。
  • @EriktheRed,是的,您可以使用本机 datetime 对象,但您只需要使用年份和月份创建它们,计数器会知道如何比较它们
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 2018-02-11
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 2018-08-30
  • 2015-11-18
相关资源
最近更新 更多