【问题标题】:Growing matrices columnwise in NumPy在 NumPy 中按列增长矩阵
【发布时间】:2009-11-23 13:54:00
【问题描述】:

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

data = []
for i in something:
    newColumn = getColumnDataAsList(i)
    data.append(newColumn)

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

所以,我的选择是在适当的条件下删除循环中的初始化:

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

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

data = []
for i in something:
    newColumn = getColumnDataAsNumpyArray(i)
    data.append(newColumn)
data = numpy.array(data)

这两种变体似乎都有点尴尬。有更好的解决方案吗?

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

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

    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)
    

    如果您添加另一行,您的第二个 sn-p (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))
    

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


    虽然这是对您的问题最直接的回答,但我应该提到循环数据源以通过 append 填充目标,虽然在 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)
    

    像在 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.]])
    

    当然,如果您事先不知道您的数组应该是什么大小 只需创建一个比您需要的大得多并修剪“未使用”部分 当你完成填充它时

    >>> M[:3,:3]
      array([[ 9.,  3.,  1.],
             [ 9.,  6.,  8.],
             [ 9.,  7.,  5.]])
    

    【讨论】:

    • 对于一个 numpy 新手来说非常有用的帖子。快速提问:你有什么理由使用for index, itm in enumerate(range(5)): 而不仅仅是,例如,for x in range(5): 看到索引和它具有相同的值并且只使用一个。
    • @JohnBarça 感谢您的反馈。您可能是对的,我的代码 sn-p 的细节应该更仔细地选择——即,在我的示例中,每次迭代的“索引”值确实与循环变量的值相同。这是一个人工制品——这两个变量的值在实践中可能不相等(例如,可迭代对象是一个包含要传递给函数的值的列表,该函数创建一维数组,然后将其“插入”到目标数组中)。
    【解决方案2】:

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

    x = len(something)
    y = getColumnDataAsNumpyArray.someLengthProperty
    
    data = numpy.zeros( (x,y) )
    for i in something:
       data[i] = getColumnDataAsNumpyArray(i)
    

    【讨论】:

      【解决方案3】:

      hstack 可以处理零大小的数组:

      import numpy as np
      
      N = 5
      M = 15
      
      a = np.ndarray(shape = (N, 0))
      for i in range(M):
          b = np.random.rand(N, 1)
          a = np.hstack((a, b))
      

      【讨论】:

        【解决方案4】:

        通常,继续重新分配 NumPy 数组的成本很高 - 所以你的第三个解决方案确实是最好的性能明智的。

        但是我认为hstack 会做你想做的事 - 提示在错误消息中,

        ValueError: 数组的维数必须相同`

        我猜 newColumn 有两个维度(而不是一维向量),所以你需要数据也有两个维度......例如,data = np.array([[]]) - 或者将 newColumn 设为一维向量(通常如果事物是一维的,最好在 NumPy 中保持一维,因此广播等工作得更好)。在这种情况下,使用 np.squeeze(newColumn)hstackvstack 应该适用于您对数据的原始定义。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-04
          • 2017-09-30
          • 1970-01-01
          • 1970-01-01
          • 2021-03-08
          • 1970-01-01
          相关资源
          最近更新 更多