关于python:展平列表和标量列表

flatten list of lists and scalars

对于矩阵,我们有numpy.flatten()这样的方法

1
np.array([[1,2,3],[4,5,6],[7,8,9]]).flatten()

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

如果我想从np.array([[1,2,3],[4,5,6],7])[1,2,3,4,5,6,7]怎么办?是否有一个现有的函数执行类似的操作?


对于不均匀的列表,数组是一个对象数据类型(和1d,因此Flatten不会更改它)。

1
2
3
4
5
6
In [96]: arr=np.array([[1,2,3],[4,5,6],7])
In [97]: arr
Out[97]: array([[1, 2, 3], [4, 5, 6], 7], dtype=object)
In [98]: arr.sum()
...
TypeError: can only concatenate list (not"int") to list

7元素出现问题。如果我将其更改为列表:

1
2
3
In [99]: arr=np.array([[1,2,3],[4,5,6],[7]])
In [100]: arr.sum()
Out[100]: [1, 2, 3, 4, 5, 6, 7]

我在耍花招。数组列表和列表[1,2,3]+[4,5]的元素是串联的。

基本点是对象数组不是二维数组。在许多方面,它更像一个列表。

最好的压扁者是chain

1
2
In [104]: list(itertools.chain(*arr))
Out[104]: [1, 2, 3, 4, 5, 6, 7]

虽然它也会扼杀整数7版本。

连接和定位

如果数组是一个列表列表(不是列表和标量的原始组合),那么np.concatenate工作。它在对象上迭代,就像它是一个列表一样。

对于混合原始列表,concatenate不起作用,但hstack起作用。

1
2
3
4
5
6
In [178]: arr=np.array([[1,2,3],[4,5,6],7])
In [179]: np.concatenate(arr)
...
ValueError: all the input arrays must have same number of dimensions
In [180]: np.hstack(arr)
Out[180]: array([1, 2, 3, 4, 5, 6, 7])

这是因为hstack首先遍历列表,并确保所有元素都是atleast_1d。这个额外的迭代使它更加健壮,但以处理速度为代价。

时间测试

1
2
3
4
5
6
7
In [170]: big1=arr.repeat(1000)
In [171]: timeit big1.sum()
10 loops, best of 3: 31.6 ms per loop
In [172]: timeit list(itertools.chain(*big1))
1000 loops, best of 3: 433 μs per loop
In [173]: timeit np.concatenate(big1)
100 loops, best of 3: 5.05 ms per loop

双倍的尺寸

1
2
3
4
5
6
7
8
9
In [174]: big1=arr.repeat(2000)
In [175]: timeit big1.sum()
10 loops, best of 3: 128 ms per loop
In [176]: timeit list(itertools.chain(*big1))
1000 loops, best of 3: 803 μs per loop
In [177]: timeit np.concatenate(big1)
100 loops, best of 3: 9.93 ms per loop
In [182]: timeit np.hstack(big1)    # the extra iteration hurts hstack speed
10 loops, best of 3: 43.1 ms per loop

sum的大小是二次的。

1
2
3
res=[]
for e in bigarr:
   res += e

res随着e的增加而增长,所以每个迭代步骤都更昂贵。

chain是最好的。


可以使用yield编写自定义展平函数:

1
2
3
4
5
6
def flatten(arr):
    for i in arr:
        try:
            yield from flatten(i)
        except TypeError:
            yield i

用法示例:

1
2
3
4
>>> myarr = np.array([[1,2,3],[4,5,6],7])
>>> newarr = list(flatten(myarr))
>>> newarr
[1, 2, 3, 4, 5, 6, 7]


您可以在这里使用apply_along_axis

1
2
3
>>> arr = np.array([[1,2,3],[4,5,6],[7]])
>>> np.apply_along_axis(np.concatenate, 0, arr)
array([1, 2, 3, 4, 5, 6, 7])

作为奖励,这也不是列表数量的二次方。