在NumPy中逐列生长矩阵

Bor*_*lik 20 python arrays numpy

在纯Python中,您可以非常轻松地逐列增长矩阵:

data = []
for i in something:
    newColumn = getColumnDataAsList(i)
    data.append(newColumn)
Run Code Online (Sandbox Code Playgroud)

NumPy的数组没有追加功能.该hstack函数不适用于零大小的数组,因此以下方法不起作用:

data = numpy.array([])
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    data = numpy.hstack((data, newColumn)) # ValueError: arrays must have same number of dimensions
Run Code Online (Sandbox Code Playgroud)

所以,我的选择是要么在适当条件下删除initalization iside循环:

data = None
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    if data is None:
        data = newColumn
    else:
        data = numpy.hstack((data, newColumn)) # works
Run Code Online (Sandbox Code Playgroud)

...或者使用Python列表并稍后转换为数组:

data = []
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    data.append(newColumn)
data = numpy.array(data)
Run Code Online (Sandbox Code Playgroud)

两种变体看起来都有点尴尬.有更好的解决方案吗?

dou*_*oug 18

NumPy实际上确实有一个追加函数,它似乎可以做你想要的,例如,

import numpy as NP
my_data = NP.random.random_integers(0, 9, 9).reshape(3, 3)
new_col = NP.array((5, 5, 5)).reshape(3, 1)
res = NP.append(my_data, new_col, axis=1)
Run Code Online (Sandbox Code Playgroud)

如果添加另一行,您的第二个片段(hstack)将起作用,例如,

my_data = NP.random.random_integers(0, 9, 16).reshape(4, 4)
# the line to add--does not depend on array dimensions
new_col = NP.zeros_like(my_data[:,-1]).reshape(-1, 1)
res = NP.hstack((my_data, new_col))
Run Code Online (Sandbox Code Playgroud)

hstack给出了相同的结果concatenate((my_data, new_col), axis=1),我不确定他们如何比较性能.


虽然这是你问题的最直接的答案,但我应该提到循环通过数据源来通过追加来填充目标,而在python中就好了,不是惯用的NumPy.原因如下:

初始化NumPy数组是相对昂贵的,并且使用这种传统的python模式,在每次循环迭代时会产生或多或少的成本(即,每个附加到NumPy数组大致就像初始化具有不同大小的新数组).

出于这个原因,NumPy中用于将列迭代添加到2D数组的常见模式是初始化空目标数组一次(或预先分配具有所有空列的单个2D NumPy数组),依次填充这些空列.设置所需的列式偏移量(索引) - 更容易显示而不是解释:

>>> # initialize your skeleton array using 'empty' for lowest-memory footprint 
>>> M = NP.empty(shape=(10, 5), dtype=float)

>>> # create a small function to mimic step-wise populating this empty 2D array:
>>> fnx = lambda v : NP.random.randint(0, 10, v)
Run Code Online (Sandbox Code Playgroud)

像在OP中一样填充NumPy数组,除了每次迭代只是在连续的列向偏移处重新设置M的值

>>> for index, itm in enumerate(range(5)):    
        M[:,index] = fnx(10)

>>> M
  array([[ 1.,  7.,  0.,  8.,  7.],
         [ 9.,  0.,  6.,  9.,  4.],
         [ 2.,  3.,  6.,  3.,  4.],
         [ 3.,  4.,  1.,  0.,  5.],
         [ 2.,  3.,  5.,  3.,  0.],
         [ 4.,  6.,  5.,  6.,  2.],
         [ 0.,  6.,  1.,  6.,  8.],
         [ 3.,  8.,  0.,  8.,  0.],
         [ 5.,  2.,  5.,  0.,  1.],
         [ 0.,  6.,  5.,  9.,  1.]])
Run Code Online (Sandbox Code Playgroud)

当然,如果你事先不知道你的阵列的大小应该只是创建一个比你需要的大一个,并在完成填充后修剪'未使用'部分

>>> M[:3,:3]
  array([[ 9.,  3.,  1.],
         [ 9.,  6.,  8.],
         [ 9.,  7.,  5.]])
Run Code Online (Sandbox Code Playgroud)


Pau*_*aul 5

通常,在创建 NumPy 数组时,您不会不断调整其大小。您不喜欢第三个解决方案的哪些方面?如果它是一个非常大的矩阵/数组,那么在开始分配其值之前可能值得分配该数组:

x = len(something)
y = getColumnDataAsNumpyArray.someLengthProperty

data = numpy.zeros( (x,y) )
for i in something:
   data[i] = getColumnDataAsNumpyArray(i)
Run Code Online (Sandbox Code Playgroud)