【问题标题】:reshaping numpy array into small subslices [duplicate]将numpy数组重塑为小子切片[重复]
【发布时间】:2020-12-05 03:11:20
【问题描述】:

我在 numpy 中有一个数组,如下所示:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

我想这样重塑它:

[[1,2,3],
[2,3,4],
[3,4,5],
[4,5,6],
[6,7,8],
[7,8,9],
[8,9,10]]

最有效的方法是什么?

我目前正在做的是一个使用 np.append 的循环,但这需要很长时间。

谢谢

【问题讨论】:

    标签: python numpy numpy-ndarray


    【解决方案1】:

    在列表上进行 3 克迭代的一种方法是使用 zip

    a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    np.array([i for i in zip(a,a[1:],a[2:])])
    
    array([[ 1,  2,  3],
           [ 2,  3,  4],
           [ 3,  4,  5],
           [ 4,  5,  6],
           [ 5,  6,  7],
           [ 6,  7,  8],
           [ 7,  8,  9],
           [ 8,  9, 10]])
    

    解决 n-gram 迭代的一般函数可以使用以下 -

    def find_ngrams(input_list, n):
        return np.array(list(zip(*[input_list[i:] for i in range(n)])))
    
    find_ngrams(a, 3) #try setting n to other values like 2 or 4 or 5
    
    array([[ 1,  2,  3],
           [ 2,  3,  4],
           [ 3,  4,  5],
           [ 4,  5,  6],
           [ 5,  6,  7],
           [ 6,  7,  8],
           [ 7,  8,  9],
           [ 8,  9, 10]])
    
    find_ngrams(a, 5)
    
    array([[ 1,  2,  3,  4,  5],
           [ 2,  3,  4,  5,  6],
           [ 3,  4,  5,  6,  7],
           [ 4,  5,  6,  7,  8],
           [ 5,  6,  7,  8,  9],
           [ 6,  7,  8,  9, 10]])
    

    【讨论】:

      【解决方案2】:

      您可以使用 numpy stride 技巧 (numpy.lib.stride_tricks.as_strided) 来创建数组的新视图。这将比任何列表理解都快,因为没有数据被复制。使用步幅技巧的IPython Cookbook has more examples

      import numpy as np
      
      a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
      bytes_per_item = a.dtype.itemsize
      b = np.lib.stride_tricks.as_strided(
          a, shape=(8, 3), strides=(bytes_per_item, bytes_per_item))
      
      array([[ 1,  2,  3],
             [ 2,  3,  4],
             [ 3,  4,  5],
             [ 4,  5,  6],
             [ 5,  6,  7],
             [ 6,  7,  8],
             [ 7,  8,  9],
             [ 8,  9, 10]])
      

      定时测试

      这个答案比这里使用循环的答案快几个数量级。找到下面的测试(在 Jupyter Notebook 中使用 %timeit 魔法完成)。请注意,其中一个函数不适用于 numpy 数组,需要 Python 列表。

      设置

      import numpy as np
      
      a = np.arange(1, 100001, dtype=np.int64)
      a_list = a.tolist()
      
      def jakub(a, shape):
          a = np.asarray(a)
          bytes_per_item = a.dtype.itemsize
          # The docs for this function recommend setting `writeable=False` to
          # prevent modifying the underlying array.
          return np.lib.stride_tricks.as_strided(
              a, shape=shape, strides=(bytes_per_item, bytes_per_item), writeable=False)
      
      # https://stackoverflow.com/a/63426256/5666087
      def daveldito(arr):
          return np.array([arr[each:each+2]+[arr[each+2]] for each in range(len(arr)-2)])
      
      # https://stackoverflow.com/a/63426205/5666087
      def akshay_sehgal(a):
          return np.array([i for i in zip(a,a[1:],a[2:])])
      

      结果

      %timeit jakub(a, shape=(a.shape[0]-2, 3))
      8.85 µs ± 425 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
      
      %timeit daveldito(a_list)
      141 ms ± 8.94 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
      
      %timeit akshay_sehgal(a)
      168 ms ± 9.43 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
      

      【讨论】:

        【解决方案3】:

        我会这样做(请注意,我仅依赖于基本的列表理解):

        arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        np.array([arr[each:each+2]+[arr[each+2]] for each in range(len(arr)-2)])
        

        输出:

        array([[ 1,  2,  3],
               [ 2,  3,  4],
               [ 3,  4,  5],
               [ 4,  5,  6],
               [ 5,  6,  7],
               [ 6,  7,  8],
               [ 7,  8,  9],
               [ 8,  9, 10]])
        

        至于性能,对于 arr 中的 100_000 元素,我 2016 年末的 MacBook Pro 给出了以下时间统计数据:

        CPU 时间:用户 148 毫秒,系统:26.1 毫秒,总计:174 毫秒 挂墙时间:186 毫秒

        【讨论】:

          【解决方案4】:

          np.lib.stride_tricks.as_strided 的另一个解决方案:

          import numpy as np
          x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
          
          newshape = x.shape[:-1] + (x.shape[-1] - 3 + 1, 3)
          a = np.lib.stride_tricks.as_strided(x, shape=newshape, strides=x.strides + (x.strides[-1],))
          

          返回

          array([[ 1,  2,  3],
                 [ 2,  3,  4],
                 [ 3,  4,  5],
                 [ 4,  5,  6],
                 [ 5,  6,  7],
                 [ 6,  7,  8],
                 [ 7,  8,  9],
                 [ 8,  9, 10]])
          

          您可以阅读有关该主题的更多信息,例如 here

          【讨论】:

          • 你可以使用strides=x.strides + x.strides[-1:],这样你就不需要那么多括号了。
          猜你喜欢
          • 2021-11-18
          • 2020-07-11
          • 1970-01-01
          • 2018-03-17
          • 2023-02-13
          • 2017-09-18
          • 2020-04-22
          • 1970-01-01
          • 2021-09-02
          相关资源
          最近更新 更多