4

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

4

6 回答 6

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]]  

不要评判我。我真的不会写那样的代码。

于 2013-06-13T21:58:23.260 回答
3

为了说明为什么使用正确的数据结构会使事情变得容易得多……</p>

假设a, b, andc实际上是dicts,而你的数字实际上是ints 而不是strs。毕竟,a 的重点dict是按名称查找内容,而 an 的重点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]}

还有一点改进的空间——你可以使用 adefaultdict来摆脱它if not k in result:——但即使只有新手级别的东西,它也非常紧凑和简单。


但是,如果你将这些列表作为输入——你希望最后有很好的 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:]]。然后整个事情是相同模式的 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
于 2013-06-13T22:28:24.727 回答
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']]
于 2013-06-13T21:59:12.697 回答
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)]

或者,您可以将其推广到一起处理所有三个列表,甚至是任意数量的列表——<code>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(element[2] for element in elements)] result.append(result_element) 返回结果

或者,您可以使其适用于 0 个或更多数字而不是恰好两个,或者不依赖于排序等。当您一步一步走得足够远时,您最终会得到类似于其他两个答案之一的结果。

于 2013-06-13T22:05:59.093 回答
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. 如果它们需要太多时间才能被理解,你应该避免使用单行词。

于 2013-06-13T22:02:46.443 回答
1

由于您的状态any result type被接受,因此这是一个返回字典的表单,我认为这是此类工作的合适返回类型:

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)}

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

于 2013-06-13T22:26:03.437 回答