【问题标题】:Python: What is an efficient way to sort a nested array by repeated values?Python:按重复值对嵌套数组进行排序的有效方法是什么?
【发布时间】:2016-08-05 09:19:12
【问题描述】:
  • data 是一个列表,其中每个条目是一个浮点数列表

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


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) 约为 200 万
L 约为 8000。

我需要一种方法来理想地加快速度!

【问题讨论】:

  • 您能否添加一些输入和预期输出,因为我不遵循您的逻辑?使用像 _ 这样的东西作为你实际使用的变量名也无济于事
  • 只是一个简短的提示:下划线_ 通常表示迭代中的一次性变量。您可能需要重命名它,因为它可能会混淆阅读您的代码的其他人。
  • 在任何情况下:你一遍又一遍地迭代data,但你可能会注意到给定一个元素seq的数据,如果int(seq[0]) == i那么该元素将完全不匹配i 的不同值的其他迭代!您可以考虑从c = [[] for _ in range(L)] 开始,然后执行:for el in data: c[int(el)].append(el)。唯一的问题是,如果你有 int(seq[0]) > L 的值,你的代码会“忽略”它们,而这个给出的是 IndexError
  • 您是否尝试通过整数类型转换bucket-sort您的浮点值?
  • 或者:将c 更改为defaultdict(list),然后执行:for seq in data: c[int(seq[0])].append(seq) 这应该独立于seq[0] 中的值。

标签: python performance list nested repeat


【解决方案1】:

优化尝试

假设您想根据每个子列表的第一个值将子列表排序到 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 000160 亿)比较。此外,您在创建时完全填充您的存储桶列表,而不是重复使用 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 中落入当前存储桶的子列表的位置。最后,收集bucket列表中的列表引用并存储。

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

lidx = ...
buck.append(lidx)

但是,仅在包含大数据的代码中优化性能是有局限性的。

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

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

【讨论】:

    【解决方案2】:

    在 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-09
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 1970-01-01
      • 2021-08-28
      • 2017-03-21
      • 1970-01-01
      相关资源
      最近更新 更多