Карта списка кортежей в словарь, python - PullRequest
4 голосов
/ 06 февраля 2010

У меня есть список кортежей, извлеченных из таблицы в БД, которая выглядит следующим образом ( key , foreignkey , value ). Между ключом и иностранными ключами существует отношение много к одному, и я хотел бы преобразовать его в dict, проиндексированный иностранным ключом, содержащий сумму всех значений с этим иностранным ключом, то есть { foreignkey , сумма ( значение ) }. Я написал нечто довольно многословное:

myDict = {}
for item in myTupleList:
    if item[1] in myDict:
        myDict [ item[1] ] += item[2]
    else:
        myDict [ item[1] ] = item[2]

но после просмотра ответа на этот вопрос или этих двух должен быть более краткий способ выразить то, что я хотел бы сделать. И если это будет повторение, я пропустил его и сниму вопрос, если сможете предоставить ссылку.

Ответы [ 5 ]

8 голосов
/ 06 февраля 2010

Предполагая, что все ваши значения int с, вы можете использовать defaultdict, чтобы сделать это проще:

from collections import defaultdict

myDict = defaultdict(int)

for item in myTupleList:
    myDict[item[1]] += item[2]

defaultdict похоже на словарь, за исключением того, что при попытке получить ключ, которого там нет, он заполняет значение, возвращаемое вызываемым объектом - в этом случае int, который возвращает 0 при вызове без аргументов .

ОБНОВЛЕНИЕ: Спасибо @ gnibbler за напоминание, но кортежи могут быть распакованы в цикле for:

from collections import defaultdict

myDict = defaultdict(int)

for _, key, val in myTupleList:
    myDict[key] += val

Здесь кортеж из 3 элементов распаковывается в переменные _, key и val. _ - это распространенное имя-заполнитель в Python, которое используется для обозначения того, что значение не очень важно. Используя это, мы можем избежать индексации item[1] и item[2]. Мы не можем полагаться на это, если кортежи в myTupleList не все одинакового размера, но я уверен, что они есть.

(Мы также избегаем ситуации, когда кто-то смотрит на код и думает, что он сломан, потому что писатель считал, что массивы были проиндексированы на 1 единицу, что я и думал, когда впервые читал код. прочитайте вопрос. Однако в приведенном выше цикле очевидно, что myTupleList - это кортеж из трех элементов, и нам просто не нужен первый.)

5 голосов
/ 06 февраля 2010
from collections import defaultdict

myDict = defaultdict(int)

for _, key, value in myTupleList:
    myDict[key] += value
4 голосов
/ 06 февраля 2010

Вот мой (язык в щеке) ответ:

myDict = reduce(lambda d, t: (d.__setitem__(t[1], d.get(t[1], 0) + t[2]), d)[1], myTupleList, {})

Это уродливо и плохо, но вот как это работает.

Первый аргумент для уменьшения (потому что там неясно) это lambda d, t: (d.__setitem__(t[1], d.get(t[1], 0) + t[2]), d)[1]. Я расскажу об этом позже, а сейчас я просто назову это joe (не обижай никого по имени Джо). Функция сокращения в основном работает так:

 joe(joe(joe({}, myTupleList[0]), myTupleList[1]), myTupleList[2])

И это для списка из трех элементов. Как вы можете видеть, он в основном использует свой первый аргумент для сортировки каждого результата в окончательный ответ. В этом случае окончательный ответ - это словарь, который вы хотели.

Теперь для самого joe. Вот joe как def:

def joe(myDict, tupleItem):
   myDict[tupleItem[1]] = myDict.get(tupleItem[1], 0) + tupleItem[2]
   return myDict

К сожалению, никакие формы = или return не разрешены в Python lambda, поэтому их нужно обойти. Я обошел проблему отсутствия =, вызвав функцию dict s __setitem__ напрямую. Я обошёл проблему отсутствия возврата, создав кортеж с возвращаемым значением __setitem__ и словарем, а затем вернул элемент кортежа, содержащий словарь. Я медленно изменю joe, чтобы вы могли видеть, как я это сделал.

Сначала удалите =:

def joe(myDict, tupleItem):
   # Using __setitem__ to avoid using '='
   myDict.__setitem__(tupleItem[1], myDict.get(tupleItem[1], 0) + tupleItem[2])
   return myDict

Далее, приведите полное выражение к значению, которое мы хотим вернуть:

def joe(myDict, tupleItem):
   return (myDict.__setitem__(tupleItem[1], myDict.get(tupleItem[1], 0) + tupleItem[2]),
           myDict)[1]

Я много раз сталкивался с этим вариантом использования для reduce и dict в моем программировании на Python. На мой взгляд, dict может использовать функцию-член reduceto(keyfunc, reduce_func, iterable, default_val=None). keyfunc будет брать текущее значение из итерируемого и возвращать ключ. reduce_func будет принимать существующее значение в словаре и значение из итерируемого и возвращать новое значение для словаря. default_val будет тем, что было передано в reduce_func, если в словаре отсутствует ключ. Возвращаемым значением должен быть сам словарь, чтобы вы могли делать такие вещи:

myDict = dict().reduceto(lambda t: t[1], lambda o, t: o + t, myTupleList, 0)
0 голосов
/ 06 февраля 2010

Посмотрите на SQLAlchemy и посмотрите, выполняет ли это все необходимые преобразования и, возможно, больше

0 голосов
/ 06 февраля 2010

Возможно не совсем читабельно, но должно работать:

fks = dict([ (v[1], True) for v in myTupleList ]).keys()
myDict = dict([ (fk, sum([ v[2] for v in myTupleList if v[1] == fk ])) for fk in fks ])

Первая строка находит все уникальные внешние ключи. Вторая строка создает ваш словарь, сначала создав список пар (fk, sum (все значения для этого fk)) - и превратив его в словарь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...