Почему вставка и добавление для numpy ndarray возвращает новый массив вместо изменения исходного массива? - PullRequest
0 голосов
/ 15 декабря 2018

Для numpy ndarray нет добавления и вставки, как для списков нативных python.

a = np.array([1, 2, 3])
a.append(5)  # this does not work
a = np.append(a, 5)  # this is the only way

В то время как для нативных списков python,

a = [1, 2, 3]
a.append(4)  # this modifies a
a  # [1, 2, 3, 4]

Почему был numpy ndarrayразработан таким образом?Я пишу подкласс ndarray, есть ли способ реализовать "append", как нативные массивы python?

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

ndarray создается с буфером данных фиксированного размера - достаточно большим, чтобы вместить байты, представляющие элементы.

arr.nbytes == arr.itemsize * arr.size

arr.resize может изменить массив на месте.Но прочитайте его документы, чтобы увидеть ограничения, особенно в отношении владения собственными данными.Это одна из немногих операций на месте, и она редко используется.

В отличие от этого, список Python хранит указатели объектов в буфере.Буфер имеет некоторую комнату роста, позволяющую эффективно append.Нужно просто добавить новый указатель на буфер.Когда буфер заполняется, он выделяет новый больший буфер и копирует указатели.

Для массива 1d буферы для ndarray и list будут аналогичными, по крайней мере, для 4 или 8 байтов числовых dtypes,Но для многомерных массивов буфер данных может быть очень большим (произведение всех измерений), в то время как верхний буфер эквивалентного вложенного массива просто содержит указатели на внешний слой списков («строки»).

Массивы dtype объекта хранят указатели как список, но буфер данных по-прежнему имеет фиксированный размер (без пространства для роста).Производительность лежит между числовыми массивами и списками.

Я могу представить, как написать добавление на месте, использующее метод resize, с последующим копированием новых значений в 0 заливок.

In [96]: arr = np.array([[1,3],[2,7]])
In [97]: arr.resize(3,2)
In [98]: arr
Out[98]: 
array([[1, 3],
       [2, 7],
       [0, 0]])
In [99]: arr[-1,:] = 10,11
In [100]: arr
Out[100]: 
array([[ 1,  3],
       [ 2,  7],
       [10, 11]])

Но обратите внимание, что происходит со значениями, когда мы изменяем размер внутренней оси:

In [101]: arr = np.array([[1,3],[2,7]])
In [102]: arr.resize(2,3)
In [103]: arr
Out[103]: 
array([[1, 3, 2],
       [7, 0, 0]])

Так что этот тип добавления довольно ограничен по сравнению с concatenate (и всеми его производными «стека»).


Вы смотрели код np.append?Убедившись, что аргументы являются массивами, и настроив их формы, он делает:

concatenate((arr, values), axis=axis)

Другими словами, это просто альтернативный способ вызова concatenate.Вероятно, лучше всего добавить одно значение в массив 1d.Он не должен использоваться повторно в цикле, именно потому, что он возвращает новый массив, и, следовательно, является относительно дорогим.В противном случае его использование портит многих пользователей.Некоторые игнорируют параметр оси.У других возникают проблемы с созданием правильного «пустого» массива для начала.Конкатенация также имеет эти проблемы, но, по крайней мере, пользователям приходится сознательно решать вопрос соответствия форм.

np.insert гораздо сложнее.Он делает разные вещи в зависимости от того, является ли индекс (obj) числом, срезом или списком чисел.Один из подходов заключается в создании целевого массива правильного размера, копировании фрагментов из оригинала и вставке значений в правильные слоты.Другой способ - использовать логическую маску для копирования значений в правильные места.Оба должны учитывать многомерность - они вставляются вдоль одной оси, но должны использовать соответствующий slice(None) для других измерений.Это намного сложнее, чем вставка списка, которая вставляет один объект (указатель) в одном месте в 1d.

0 голосов
/ 15 декабря 2018

NumPy интенсивно использует просмотров , функцию, которую не поддерживают списки Python.Представление - это массив, который использует память другого объекта, а не владеет собственной памятью;например, в следующем фрагменте

a = numpy.arange(5)
b = a[1:3]

b является представлением a.

Представления будут очень плохо взаимодействовать с внутренним append или другимоперации по изменению размера места.Массивы внезапно не будут представлениями массивов, которыми они должны быть представления, или они будут представлениями освобожденной памяти, или было бы непредсказуемым, повлияет ли append в одном массиве на массив, представление которого он рассматривал, или на все видыдругие проблемыНапример, как будет выглядеть a после b.append(6)?Или как будет выглядеть b после a.clear()?А какие гарантии производительности вы могли бы дать?Вероятно, не амортизированная гарантия постоянного времени list.append.

Если вы хотите append, вам, вероятно, не следует использовать массивы NumPy;Вы должны использовать список и создать массив из списка, когда закончите добавление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...