将NumPy数组列表展平?

100

看起来我有以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 谢谢!对我有用! - Jerry Zhang
2
concatenate假设所有的数组大小都相同,这对你来说可能总是成立的,否则可以查看类似于https://dev59.com/7XRC5IYBdhLWcg3wG9To#406822的内容。 - Andy Hayden
这些数组长度是否相同? - Martin Thoma
不确定是否重复,但肯定相关 https://dev59.com/fV4b5IYBdhLWcg3wlilP。 - ayorgo
5个回答

113

你可以使用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]

1
通过这样做,我得到了“ValueError:除了连接轴以外的所有输入数组维度必须完全匹配”的错误。 - Athena
2
@Athena 请发布一个新问题。不清楚数据格式是什么。 - Divakar
@Athena 我认为我遇到了同样的问题:这是因为列表中的数组具有不同的形状。我能够使用以下代码获取一个扁平化的数组:np.concatenate(input_list, axis=None).ravel() - user2561747

18

也可以通过{{某种方式}}完成

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在评论中指出的那样,如果不关心复制或视图,使用numpy.ndarray.ravel可能会更快。

np.array(list_of_arrays).ravel()

尽管如此,根据文档所述

当希望在尽可能多的情况下使用视图时,arr.reshape(-1)可能更可取。

换句话说

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

我的最初建议是使用numpy.ndarray.flatten,但它会每次返回一个副本,影响性能。
现在让我们看看上述解决方案的时间复杂度如何比较,使用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')

enter image description here

在这里,{{flatten}}演示了分段线性复杂度,可以通过它对初始数组进行复制来合理解释,而{{ravel}}和{{reshape}}的常数复杂度则返回一个视图。
值得注意的是,将输出转换为{{.tolist()}}可以使三者的性能均匀化为线性。

np.flatten 可以使用,但值得注意的是它比 np.ravel 慢得多。随着数组长度的增加,这种差异会变得更糟。 - aydow
@aydow 嗯,为什么呢?np.flatten的确比较慢,但并不显著。我刚才在 list(map(np.array, np.random.rand(1_000_000, 10))) 上分别使用了 %%timeit 进行测试,np.concatenate(list_of_arrays).ravel() 花费了 290ms ± 2.49 ms,而 np.array(list_of_arrays).flatten() 花费了 446ms ± 26.5 ms,两者都在我的笔记本电脑上顷刻间完成了操作。 - ayorgo
嗨@ayorgo,我偏离了原始问题。我假设有一个np.array的数组(这与我的问题相关),而不是np.array的列表。仅使用np.ravel需要249 ns ± 8.43 ns,而仅使用np.flatten需要25.4 ms ± 244 µs!添加np.concatenatenp.array会使它变慢到你提到的数字。抱歉我在最初的评论中没有说明这一点。 - aydow
@aydow 哈哈,确实!我认为在性能方面的不同之处在于 np.flatten 总是返回副本,而不像 'np.ravel' (https://dev59.com/fV4b5IYBdhLWcg3wlilP#28930580)。有趣的事情也是,被接受的答案不需要使用 np.concatenate。只需转换为 np.array 并使用 .ravel() 即可。 - ayorgo

5

使用 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://dev59.com/7XRC5IYBdhLWcg3wG9To#408281

如果生成的数据结构应该是一个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/zh-cn/3/library/itertools.html#itertools.chain.from_iterable

numpy.fromiter()文档: https://numpy.org/doc/stable/reference/generated/numpy.fromiter.html


5
另一种简单的方法是使用 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])

3

我遇到了同样的问题,并找到了一个解决方案,它将不定长度的1维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的错误 :( - Shir
我使用np.hstack而不是np.column_stack成功地解决了问题。我认为这是因为我的数组是1维的,而且我没有仔细阅读原始问题。无论如何,还是谢谢 :) - Shir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接