Python: 在嵌套列表中相加列表值

0 投票
4 回答
2461 浏览
提问于 2025-04-18 13:47

我有一个列表,里面包含了很多小列表,像这样:

[[12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.15, 0.1, 0.15, 0.2, 0.1, 0.15, 0.15, 0.15, 0.15], [12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]], 等等]

如果一个小列表的前两个元素和另一个小列表的前两个元素相同(就像上面的例子),我想写一个函数,把剩下的值相加,并把它们合并成一个列表。比如,输出结果应该像这样:

[12411.0, 31937, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.25, 0.2, 0.25, 0.3, 0.2, 0.25, 0.25, 0.25, 0.25]

我在告诉Python如何先识别和比较这两个元素,然后再合并它们时遇到了困难。以下是我目前的最佳尝试:

def group(A):
for i in range(len(A)):
    for j in range(len(A[i])):
        if A[i][0:1] == A[i: ][0:1]:
            return [A[i][0], A[i][1], sum(A[i][j+2], A[i: ][j+2])]

我认为我遇到了索引错误,因为代码中的 A[i: ] 和 A[i: ][j+2] 部分。我不知道该如何用Python来表达,让函数去添加任何符合条件的行。

4 个回答

1

如果你喜欢使用 itertools,只需稍微动动脑筋,就能通过玩弄 groupbyisliceizipimapchain 来轻松解决这个问题。

当然,你还应该记得使用 operator.itemgetter

实现

# Create a group of lists where the key (the first two elements of the lists) matches
groups = groupby(sorted(l, key = itemgetter(0, 1)), key = itemgetter(0, 1))
# zip the lists and then chop of the first two elements. Sum the elements of the resultant list
# Remember to add the newly accumulated list with the first two elements
groups_sum = ([k, imap(sum, islice(izip(*g), 2, None))] for k, g in groups )
# Reformat the final list to match the output format
[list(chain.from_iterable(elem)) for elem in groups_sum]

实现(如果你喜欢一行代码)

[list(chain.from_iterable([k, imap(sum, islice(izip(*g), 2, None))]))
  for k, g in groupby(sorted(l, key = itemgetter(0, 1)), key = itemgetter(0, 1))]

示例输入

l = [[10,20,0.1,0.2,0.3,0.4],
     [11,22,0.1,0.2,0.3,0.4],
     [10,20,0.1,0.2,0.3,0.4],
     [11,22,0.1,0.2,0.3,0.4],
     [20,30,0.1,0.2,0.3,0.4],
     [10,20,0.1,0.2,0.3,0.4]]

示例输出

[[10, 20, 0.3, 0.6, 0.9, 1.2],
 [11, 22, 0.2, 0.4, 0.6, 0.8],
 [20, 30, 0.1, 0.2, 0.3, 0.4]]

分析

groups = groupby(sorted(l, key = itemgetter(0, 1)), key = itemgetter(0, 1))
# After grouping, similar lists gets clustered together
[((10, 20),
  [[10, 20, 0.1, 0.2, 0.3, 0.4],
   [10, 20, 0.1, 0.2, 0.3, 0.4],
   [10, 20, 0.1, 0.2, 0.3, 0.4]]),
 ((11, 22), [[11, 22, 0.1, 0.2, 0.3, 0.4], [11, 22, 0.1, 0.2, 0.3, 0.4]]),
 ((20, 30), [[20, 30, 0.1, 0.2, 0.3, 0.4]])]

groups_sum = ([k, imap(sum, islice(izip(*g), 2, None))] for k, g in groups )
# Each group is accumulated from the second element onwards
[[(10, 20), [0.3, 0.6, 0.9, 1.2]],
 [(11, 22), [0.2, 0.4, 0.6, 0.8]],
 [(20, 30), [0.1, 0.2, 0.3, 0.4]]]

[list(chain.from_iterable(elem)) for elem in groups_sum]
# Now its just a matter of representing in the output format
[[10, 20, 0.3, 0.6, 0.9, 1.2],
 [11, 22, 0.2, 0.4, 0.6, 0.8],
 [20, 30, 0.1, 0.2, 0.3, 0.4]]
3

这里有一个函数,可以把所有子列表中前两个元素相同的合并在一起。它还可以处理子列表长度不一样的情况:

from itertools import izip_longest

l = [[1,3,4,5,6], [1,3,2,2,2], [2,3,5,6,6], [1,1,1,1,1], [1,1,2,2,2], [1,3,6,2,1,1,2]]
l2 = [[12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.15, 0.1, 0.15, 0.2, 0.1,  0.15, 0.15, 0.15, 0.15], [12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]

def merge(l):
    d = {}
    for ent in l:
        key = tuple(ent[0:2])
        merged = d.get(key, None)
        if merged is None:
            d[key] = ent
        else:
            merged[2:] = [a+b for a,b in izip_longest(merged[2:], ent[2:], fillvalue=0)]
    return d.values()

print merge(l)
print merge(l2)

输出结果:

[[1, 3, 12, 9, 9, 1, 2], [2, 3, 5, 6, 6], [1, 1, 3, 3, 3]]
[[12411.0, 31937.0, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.25, 0.2, 0.25, 0.30000000000000004, 0.2, 0.25, 0.25, 0.25, 0.25]]

这个函数的实现方式是使用一个字典,字典的键是子列表的前两个元素(以元组的形式存储)。当我们遍历这些子列表时,会检查字典里是否已经有这个键。如果没有,就把当前的子列表存进去。如果已经有了这个键,就把从第三个元素开始的所有值加起来,然后更新字典。等我们遍历完所有子列表后,就可以把字典里的所有值返回出来。

3

这是一个实现方法:

>>> a_list = [[12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.15, 0.1, 0.15, 0.2, 0.1, 0.15, 0.15, 0.15, 0.15], [12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]
>>> result = [a + b for a, b in zip(*a_list)]
>>> result[:2] = a_list[0][:2]
>>> result
[12411.0, 31937.0, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.25, 0.2, 0.25, 0.30000000000000004, 0.2, 0.25, 0.25, 0.25, 0.25]

这个方法通过简单地把所有子列表中对应的元素加起来来工作,具体做法是:

[a + b for a, b in zip(*a_list)]

然后根据题目的要求,重新写结果中的前两个元素,因为这两个元素是不会改变的,具体做法是:

result[:2] = a_list[0][:2]

从你的问题中并不清楚,如果子列表的前两个元素不匹配,应该怎么处理。不过,下面这段代码可以帮助你检查子列表的前两个元素是否匹配。假设 a_list 包含的子列表的前两个元素不匹配:

>>> a_list = [[12411.0, 31937.0, 0.1, 0.1], [12411.3, 31937.0, 0.1, 0.1]]

那么,这个条件:

all([True if list(a)[1:] == list(a)[:-1] else False for a in list(zip(*a_list))[:2]])

将返回 False。如果匹配则返回 True。这段代码提取了所有子列表的第一个元素和第二个元素,然后检查它们是否相等。

你可以把上面的检查加入到你的代码中,并根据预期的行为进行相应的修改。

总结一下:

a_list = [[12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.15, 0.1, 0.15, 0.2, 0.1, 0.15, 0.15, 0.15, 0.15], [12411.0, 31937.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]
check = all([True if list(a)[1:] == list(a)[:-1] else False for a in list(zip(*a_list))[:2]])
result = []
if check:
    result = [a + b for a, b in zip(*a_list)]
    result[:2] = a_list[0][:2]
else:
    # whatever the behavior should be.
1

这是一个函数,它会接收一个包含多个列表的列表A,然后根据你的标准检查里面的列表ij。如果这两个列表的前两个元素匹配,它就会返回你想要的总和列表;如果不匹配,它就会返回None

def check_internal_ij(A,i,j):
    """ checks internal list i against internal list j """ 
    if A[i][0:2] == A[j][0:2]:
        new = [x+y for x,y in zip( A[i], A[j] )]
        new[0:2] = A[i][0:2]
        return new
    else:
        return None

接着,你可以对你想检查的所有内部列表组合运行这个函数。

撰写回答