【问题标题】:faster way to append value更快的附加价值的方法
【发布时间】:2012-05-26 04:13:52
【问题描述】:

假设我有一个很大的浮点值列表,我只想选择其中一些查看另一个数组:

result = []
for x,s in zip(xlist, slist):
    if f(s): result.append(x)

在循环开始时,我可以粗略估计有多少条目将通过f 选择

现在这很慢,我尝试将 list 更改为 array 但仅查看附加我会变慢

def f(v):
    for ii in a: v.append(ii)
a = range(int(1E7))
v = []
t = time(); f(v); print time()-t # -> 1.3
v = array.array('i')
t = time(); f(v); print time()-t # -> 3.4

我需要更快,因为这个循环在我的程序中真的很慢。 numpy.array 可以帮助我吗?没有append 方法。

【问题讨论】:

    标签: python optimization numpy append


    【解决方案1】:

    可能有更好的 numpy 解决方案,但在纯 python 中你可以尝试迭代器:

    from itertools import izip
    
    xlist = [1,2,3,4,5,6,7,8]
    slist = [0,1,0,1,0,0,0,1]
    
    def f(n):
        return n
    
    results = (x for x,s in izip(xlist, slist) if f(s))
    
    # results is an iterator--you don't have values yet
    # and no extra memory is consumed
    # you can retrieve results one by one with iteration
    # or you can exhaust all values and store in a list
    
    assert list(results)==[2,4,8]
    
    # you can use an array too
    # import array
    # a = array.array('i', results)
    

    您还可以将此方法与 numpy 数组结合使用,看看它是否更快。请参阅fromiter constructor

    但是,如果您可以重组代码以使用迭代器,则可以避免生成完整列表,从而完全避免使用 append

    不言而喻,您应该看看是否可以加快您的 f() 过滤函数,因为它会为每个元素调用一次。

    【讨论】:

      【解决方案2】:

      根据您问题中的第一句话,您希望根据另一个列表或数组中的值来选择值。

      在 numpy 中,您可以使用索引从数组中获取选定的值。我在示例中使用Boolean indexing。这避免了将值附加到现有数组的需要,但会为您提供所选值的副本作为数组。 您可以使用&| 运算符、来自numpy 的logic functions 或您自己的函数来组合多个条件。

      In [1]: import numpy as np
      
      In [2]: size = int(1E7)
      
      In [3]: ar = np.arange(size)
      
      In [4]: ar2 = np.random.randint(100, size=size)
      
      In [5]: %timeit ar[(ar2 > 50) & (ar2 < 70) | (ar2 == 42)]
      10 loops, best of 3: 249 ms per loop
      

      如果您需要根据不同条件(或评论中给出的范围)在单独的数组中进行每个选择,您可以执行以下操作:

      conditions = [(10, 20), (20, 50)] # min, max as tuples in a list
      results = {}
      for condition in conditions:
          selection = ar[(ar2 > condition[0]) & (ar2 < condition[1])]
          # do something with the selection ?
          results[condition] = selection
      print results
      

      会给你类似的东西

      {(20, 50): array([      2,       6,       7, ..., 9999993, 9999997, 9999998]),
       (10, 20): array([      1,       3,      66, ..., 9999961, 9999980, 9999999])}
      

      一般来说,您应该避免循环遍历一个 numpy 数组,而是使用向量化函数来操作您的数组。

      【讨论】:

      • 好的,这听起来不错。假设现在我有很多选择。如果值通过selection1,则放入ar1,如果通过selection2,则放入ar2,...。选择类似于10 &lt; x &lt;= 2020 &lt; x &lt;= 50,...
      • @wiso 我改编了这个例子。
      • 谢谢,但这不是我在评论中提出的问题。作为输出,我需要不同的集合,一个用于10&lt;x&lt;=20,第二个用于20&lt;x&lt;=50,...
      • @wiso 希望你现在就问这个问题
      • 是的,您的解决方案很简单,关键是您必须为每个选择循环数据,这不是最佳的
      【解决方案3】:

      尝试双端队列:http://docs.python.org/library/collections.html#collections.deque

      来自 python 文档:

      双端队列是堆栈和队列的概括(名称发音为“deck”,是“双端队列”的缩写)。双端队列支持线程安全、内存高效的从双端队列的任一侧追加和弹出,在任一方向上的 O(1) 性能大致相同。

      虽然列表对象支持类似的操作,但它们针对快速固定长度操作进行了优化,并且会为 pop(0) 和 insert(0, v) 操作带来 O(n) 的内存移动成本,这些操作会同时改变列表的大小和位置底层数据表示。

      在我的系统上(由于内存有限,我使用 1e6 范围):

      def f(v):
          for ii in a: v.append(ii)
      a = range(int(1E6))
      v = []
      t = time(); f(v); print time()-t # -> .12
      v = array.array('i')
      t = time(); f(v); print time()-t # -> .25
      v = collections.deque()
      t = time(); f(v); print time()-t # -> .11
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-29
        • 2014-07-22
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多