[英]How do I sum multiple lists-of-lists on certain sub-list's fields?
我有3個列表清單。
子列表'字段1是名稱,字段2是數字,字段3是數字。 此格式始終相同,不會更改。 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中執行它的正確方法。 更別說以Pythonic方式做了:
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工具可以幫助我解決這個問題?
這似乎使用更多pythonic列表推導給你想要的東西。
>>> [[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]]
不要評判我。 我真的不是那樣寫代碼的。
為了說明為什么使用正確的數據結構會讓事情變得更容易......
假設a
, b
和c
實際上是dict
s,你的數字實際上是int
而不是str
。 畢竟, 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]}
還有一些改進的余地 - 你可以使用defaultdict
來消除結果中的if not k in result:
bit-but即使只是新手級別的東西,這也非常緊湊和簡單。
但是,如果你把這些列表作為輸入怎么辦 - 你最終會喜歡有好的詞匯,但是你不能從那里開始呢?
您可以編寫一個函數來轉換它們,如下所示:
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(…)
模式中發現了熟悉的values = []… for value in … values.append(…)
,則可以將其轉換[int(value) for value in element[1:]]
的簡單列表解析[int(value) for value in element[1:]]
。 然后整個事情就是相同模式的dict,所以你可以把它全部減少到:
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
使用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']]
Recursive的答案是最緊湊的合理方式,Ashwini Chaudhary是最靈活,最概念最好的......但如果你想知道如何使你的代碼幾乎完成:
你缺少的關鍵是“如何在鎖定步驟中迭代兩個列表?” 這正是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
,sum,並轉換回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),
sum(元素中元素的元素[2])] result.append(result_element)返回結果
或者你可以讓它適用於0或更多的數字而不是正好兩個,或者不依賴於排序等。當你走得足夠遠時,一步一步,你會得到類似於其他兩個答案之一的東西。
列表理解將再一次成功:
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的Zen所說的內容: Readability counts
。 如果他們需要太多的時間來理解,你應該避免單行。
既然你聲明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)}
肯定有更緊湊,也許是高性能的選擇,但這種方法的優點(我相信!)是它看起來確實可讀。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.