Подтвердить что ты не робот

Выращивание матриц по столбцу в NumPy

В чистом 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)

Оба варианта кажутся немного неудобными. Есть ли более приятные решения?

4b9b3361

Ответ 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)

ваш второй фрагмент (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)

заполняет массив NumPy как в OP, за исключением того, что каждая итерация только повторно устанавливает значения 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.]])

Ответ 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 имеет два измерения (а не 1D-вектор), поэтому вам нужны данные, которые также должны иметь два измерения..., например, data = np.array([[]]) - или, альтернативно, сделать newColumn 1D-вектором (обычно, если вещи 1D лучше держать их 1D в NumPy, поэтому трансляция и т.д. работают лучше). в этом случае использование np.squeeze(newColumn) и hstack или vstack должно работать с вашим исходным определением данных.