Python - 使用优化的for循环将数组拆分为两个

时间:2013-11-26 19:45:49

标签: python optimization for-loop numpy iterator

这是我发布的here问题的后续问题,但这是一个非常不同的问题,所以我想我会单独发布。

我有一个Python脚本,它读取一个非常大的数组,我需要优化每个元素的操作(参见引用的SO问题)。我现在需要将输出数组拆分为两个独立的数组。

我有代码:

output = [True if (len(element_in_array) % 2) else False for element_in_array in master_list]

输出一个长度为len(master_list)的数组,由True或False组成,具体取决于element_in_array的长度是奇数还是偶数。我的问题是我需要将master_list拆分为两个数组:一个数组包含与element_in_array中的True元素对应的output,另一个包含element_in_array元素1}}对应False中的output元素。

这可以通过append这样的传统数组运算符来实现,但我需要尽可能优化和尽可能快。我的master_list中有数百万个元素,所以有没有办法在不直接循环master_list并使用append创建两个新数组的情况下完成此操作。

任何建议都将不胜感激。 谢谢!

3 个答案:

答案 0 :(得分:0)

您可以使用itertools.compress

>>> from itertools import compress, imap
>>> import operator
>>> lis = range(10)
>>> output = [random.choice([True, False]) for _ in xrange(10)]
>>> output
[True, True, False, False, False, False, False, False, False, False]
>>> truthy = list(compress(lis, output))
>>> truthy
[0, 1]
>>> falsy = list(compress(lis, imap(operator.not_,output)))
>>> falsy
[2, 3, 4, 5, 6, 7, 8, 9]

如果您想要更快的解决方案,请转到NumPy,此外它还允许我们根据布尔数组进行数组过滤:

>>> import numpy as np
>>> a = np.random.random(10)*10
>>> a
array([ 2.94518349,  0.09536957,  8.74605883,  4.05063779,  2.11192606,
        2.24215582,  7.02203768,  2.1267423 ,  7.6526713 ,  3.81429322])
>>> output = np.array([True, True, False, False, False, False, False, False, False, False])
>>> a[output]
array([ 2.94518349,  0.09536957])
>>> a[~output]
array([ 8.74605883,  4.05063779,  2.11192606,  2.24215582,  7.02203768,
        2.1267423 ,  7.6526713 ,  3.81429322])

时间比较:

>>> lis = range(1000)
>>> output = [random.choice([True, False]) for _ in xrange(1000)]
>>> a = np.random.random(1000)*100
>>> output_n = np.array(output)
>>> %timeit list(compress(lis, output))
10000 loops, best of 3: 44.9 us per loop
>>> %timeit a[output_n]
10000 loops, best of 3: 20.9 us per loop
>>> %timeit list(compress(lis, imap(operator.not_,output)))
1000 loops, best of 3: 150 us per loop
>>> %timeit a[~output_n]
10000 loops, best of 3: 28.7 us per loop

答案 1 :(得分:0)

如果您可以使用NumPy,这将更加简单。并且,作为奖励,它也会快得多,并且它将使用更少的内存来存储您的巨型阵列。例如:

>>> import numpy as np
>>> import random
>>> # create an array of 1000 arrays of length 1-1000
>>> a = np.array([np.random.random(random.randint(1, 1000))
                  for _ in range(1000)])
>>> lengths = np.vectorize(len)(a)
>>> even_flags = lengths % 2 == 0
>>> evens, odds = a[even_flags], a[~even_flags]
>>> len(evens), len(odds)
(502, 498)

答案 2 :(得分:0)

您可以尝试使用groupby中的itertools功能。关键功能是确定元素长度是否均匀的函数。由groupby返回的迭代器由键值元组组成,其中key是键函数返回的值(此处为TrueFalse),值为一系列项目所有股 同样的关键。创建一个字典,将键函数返回的值映射到列表,并且可以使用初始迭代器中的一组值扩展相应的列表。

trues = []
falses = []
d = { True: trues, False: falses }
def has_even_length(element_in_array):
    return len(element_in_array) % 2 == 0

for k, v in itertools.groupby(master_list, has_even_length):
   d[k].extend(v)

groupby的文档说您通常希望确保列表按键功能返回的相同键排序。在这种情况下,可以保持未分类;你将只有groupby返回的迭代器返回的东西,因为序列中可能存在许多交替的真/假集。