如何在某些子列表的字段中汇总多个列表列表?

时间:2013-06-13 21:52:25

标签: python python-3.x

我有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工具可以帮助我解决这个问题?

6 个答案:

答案 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)

为了说明为什么使用正确的数据结构会让事情变得更容易......

假设abc实际上是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)}

肯定有更紧凑,也许是高性能的选择,但这种方法的优点(我相信!)是它看起来确实可读。