【问题标题】:Flattening a list of NumPy arrays?展平 NumPy 数组列表?
【发布时间】:2016-02-16 04:05:31
【问题描述】:

看来我有 NumPy 数组列表格式的数据 (type() = np.ndarray):

[array([[ 0.00353654]]), array([[ 0.00353654]]), array([[ 0.00353654]]), 
array([[ 0.00353654]]), array([[ 0.00353654]]), array([[ 0.00353654]]), 
array([[ 0.00353654]]), array([[ 0.00353654]]), array([[ 0.00353654]]), 
array([[ 0.00353654]]), array([[ 0.00353654]]), array([[ 0.00353654]]),
array([[ 0.00353654]])]

我正在尝试将它放入一个 polyfit 函数中:

m1 = np.polyfit(x, y, deg=2)

但是,它返回错误:TypeError: expected 1D vector for x

我假设我需要将我的数据扁平化为:

[0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654 ...]

我尝试了一个通常适用于列表列表的列表理解,但正如预期的那样,这没有奏效:

[val for sublist in risks for val in sublist]

最好的方法是什么?

【问题讨论】:

  • @Divakar 谢谢!为我工作!
  • concatenate 假定所有数组的大小相同,您可能总是这样,否则请查看 stackoverflow.com/a/406822/1240268 之类的内容。
  • 数组的长度都一样吗?
  • 不确定是否重复但肯定相关@​​987654322@。

标签: python arrays numpy list-comprehension


【解决方案1】:

您可以使用numpy.concatenate,顾名思义,它基本上将此类输入列表的所有元素连接到单个 NumPy 数组中,就像这样 -

import numpy as np
out = np.concatenate(input_list).ravel()

如果你希望最终输出是一个列表,你可以像这样扩展解决方案 -

out = np.concatenate(input_list).ravel().tolist()

示例运行 -

In [24]: input_list
Out[24]: 
[array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]]),
 array([[ 0.00353654]])]

In [25]: np.concatenate(input_list).ravel()
Out[25]: 
array([ 0.00353654,  0.00353654,  0.00353654,  0.00353654,  0.00353654,
        0.00353654,  0.00353654,  0.00353654,  0.00353654,  0.00353654,
        0.00353654,  0.00353654,  0.00353654])

转换为列表-

In [26]: np.concatenate(input_list).ravel().tolist()
Out[26]: 
[0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654,
 0.00353654]

【讨论】:

  • 通过这样做,我得到ValueError: all the input array dimensions except for the concatenation axis must match exactly
  • @Athena 请发布一个新问题。不清楚数据格式到底是什么。
【解决方案2】:

也可以通过

np.array(list_of_arrays).flatten().tolist()

导致

[0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654]

更新

正如@aydow 在 cmets 中指出的那样,如果不关心获得copy or a view,使用numpy.ndarray.ravel 会更快

np.array(list_of_arrays).ravel()

虽然,根据docs

当需要在尽可能多的情况下查看视图时,arr.reshape(-1) 可能更可取。

换句话说

np.array(list_of_arrays).reshape(-1)

我的初步建议是使用numpy.ndarray.flattenreturns a copy every time会影响性能。

现在让我们看看上面列出的解决方案的时间复杂度如何比较使用perfplot 包进行类似于 OP 的设置

import perfplot

perfplot.show(
    setup=lambda n: np.random.rand(n, 2),
    kernels=[lambda a: a.ravel(),
             lambda a: a.flatten(),
             lambda a: a.reshape(-1)],
    labels=['ravel', 'flatten', 'reshape'],
    n_range=[2**k for k in range(16)],
    xlabel='N')

这里flatten 演示了分段线性复杂性,可以通过复制初始数组与返回视图的ravelreshape 的恒定复杂性进行比较来合理地解释这一点。

还值得注意的是,可以预见的是,将输出 .tolist() 转换为所有三个的性能均等线性。

【讨论】:

  • np.flatten 有效,但值得注意的是它比np.ravel 慢得多。随着array 长度的增加,这种差异会变得更糟
  • @aydow 嗯,怎么会这样? np.flatten 确实较慢但并不显着。我只是在list(map(np.array, np.random.rand(1_000_000, 10)))np.concatenate(list_of_arrays).ravel() 上使用%%timeit290 ms ± 2.49 ms 对抗np.array(list_of_arrays).flatten()446 ms ± 26.5 ms,在我的笔记本电脑上没有%%timeit 的情况下,两者似乎都是瞬间执行的。
  • 嗨@ayorgo,我与OP问题略有不同。我假设np.arrays 的np.array(这与我自己的问题有关)而不是np.arrays 的list。只使用np.ravel 需要249 ns ± 8.43 ns 而只使用np.flatten 需要25.4 ms ± 244 µs!!添加np.concatenatenp.array 会将其减慢到您提到的数字。很抱歉在我最初的评论中没有说明这一点
  • @aydow 哈哈,确实!我认为在性能上如此不同的是np.flatten 总是返回一个与'np.ravel' (stackoverflow.com/a/28930580/4755520) 不同的副本。有趣的是,接受的答案不需要使用np.concatenate。只需转换为np.array.ravel() 就足够了。
【解决方案3】:

另一种简单的方法是使用numpy.hstack(),然后使用squeeze() 删除单一维度,如下所示:

In [61]: np.hstack(list_of_arrs).squeeze()
Out[61]: 
array([0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654,
       0.00353654, 0.00353654, 0.00353654, 0.00353654, 0.00353654,
       0.00353654, 0.00353654, 0.00353654])

【讨论】:

    【解决方案4】:

    使用itertools 扁平化数组的另一种方式:

    import itertools
    
    # Recreating array from question
    a = [np.array([[0.00353654]])] * 13
    
    # Make an iterator to yield items of the flattened list and create a list from that iterator
    flattened = list(itertools.chain.from_iterable(a))
    

    这个解决方案应该非常快速,更多解释请参见https://stackoverflow.com/a/408281/5993892

    如果结果数据结构应该是 numpy 数组,请使用 numpy.fromiter() 将迭代器耗尽到数组中:

    # Make an iterator to yield items of the flattened list and create a numpy array from that iterator
    flattened_array = np.fromiter(itertools.chain.from_iterable(a), float)
    

    itertools.chain.from_iterable() 的文档: https://docs.python.org/3/library/itertools.html#itertools.chain.from_iterable

    numpy.fromiter() 的文档: https://docs.scipy.org/doc/numpy/reference/generated/numpy.fromiter.html

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,并找到了一个结合可变长度的一维 numpy 数组的解决方案:

      np.column_stack(input_list).ravel()
      

      请参阅numpy.column_stack 了解更多信息。

      带有示例数据的可变长度数组示例:

      In [135]: input_list
      Out[135]: 
      [array([[ 0.00353654,  0.00353654]]),
       array([[ 0.00353654]]),
       array([[ 0.00353654]]),
       array([[ 0.00353654,  0.00353654,  0.00353654]])]
      
      In [136]: [i.size for i in input_list]    # variable size arrays
      Out[136]: [2, 1, 1, 3]
      
      In [137]: np.column_stack(input_list).ravel()
      Out[137]: 
      array([ 0.00353654,  0.00353654,  0.00353654,  0.00353654,  0.00353654,
              0.00353654,  0.00353654])
      

      注意:仅在 Python 2.7.12 上测试

      【讨论】:

      • 我试过这个并得到ValueError: all the input array dimensions except for the concatenation axis must match exactly :(
      • 我能够使用np.hstack 而不是np.column_stack 使其工作。我认为这是因为我的数组是一维的,而且我没有仔细阅读原始问题。无论如何谢谢:)
      猜你喜欢
      • 2015-03-14
      • 2019-09-13
      • 1970-01-01
      • 1970-01-01
      • 2014-08-29
      • 2020-04-27
      • 2020-01-25
      • 2016-08-26
      • 2019-08-26
      相关资源
      最近更新 更多