2013-06-13 3 views
3

У меня есть 3 списка списков.Как суммировать несколько списков списков в определенных полях списка?

Поле подписок 1 - это имя, поле 2 - это число, а поле 3 - это число. Этот формат всегда один и тот же и не меняется. В трех списках всегда одинаковые имена; однако, порядок может отличаться от.

a = [['jane', '1', '120'], ['bob', '3', '35'], ['joe', '5', '70']] 
b = [['bob', '1', '12'], ['jane', '2', '240'], ['joe', '1', '100']] 
c = [['joe', '2', '30'], ['jane', '5', '45'], ['bob', '0', '0']] 

Я хотел бы результат (любой тип объекта) с суммой полей 2 & 3 из подсписков в списках.

result = [['jane', '8', '405'], ['bob', '4', '47'], ['joe', '8', '200']] 

В псевдо Python3 коде, я предполагаю, что бы выглядеть, но я не могу понять, правильный способ сделать это в Python3. Не говоря уже о том, чтобы сделать это по-питоновски:

def sum_f2_f3(list_a, list_b) 
    where element[0] in list_a.sub_list == element[0] in list_b.sub_list: 
     x = element[0] 
     result[x:1] = list_a.sub_list[0:1] + list_b.sub_list[0:1] 
     result[x:2] = list_a.sub_list[0:2] + list_b.sub_list[0:2] 
    return result 

result = sum_f2_f3(sum_f2_f3(a,b), c) 

Любые идеи? Какие встроенные инструменты Python могут мне помочь?

+2

Маленькая точка, все ваши поля - это строки. –

+0

@BurhanKhalid Да, вы правы ... Я должен был сказать, что в под списках есть «имя», «номер», «номер». – svenglar

+0

У вас есть '' '' '' '' '' '' jane' в 'примере' вашего результата вашего примера. Кроме того, как и в общем комментарии, я чувствую, что вложенные 'list' должны быть объектами. Это может сделать ваши списки понятными более читабельными. (Имена атрибутов вместо таинственных индексов.) – jpmc26

ответ

2

Чтобы проиллюстрировать, почему использование правильных структур данных делает вещи намного проще ...

Допустим, что a, b и c были фактически dict s, и ваши номера были на самом деле int s вместо str s. В конце концов, весь смысл dict - это искать вещи по имени, а вся точка int - это сделать арифметику. Итак:

a = {'jane': [1, 120], 'bob': [3, 35], 'joe': [5, 70]} 
b = {'bob': [1, 12], 'jane': [2, 240], 'joe': [1, 100]} 
c = {'joe': [2, 30], 'jane': [5, 45], 'bob': [0, 0]} 

Теперь, все, что вам нужно сделать, это:

result = {} 
for d in a, b, c: 
    for k, v in d.items(): 
     if not k in result: 
      result[k] = [0, 0] 
     result[k][0] += v[0] 
     result[k][1] += v[1] 

И результат:

{'bob': [4, 47], 'jane': [8, 405], 'joe': [8, 200]} 

Там еще немного возможностей для улучшения, вы можете использовать a defaultdict, чтобы избавиться от бит if not k in result:, но даже с новичком, это довольно компактно и просто.


Но что, если вы получили эти списки как вход-you'd хотел бы иметь хорошую dicts в конце концов, но не начать там?

Вы можете написать функцию, чтобы преобразовать их, как это:

def convert(list_of_lists): 
    result = {} 
    for element in list_of_lists: 
     key = element[0] 
     values = [] 
     for value in element[1:] 
      values.append(int(value)) 
     result[key] = values 
    return result 

И если вы заметили знакомый values = []… for value in … values.append(…) шаблон, вы можете превратить это в простой список понимания [int(value) for value in element[1:]]. И тогда все это ДИКТ эквивалент той же схеме, так что вы можете сократить все это до:

return {element[0]: [int(value) for value in element[1:]] for element in list_of_lists} 

Между тем, если вам нужно преобразовать обратно в первоначальную форму, это просто:

def unconvert(dict_of_lists): 
    result = [] 
    for key, values in dict_of_lists.items(): 
     element = [key] + [str(value) for value in values] 
     result.append(element) 
    return result 
+0

Мне нравится этот! – svenglar

+0

@svenglar: Просто имейте в виду, что он не использует те же типы данных, с которых вы начали, и он не дает вам результатов в желаемом формате. Но, даже если это важно, возможно, стоит написать код для конвертирования туда и обратно и все еще делать это с помощью dicts. – abarnert

+0

Я принимаю это как ответ, хотя я знаю, что изначально я никогда не упоминал диктов. Спасибо всем за все дополнительные ответы, хотя они действительно помогли мне понять проблему. – svenglar

3

Это, кажется, дает то, что вы хотите, используя больше понятий pythonic list.

>>> [[e[0][0], sum(int(r[1]) for r in e), sum(int(r[2]) for r in e)] 
     for e in zip(a, b, c)] 
[['jane', 8, 405], ['bob', 4, 47], ['joe', 8, 200]] 

Если вы хотите работать с из имен заказа, вы могли бы сделать что-то вроде этого

>>> from itertools import groupby 
>>> [[name] + 
     reduce(
      lambda a, b: [int(c) + int(d) for (c,d) in zip(a, b)], 
      [r[1:] for r in records]) 
     for name, records 
     in groupby(
      sorted(r for l in [a, b, c] for r in l), 
      lambda r: r[0]) 
    ] 

[['bob', 4, 47], ['jane', 8, 405], ['joe', 8, 200]] 

Не судите меня. На самом деле я не пишу такой код.

+0

Спасибо, это дает мне желаемый результат. Теперь, если бы я мог понять/прочитать его :) – svenglar

+1

@svenglar: Мой ответ пытается показать вам шаг за шагом, откуда вы начали этот ответ (что, вероятно, там, где вы хотите закончить или, по крайней мере, закрыть к нему). – abarnert

2

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

>>> from itertools import chain 
>>> a = [['jane', '1', '120'], ['bob', '3', '35'], ['joe', '5', '70']] 
>>> b = [['bob', '1', '12'], ['jane', '2', '240'], ['joe', '1', '100']] 
>>> c = [['joe', '2', '30'], ['jane', '5', '45'], ['bob', '0', '0']] 

for k in chain(a,b,c): 
    if k[0] not in dic: 
     dic[k[0]] = [ int(x) for x in k[1:]] 
    else: 
     dic[k[0]] = [x + int(y) for x,y in zip(dic[k[0]], k[1:])] 

>>> [ [k]+[str(x) for x in v] for k,v in dic.items()] 
[['joe', '8', '200'], ['jane', '8', '405'], ['bob', '4', '47']] 
+0

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

+0

@svenglar, тогда решение на основе dict будет работать нормально. –

+0

Предположим, у меня был дикт, где имя - это индекс. Было бы лучше иметь 2 значения в списке? Может ли dict иметь индекс с более чем одним значением? – svenglar

1

Еще раз, список понимание будет делать трюк:

l = [a, b, c] 
result =[ [e[0], sum([int(ls[id][1]) for ls in l]), 
sum([int(ls[id][2]) for ls in l]) ] for id, e in enumerate(l[0])] 

Но не стоит забывать, что Дзен Python говорит: Readability counts. Вы должны избегать однострочных линий, если им требуется слишком много времени для понимания.

2

ответ рекурсивный является самым компактным разумным способом сделать это, и Ashwini Чаудхари является наиболее гибким и концептуально приятным ... но если вы хотите знать, как сделать свою почти код работу:

Ключом, что вы «Отсутствует» «Как перебирать два списка в режиме блокировки?» Именно для этого и есть zip. Просто заархивировать два списка вместе, и вы получите это: (. За исключением это итератор, а не список)

[(['jane', '1', '120'], ['jane', '2', '240']), 
(['bob', '3', '35'], ['bob', '1', '12']), 
(['joe', '5', '70'], ['joe', '1', '100'])] 


Остальная часть кода довольно много работ, за исключением того, что вы получили немного запутался в именах. Вот скопированная версия.

def sum_f2_f3(list_a, list_b): 
    result = [] 
    for element_a, element_b in zip(list_a, list_b): 
     result_element = [element_a[0], 
          element_a[1] + element_b[1], 
          element_a[2] + element_b[2]] 
     result.append(result_element) 
    return result 

result = sum_f2_f3(sum_f2_f3(a,b), c) 

Кроме того, что вы подведение кучи строк.Это совершенно законно, но то, что он получает вас это:

[['jane', '125', '12024045'], 
['bob', '310', '35120'], 
['joe', '512', '7010030']] 

Вы, вероятно, хотел, чтобы преобразовать эти значения в int в какой-то момент. Если нет, если вы хотите преобразовать в int, сумму, и преобразовать обратно в str, это довольно тривиальным:

def sum_f2_f3(list_a, list_b): 
    result = [] 
    for element_a, element_b in zip(list_a, list_b): 
     result_element = [element_a[0], 
          str(int(element_a[1]) + int(element_b[1])), 
          str(int(element_a[2]) + int(element_b[2]))] 
     result.append(result_element) 
    return result 

После того, как вы получили это, есть несколько способов, вы можете улучшить его ,

Например, вы всегда можете заменить results = [], for петлю и result.append(…) со списком понимания, как только вы осознаете, что вы попали в точную картину постижение для:

def sum_f2_f3(list_a, list_b): 
    return [[element_a[0], 
      element_a[1] + element_b[1], 
      element_a[2] + element_b[2]] 
      for elementa, element_b in zip(list_a, list_b)] 

Или, вы можете обобщить его на работу на всех трех списков вместе, или даже любое количество lists- zip уже делает это, и вы можете просто заменить + с sum:

def sum_f_lists(*lists): 
    results = [] 
    for elements in zip(*lists): 
     result_element = [elements[0][0], 
          sum(element[1] for element in elements), 

суммы (элемент [2] для элемента элементов)] result.append (result_element) возвращаемого результат

Или вы можете заставить его работать на 0 или более цифр, а не ровно два, или не зависит от заказа, и т. д. Когда вы делаете это достаточно далеко, шаг за шагом, вы получите что-то вроде одного из двух других ответов.

+0

Спасибо за подробное объяснение! Мой вопрос - это лишь часть более крупного проекта, поэтому вы идете, помогая мне оптимизировать весь проект, а не только это суммирование проблем с списками. – svenglar

1

Поскольку вы заявляете any result type принимаются, здесь есть форма, которая возвращает Dict, который я думаю, это подходящий тип возвращаемого значения для такого рода работы:

a = [['jane', '1', '120'], ['bob', '3', '35'], ['joe', '5', '70']] 
b = [['jane', '2', '240'], ['bob', '1', '12'], ['joe', '1', '100']] 
c = [['jane', '5', '45'], ['bob', '0', '0'], ['joe', '2', '30']] 

def summation(*args): 
    d = {} 
    for name, v1, v2 in [item for sublist in args for item in sublist]: 
     v1, v2 = int(v1), int(v2) 
     try: 
      d[name] = (d[name][0]+v1, d[name][1]+v2) 
     except KeyError: 
      d[name] = (v1, v2) 

    return d 

print summation(a,b,c) 

ВОЗВРАТА

{'jane': (8, 405), 'bob': (4, 47), 'joe': (8, 200)} 

Там в определенно быть более компактными и, возможно, перформантными вариантами, но плюс к этому подходу (я верю!) заключается в том, что он кажется читаемым.

Смежные вопросы