Как мне группировать этот список dicts в тот же месяц?
Python newb...
У меня есть список dicts, который я пытаюсь организовать в тот же месяц и год:
[{'date':'2008-04-23','value':'1'},
{'date':'2008-04-01','value':'8'},
{'date':'2008-04-05','value':'3'},
{'date':'2009-04-19','value':'5'},
{'date':'2009-04-21','value':'8'},
{'date':'2010-09-09','value':'3'},
{'date':'2010-09-10','value':'4'},
]
То, что я пытаюсь получить, это список таких как:
[{'date':2008-04-01,'value':'12'},
{'date':2009-04-01,'value':'13'},
{'date':2010-09-01,'value':'7'},
]
Здесь мой код, который просто печатает пустой список:
from datetime import datetime
myList = [{'date':'2008-04-23','value':'1'}, {'date':'2008-04-01','value':'8'}, {'date':'2008-04-05','value':'3'}, {'date':'2009-04-19','value':'5'}, {'date':'2009-04-21','value':'8'},{'date':'2010-09-09','value':'3'},
{'date':'2010-09-10','value':'4'},
]
newList = []
newDict = {}
for cnt in range(len(myList)):
for k,v in myList[cnt].iteritems():
if k == 'date':
d = datetime.strptime(v,'%Y-%m-%d').date()
for elem in newList:
if elem['date'] != d:
newList.append({'date':d,'value':myList[cnt]['value']})
else:
newList[cnt]['value'] += myList[cnt]['value']
print newList
Ответы
Ответ 1
Сначала я сортировал данные 1:
>>> lst = [{'date':'2008-04-23','value':'1'},
... {'date':'2008-04-01','value':'8'},
... {'date':'2008-04-05','value':'3'},
... {'date':'2009-04-19','value':'5'},
... {'date':'2009-04-21','value':'8'},
... {'date':'2010-09-09','value':'3'},
... {'date':'2010-09-10','value':'4'},
... ]
>>> lst.sort(key=lambda x:x['date'][:7])
>>> lst
[{'date': '2008-04-23', 'value': '1'}, {'date': '2008-04-01', 'value': '8'}, {'date': '2008-04-05', 'value': '3'}, {'date': '2009-04-19', 'value': '5'}, {'date': '2009-04-21', 'value': '8'}, {'date': '2010-09-09', 'value': '3'}, {'date': '2010-09-10', 'value': '4'}]
Затем я использовал бы itertools.groupby
для группировки:
>>> from itertools import groupby
>>> for k,v in groupby(lst,key=lambda x:x['date'][:7]):
... print k, list(v)
...
2008-04 [{'date': '2008-04-23', 'value': '1'}, {'date': '2008-04-01', 'value': '8'}, {'date': '2008-04-05', 'value': '3'}]
2009-04 [{'date': '2009-04-19', 'value': '5'}, {'date': '2009-04-21', 'value': '8'}]
2010-09 [{'date': '2010-09-09', 'value': '3'}, {'date': '2010-09-10', 'value': '4'}]
>>>
Теперь, чтобы получить желаемый результат:
>>> for k,v in groupby(lst,key=lambda x:x['date'][:7]):
... print {'date':k+'-01','value':sum(int(d['value']) for d in v)}
...
{'date': '2008-04-01', 'value': 12}
{'date': '2009-04-01', 'value': 13}
{'date': '2010-09-01', 'value': 7}
1 Ваши данные на самом деле уже сортируются в этом отношении, поэтому вы можете пропустить этот шаг.
Ответ 2
Используйте itertools.groupby:
data = [{'date':'2008-04-23','value':'1'},
{'date':'2008-04-01','value':'8'},
{'date':'2008-04-05','value':'3'},
{'date':'2009-04-19','value':'5'},
{'date':'2009-04-21','value':'8'},
{'date':'2010-09-09','value':'3'},
{'date':'2010-09-10','value':'4'},
]
import itertools
key = lambda datum: datum['date'].rsplit('-', 1)[0]
data.sort(key=key)
result = [{
'date': key + '-01',
'value': sum(int(item['value']) for item in group)
} for key, group in itertools.groupby(data, key=key)]
print result
# [{'date': '2008-04-01', 'value': 12},
# {'date': '2009-04-01', 'value': 13},
# {'date': '2010-09-01', 'value': 7}]
Ответ 3
Принятый ответ правильный, но его временная сложность O (n lg n) из-за сортировки. Здесь (амортизированное) O (n) решение.
>>> L=[{'date':'2008-04-23','value':'1'},
... {'date':'2008-04-01','value':'8'},
... {'date':'2008-04-05','value':'3'},
... {'date':'2009-04-19','value':'5'},
... {'date':'2009-04-21','value':'8'},
... {'date':'2010-09-09','value':'3'},
... {'date':'2010-09-10','value':'4'},
... ]
Вот для чего создан Counter
:
>>> import collections
>>> value_by_month = collections.Counter()
>>> for d in L:
... value_by_month[d['date'][:7]+'-01'] += int(d['value'])
...
>>> value_by_month
Counter({'2009-04-01': 13, '2008-04-01': 12, '2010-09-01': 7})
И если ваш вывод должен быть объектом dict
:
>>> dict(value_by_month)
{'2008-04-01': 12, '2009-04-01': 13, '2010-09-01': 7}
Бонус: если вы хотите избежать импорта.
Сначала создайте dict month → list of values
. Функция setdefault
удобна для создания этого типа dict:
>>> values_by_month = {}
>>> for d in L:
... values_by_month.setdefault(d['date'][:7], []).append(int(d['value']))
...
>>> values_by_month
{'2008-04': [1, 8, 3], '2009-04': [5, 8], '2010-09': [3, 4]}
Во-вторых, суммируйте значения по месяцам и установите дату на первый день:
>>> [{'date':m+'-01', 'value':sum(vs)} for m, vs in values_by_month.items()]
[{'date': '2008-04-01', 'value': 12}, {'date': '2009-04-01', 'value': 13}, {'date': '2010-09-01', 'value': 7}]