在字典列表中用月份和年份填充缺失值

1 投票
3 回答
952 浏览
提问于 2025-04-18 00:40

我正在准备一些数据用于可视化,数据的结构是这样的:

data = [{u'count': 1, u'_id': {u'year': 2010, u'month': 4}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 5}}, {u'count': 2, u'_id': {u'year': 2010, u'month': 7}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 9}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 10}}, {u'count': 4, u'_id': {u'year': 2010, u'month': 12}}]

我用一种方法把这些数据转换成一个包含时间戳和计数变量的列表:

chart = []
for month in data:
      d = datetime.datetime.strptime(str(month['_id']['year'])+"-"+str(month['_id']['month']),'%Y-%m')
      dat = time.mktime(d.timetuple())
      chart.append([dat*1000,month['count']])

结果大致是这样的(这个例子和输入数据的例子不完全一致):

chart: [[1220216400000.0, 1], [1222808400000.0, 8], [1225490400000.0, 1], [1228082400000.0, 6], [1230760800000.0, 4], [1233439200000.0, 1], [1235858400000.0, 1], [1238533200000.0, 1], [1241125200000.0, 2], [1243803600000.0, 1], [1246395600000.0, 1], [1249074000000.0, 1]]

我想做的是修改代码,让它也能包含从第一个日期到最后一个日期之间缺失的月份,并把这些月份的计数设置为0。例如,在数据中,从2010年5月到下一个字段是2010年7月,中间的6月缺失了,我想把6月也加上,并把它的计数设置为0。

有什么想法吗?

3 个回答

0

我看到你的列表是排好序的,所以你只需要记住前一个日期(最开始可以设为1),如果有缺失的元素,就把它们填上(也就是说,如果month['_id']['month']和前一个日期之间的差值大于1的话)。

1

这里有一种方法可以做到。

这个思路是使用一个字典 dat -> count。如果你不知道数据中会包含哪些年份,你需要在每次循环时初始化每个月的数据:

import datetime
from pprint import pprint
import time

data = [{u'count': 1, u'_id': {u'year': 2010, u'month': 4}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 5}},
        {u'count': 2, u'_id': {u'year': 2010, u'month': 7}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 9}},
        {u'count': 1, u'_id': {u'year': 2010, u'month': 10}}, {u'count': 4, u'_id': {u'year': 2010, u'month': 12}}]

chart = {}
for month in data:
    year = month['_id']['year']
    for m in xrange(1, 12):
        d = datetime.datetime.strptime(str(year) + "-" + str(m), '%Y-%m')
        dat = time.mktime(d.timetuple()) * 1000
        if dat not in chart:
            chart[dat] = 0

    d = datetime.datetime.strptime(str(year) + "-" + str(month['_id']['month']), '%Y-%m')
    dat = time.mktime(d.timetuple()) * 1000
    chart[dat] = month['count']

pprint(sorted(chart.items()))

如果你知道数据中包含哪些年份,那么在遍历 data 之前就可以初始化每个月的计数。

输出结果:

[(1262322000000.0, 0),
 (1265000400000.0, 0),
 (1267419600000.0, 0),
 (1270094400000.0, 1),
 (1272686400000.0, 1),
 (1275364800000.0, 0),
 (1277956800000.0, 2),
 (1280635200000.0, 0),
 (1283313600000.0, 1),
 (1285905600000.0, 1),
 (1288584000000.0, 0),
 (1291179600000.0, 4)]

你看,缺失的月份也显示出来了,计数是 0

希望这对你有帮助。

1

这里有一个使用 dateutil 库来按月遍历日期范围的解决方案。

这个方法的思路是先创建一个 OrderedDict,用 datetime 作为键,用 count 作为值。接下来,对于有序字典中的每一个项目,按月遍历当前日期和之前添加的日期之间的范围,并添加 0 的计数:

from collections import OrderedDict
import datetime
from pprint import pprint
import time
from dateutil.rrule import rrule, MONTHLY


data = [{u'count': 1, u'_id': {u'year': 2010, u'month': 4}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 5}},
        {u'count': 2, u'_id': {u'year': 2010, u'month': 7}}, {u'count': 1, u'_id': {u'year': 2010, u'month': 9}},
        {u'count': 1, u'_id': {u'year': 2010, u'month': 10}}, {u'count': 4, u'_id': {u'year': 2010, u'month': 12}}]

new_data = OrderedDict()
for item in data:
    year, month = item['_id']['year'], item['_id']['month']
    d = datetime.datetime.strptime(str(year) + "-" + str(month), '%Y-%m')
    new_data[d] = item['count']

chart = {}
last_added = None
for d, count in new_data.iteritems():
    date_start = last_added if last_added else d
    for dt in rrule(MONTHLY, dtstart=date_start, until=d):
        key = time.mktime(dt.timetuple()) * 1000
        if key not in chart:
            chart[key] = count if dt == d else 0
    last_added = d

pprint(sorted(chart.items()))

输出结果:

[(1270094400000.0, 1),
 (1272686400000.0, 1),
 (1275364800000.0, 0),
 (1277956800000.0, 2),
 (1280635200000.0, 0),
 (1283313600000.0, 1),
 (1285905600000.0, 1),
 (1288584000000.0, 0),
 (1291179600000.0, 4)]

希望这个方法对你有用。

撰写回答