Python:通过重复值对嵌套数组进行排序的有效方法是什么?

时间:2016-08-05 09:19:13

标签: python performance list nested repeat

  • data是一个列表,其中每个条目都是一个浮动列表

  • L是检查_data的第一个条目是否相等的范围,如果是,则将其存储在c <中的该索引处/ p>

c = []
d = []
for i in range(L):
    for seq in data:
        if int(seq[0]) == i:
            d.append(seq)
    c.append(d)
    d = []
return c
>>> data = [[4.0, 0.0, 15.0, 67.0], [3.0, 0.0, 15.0, 72.0], [4.0, 0.0, 15.0, 70.0], [1.0, -0.0, 15.0, 90.0], [3.0, -0.0, 15.0, 75.0], [2.0, -0.0, 15.0, 83.0], [3.0, 0.0, 15.0, 74.0], [4.0, 0.0, 15.0, 69.0], [4.0, 0.0, 14.0, 61.0], [3.0, 0.0, 15.0, 74.0], [3.0, 0.0, 15.0, 75.0], [4.0, 0.0, 15.0, 67.0], [5.0, 0.0, 14.0, 45.0], [6.0, 0.0, 13.0, 30.0], [3.0, 0.0, 15.0, 74.0], [4.0, 0.0, 15.0, 55.0], [7.0, 0.0, 13.0, 22.0], [6.0, 0.0, 13.0, 25.0], [1.0, -0.0, 15.0, 83.0], [7.0, 0.0, 13.0, 18.0]]
>>> sort(data,7)
[[], [[1.0, -0.0, 15.0, 90.0], [1.0, -0.0, 15.0, 83.0]], [[2.0, -0.0, 15.0, 83.0]], [[3.0, 0.0, 15.0, 72.0], [3.0, -0.0, 15.0, 75.0], [3.0, 0.0, 15.0, 74.0], [3.0, 0.0, 15.0, 74.0], [3.0, 0.0, 15.0, 75.0], [3.0, 0.0, 15.0, 74.0]], [[4.0, 0.0, 15.0, 67.0], [4.0, 0.0, 15.0, 70.0], [4.0, 0.0, 15.0, 69.0], [4.0, 0.0, 14.0, 61.0], [4.0, 0.0, 15.0, 67.0], [4.0, 0.0, 15.0, 55.0]], [[5.0, 0.0, 14.0, 45.0]], [[6.0, 0.0, 13.0, 30.0], [6.0, 0.0, 13.0, 25.0]]]

len(data)大约为2百万 L大约是8000。

我需要一种方法来理想地提高速度!

2 个答案:

答案 0 :(得分:0)

优化尝试

假设您要根据每个子列表的第一个值将子列表分类为 buckets

为简单起见,我使用以下内容生成随机数进行测试:

L = 10
data = [[round(random.random() * 10.0, 2) for _ in range(3)] for _ in range(10)]

首先关于你的代码,只是为了确保我的意图正确。

c = []
d = []
for i in range(L): # Loop over all buckets
    for e in data: # Loop over entire data
        if int(e[0]) == i: # If first float of sublist falls into i-th bucket
            d.append(e) # Append entire sublist to current bucket
    c.append(d) # Append current bucket to list of buckets
    d = [] # Reset

这是低效的,因为您遍历每个存储桶的完整数据集。如果您拥有8000存储桶和2 000 000浮动列表,那么您将基本上执行16 000 000 000 160亿)比较。此外,您可以在创建时完全填充存储桶列表,而不是重复使用data变量中的现有列表。因此,这会产生尽可能多的数据引用副本。

因此,您应该考虑使用数据的索引,例如

bidx = [int(e[0]) for e in data] # Calculate bucket indices for all sublists
buck = []
for i in range(L): # Loop over all buckets
    lidx = [k for k, b in enumerate(bidx) if b == i] # Get sublist indices for this bucket
    buck.append([data[l] for l in lidx]) # Collect list references
print(buck)

这应该导致对您的数据进行单次迭代,就地计算存储桶索引。然后,只对你的所有存储桶执行一次迭代,其中从bidx收集相应的存储区索引(你已经有这个双循环,但这可能会快一些) - 导致lidx保持data中属于当前存储桶的子列表的位置。最后,收集桶列表中的列表引用并存储它。

最后一步可能代价高昂,因为它包含大量的参考复制。您应该考虑仅在每个存储桶中存储索引,而不是整个数据,例如

lidx = ...
buck.append(lidx)

但是,仅在具有大数据的代码中优化性能具有限制。

如果您的数据很大,所有线性迭代都会很昂贵。您可以尝试尽可能地减少它们,但是数据大小本身定义了较低的上限!

如果必须执行数百万条记录的更多操作,则应考虑更改为其他数据表示形式或格式。例如,如果您需要在一个脚本中执行频繁操作,您可能需要考虑树(例如b树)。如果要将其存储以供进一步处理,您可能需要考虑具有适当索引的数据库。

答案 1 :(得分:0)

在Python 3中运行使用此算法,我获得的性能比jbndlr高2个数量级:

rl = range(L)   # Generate the range list
buck = [[] for _ in rl]     # Create all the buckets
for seq in data:  # Loop over entire data
    try:
        idx = rl.index(int(seq[0]))   # Find the bucket index
        buck[idx].append(seq)     # Append current data in its bucket
    except ValueError:
        pass    # There is no bucket for that value 

将算法与:

进行比较
L = 1000
data = [[round(random.random() * 1200.0, 2) for _ in range(3)] for _ in range(100000)]

我明白了:

yours: 26.66 sec
jbndlr: 6.78 sec
mine: 0.07 sec