我有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工具可以帮助我解决这个问题?
答案 0 :(得分:3)
这似乎可以使用更多的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]]
不要评判我。我真的不会写那样的代码。
答案 1 :(得分:3)
为了说明为什么使用正确的数据结构会让事情变得更容易......
假设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:
位 - 但即使只是新手级别的东西,这也非常紧凑和简单。
但是如果你把这些列表作为输入怎么办 - 你最终会喜欢有好的词汇,但是你不能从那里开始呢?
您可以编写一个函数来转换它们,如下所示:
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:]]
。然后整个事情就是相同模式的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
答案 2 :(得分: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']]
答案 3 :(得分:2)
递归的答案是最紧凑的合理方式,而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
,求和,并转换回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)]
或者,您可以将其推广到一起使用所有三个列表,甚至可以使用任意数量的列表 - 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或更多的数字,而不是正好两个,或者不依赖于订购等。当你走得足够远时,一步一步,你最终会得到像两个中的一个其他答案。
答案 4 :(得分: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
。如果他们需要太多的时间来理解,你应该避免使用单行。
答案 5 :(得分: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)
RETURNS
{'jane': (8, 405), 'bob': (4, 47), 'joe': (8, 200)}
肯定有更紧凑,也许是高性能的选择,但这种方法的优点(我相信!)是它看起来确实可读。