匹配两个Python列表的长度

4
我有两个不同长度的Python列表。可以假设其中一个列表比另一个大多倍。
这两个列表包含相同的物理数据,但以不同的采样率捕获。
我的目标是对较大的信号进行下采样,使其具有与较小的信号完全相同的数据点。
我想出了以下代码,基本上完成了工作,但既不太符合Python的规范,也不能以高效的方式处理非常大的列表:
import math

a = [1,2,3,4,5,6,7,8,9,10]
b = [1,4.5,6.9]

if len(a) > len(b):
    div = int(math.floor(len(a)/len(b)))
    a = a[::div]
    diff = len(a)-len(b)
    a = a[:-diff]
else:
    div = int(math.floor(len(b)/len(a)))
    b = b[::div]
    diff = len(b)-len(a)
    b = b[:-diff]
print a
print b

我希望有更有经验的Python用户能够详细说明解决此任务的替代方法。非常感谢任何答案或评论。

请注意,仅仅将除法的地板向前推进一位,对于较长的列表会产生大量的累积误差!例如,如果一个列表有7个元素,而另一个列表只有4个元素,那么你只会取前四个元素,而不是每隔一个取一个的元素。 - tobias_k
@tobias_k 这是真的。然而在我的情况下,一个列表可能有80000个元素,而另一个列表只有300个。 - Rickson
4个回答

1
这是代码的缩短版本(性能不一定更好):
a = [1,2,3,4,5,6,7,8,9,10]
b = [1,4.5,6.9]
order = 0  # To determine a and b.

if len(b) > len(a):
    a, b = b, a  # swap the values so that 'a' is always larger.
    order = 1

div = len(a) / len(b)  # In Python2, this already gives the floor.
a = a[::div][:len(b)]

if order:
    print b
    print a
else:
    print a
    print b

由于您最终会丢弃较大列表的某些后续元素,因此显式使用for循环可能会提高性能,因为您不必“跳转”到将被丢弃的值:

new_a = []
jump = len(b)
index = 0
for i in range(jump):
    new_a.append(a[index])
    index += jump
a = new_a

1
你可以进一步简化为 a = a[::div][:len(b)]。如果我们从开头计数,就不需要新的 a 长度了。我认为这样更清晰明确。 - tobias_k
请注意,然而,OP的原始代码并没有交换变量...后面的代码可能需要进行调整,或者您应该再次交换。 - tobias_k
@tobias_k 对不起?我不明白。 - Sam Chats
OP仅对较大的列表进行“下采样”,而您的代码会对其中一个进行下采样,可能会交换列表。运行您的代码后,无法确定执行之前哪个列表是“a”哪个是“b”。 - tobias_k
1
@SamChats 是的,我建议去掉交换。你也可以像user2699提到的那样使用numpy数组而不是列表。感谢你的努力! - Rickson
显示剩余3条评论

1
首先,为了提高性能,您应该使用numpy。 问题已经标记了numpy,所以也许您已经在使用它,只是没有显示出来,但无论如何,可以使用以下代码将列表转换为numpy数组
import numpy as np
a = np.array(a)
b = np.array(b)

索引操作是一样的。可以在数组上使用len,但是array.shape更通用,下面是(非常相似的)代码。
 a[::a.shape[0] // b.shape[0]]

就性能而言,这应该为大多数数据提供了巨大的速度提升。 使用更大的a和b数组进行测试(分别为10e6和1e6个元素),显示numpy可以大幅提高性能。

a = np.ones(10000000)
b = np.ones(1000000)

%timeit a[::a.shape[0] // b.shape[0]]  # Numpy arrays
1000000 loops, best of 3: 348 ns per loop

a = list(a); 
b = list(b);
%timeit a[::len(a) // len(b)]    # Plain old python lists
1000000 loops, best of 3: 29.5 ms per loop

0

如果您正在迭代列表,您可以使用生成器,这样您就不必将整个列表复制到内存中。

from __future__ import division

a = [1,2,3,4,5,6,7,8,9,10]
b = [1,4.5,6.9]

def zip_downsample(a, b):
    if len(a) > len(b):
        b, a = a, b  # make b the longer list
    for i in xrange(len(a)):
        yield a[i], b[i * len(b) // len(a)]

for z in zip_downsample(a, b):
    print z

-2
#a = [1,2,3,4,5,6,7,8,9,10]
#b = [1,4.5,6.9]

a, b = zip(*zip(a, b))

# a = [1, 2, 3]
# b = [1, 4.5, 6.9]

内部zip将列表组合成一对,从较大的列表中丢弃多余的项,返回类似于[(1, 1), (2, 4.5), (3, 6.9)]的内容。外部zip执行其反向操作(因为我们使用*运算符进行解包),但由于我们已经通过第一个zip丢弃了多余的内容,所以列表应该具有相同的大小。这将返回[a, b],因此我们然后解包到各自的变量中(a,b = ...)。

请参见https://www.programiz.com/python-programming/methods/built-in/zip获取有关zip及其作为自身反向使用的更多信息。


1
a的预期输出为[1, 4, 7],b不需要计算。 - dansalmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接