#list #python
#Список #python
Вопрос:
У меня есть следующие данные списка.
data = [['2009-01-20', 3000.0], ['2011-03-01', 6000.0], ['2008-12-15',
6000.0], ['2002-02-15', 6000.0], ['2009-04-20', 6000.0], ['2010-08-01',
4170.0], ['2002-07-15', 6000.0], ['2008-08-15', 6000.0], ['2010-12-01',
6000.0], ['2011-02-01', 8107.0], ['2011-04-01', 8400.0], ['2011-05-15',
9000.0], ['2010-05-01', 6960.0], ['2005-12-15', 6000.0], ['2010-10-01',
6263.0], ['2011-06-02', 3000.0], ['2010-11-01', 4170.0], ['2009-09-25',
6000.0]]
где первый аргумент — date, а второй аргумент — total.
я хочу получить результат, используя группу по месяцам и годам из приведенного выше списка.
т. е. результат хотел бы:
--> for month: [['JAN',tot1],['FEB',tot2],['MAR',tot3] ...]
--> for year: [['2002',tot1],['2005',tot2],['2008',tot3] ...]
Ответ №1:
from collections import defaultdict
yeartotal = defaultdict(float)
monthtotal = defaultdict(float)
for s in data:
d = s[0].split('-')
yeartotal[d[0]] = s[1]
monthtotal[d[1]] = s[1]
In [37]: [item for item in yeartotal.iteritems()]
Out[37]:
[('2002', 12000.0),
('2005', 6000.0),
('2008', 12000.0),
('2009', 15000.0),
('2011', 34507.0),
('2010', 27563.0)]
In [38]: [item for item in monthtotal.iteritems()]
Out[38]:
[('02', 14107.0),
('03', 6000.0),
('12', 18000.0),
('06', 3000.0),
('07', 6000.0),
('04', 14400.0),
('05', 15960.0),
('08', 10170.0),
('09', 6000.0),
('01', 3000.0),
('11', 4170.0),
('10', 6263.0)]
Комментарии:
1. 1, хотя почему не коллекции. Счетчик вместо defaultdict?
2. Хорошее наблюдение. Без причины;
defaultdict
это просто то, к чему я привык.
Ответ №2:
Во-первых, давайте преобразуем данные в более удобную форму. Мы будем использовать модуль datetime для обработки этих дат.
>>> trans = lambda row: (datetime.datetime.strptime(row[0], "%Y-%m-%d"), row[1])
>>> tdata = map(trans, data)
Далее, функция (по одной для каждой из двух групповых операций), которая суммирует значение в dict с соответствующей группой.
>>> def mker(left, right):
... result = dict(left)
... mo = right[0].strftime('%b')
... result[mo] = right[1] left.get(mo, 0)
... return result
...
>>> def yker(left, right):
... result = dict(left)
... mo = right[0].strftime('%Y')
... result[mo] = right[1] left.get(mo, 0)
... return result
...
Наконец, мы применяем эти функции ядра к данным с помощью reduce()
>>> reduce(mker, tdata, {})
{'Apr': 14400.0,
'Aug': 10170.0,
'Dec': 18000.0,
'Feb': 14107.0,
'Jan': 3000.0,
'Jul': 6000.0,
'Jun': 3000.0,
'Mar': 6000.0,
'May': 15960.0,
'Nov': 4170.0,
'Oct': 6263.0,
'Sep': 6000.0}
>>> reduce(yker, tdata, {})
{'2002': 12000.0,
'2005': 6000.0,
'2008': 12000.0,
'2009': 15000.0,
'2010': 27563.0,
'2011': 34507.0}
Ответ №3:
риффинг к ответу Стива:
>>> data = [['2009-01-20', 3000.0], ['2011-03-01', 6000.0], ['2008-12-15',
... 6000.0], ['2002-02-15', 6000.0], ['2009-04-20', 6000.0], ['2010-08-01',
... 4170.0], ['2002-07-15', 6000.0], ['2008-08-15', 6000.0], ['2010-12-01',
... 6000.0], ['2011-02-01', 8107.0], ['2011-04-01', 8400.0], ['2011-05-15',
... 9000.0], ['2010-05-01', 6960.0], ['2005-12-15', 6000.0], ['2010-10-01',
... 6263.0], ['2011-06-02', 3000.0], ['2010-11-01', 4170.0], ['2009-09-25',
... 6000.0]]
>>> monthtotal = defaultdict(float)
>>> months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL',
... 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
>>> for s in data:
... monthtotal[months[int(s[0].split('-')[1]) - 1]] = s[1]
...
>>> monthtotal
defaultdict(<type 'float'>, {'MAR': 6000.0, 'FEB': 14107.0, 'AUG': 10170.0, 'SEP': 6000.0, 'APR': 14400.0, 'JUN': 3000.0, 'JUL': 6000.0, 'JAN': 3000.0, 'MAY': 15960.0, 'NOV': 4170.0, 'DEC': 18000.0, 'OCT': 6263.0})
Ответ №4:
Еще одно решение без коллекций:
from datetime import datetime
getdate = lambda strd: (datetime.strptime(strd, '%Y-%m-%d').strftime('%Y-%b').split('-'))
data = [['2009-01-20', 3000.0], ['2011-03-01', 6000.0], ['2008-12-15',
6000.0], ['2002-02-15', 6000.0], ['2009-04-20', 6000.0], ['2010-08-01',
4170.0], ['2002-07-15', 6000.0], ['2008-08-15', 6000.0], ['2010-12-01',
6000.0], ['2011-02-01', 8107.0], ['2011-04-01', 8400.0], ['2011-05-15',
9000.0], ['2010-05-01', 6960.0], ['2005-12-15', 6000.0], ['2010-10-01',
6263.0], ['2011-06-02', 3000.0], ['2010-11-01', 4170.0], ['2009-09-25',
6000.0]]
yeartotal = {}
monthtotal = {}
for dateVal, total in map(lambda sdata: (getdate(sdata[0]), sdata[1]), data):
if dateVal[0] not in yeartotal:
yeartotal[dateVal[0]] = 0
if dateVal[1] not in monthtotal:
monthtotal[dateVal[1]] = 0
yeartotal[dateVal[0]] = total
monthtotal[dateVal[1]] = total
Ответ №5:
Вот другое решение, использующее numpy.
Во-первых, нам нужно изменить форму данных, чтобы они выглядели немного как матрица. мы будем использовать dict по умолчанию с годами в качестве ключей и списками с плавающей точкой в качестве значений.
>>> pre_matrix = collections.defaultdict(lambda:[0]*12)
>>> for row in tdata:
... pre_matrix[row[0].year][row[0].month - 1] = row[1]
...
Поскольку нам не нужен массив a, содержащий каждый год, начиная с Common Era, давайте рассмотрим предварительно отформатированные данные и извлекем минимальный и максимальный годы.
>>> r = range(min(pre_matrix.keys()),1 max(pre_matrix.keys()))
Наконец, постройте матрицу, каждая строка которой содержит данные за один год.
>>> matrix = numpy.array([pre_matrix[y] for y in r])
Оттуда просто получить суммы строк и столбцов. мы будем использовать zip()
, чтобы вернуть интересные значения даты обратно.
>>> zip((datetime.datetime(1970, i 1, 1).strftime("%b"), s) for i, s in enumerate(matrix.sum(0)))
[(('Jan', 3000.0),),
(('Feb', 14107.0),),
(('Mar', 6000.0),),
(('Apr', 14400.0),),
(('May', 15960.0),),
(('Jun', 3000.0),),
(('Jul', 6000.0),),
(('Aug', 10170.0),),
(('Sep', 6000.0),),
(('Oct', 6263.0),),
(('Nov', 4170.0),),
(('Dec', 18000.0),)]
Поскольку нам не нужно локализовывать годы, это немного проще.
>>> list(zip(r, matrix.sum(1)))
[(2002, 12000.0),
(2003, 0.0),
(2004, 0.0),
(2005, 6000.0),
(2006, 0.0),
(2007, 0.0),
(2008, 12000.0),
(2009, 15000.0),
(2010, 27563.0),
(2011, 34507.0)]