Оптимизирован ли массив numpy и список python для динамического роста?

За это время я сделал много вещей, которые требуют от меня использования функции list .append(), а также функции numpy.append() для массивов numpy. Я заметил, что оба растут очень медленно, когда размеры массивов велики.

Мне нужен динамически растущий массив размером около 1 миллиона элементов. Я могу реализовать это самостоятельно, точно так же, как std::vector делается на C++, добавление длины буфера (reserve length), которая недоступна извне. Но нужно ли мне изобретать велосипед? Я думаю, что это должно быть реализовано где-то. Итак, мой вопрос: существует ли такая вещь уже в Python?

Что я имею в виду. Есть ли в Python тип массива, который в большинстве случаев способен динамически увеличиваться со временной сложностью O(C)?


person The Quantum Physicist    schedule 09.12.2016    source источник
comment
@MooingRawr Причина, по которой я задействовал list в этом, заключается в том, что он не обязательно должен быть последовательным в памяти, и все же он работает медленно с большими массивами. Так что я честно не понимаю, что происходит.   -  person The Quantum Physicist    schedule 09.12.2016
comment
@TheQuantumPhysicist: Python list непрерывен; это не связанный список. Это как C++ std::vector указателей.   -  person user2357112 supports Monica    schedule 09.12.2016


Ответы (2)


Оба используют базовый массив. Вместо этого вы можете использовать collections.deque, специально созданный для добавления и удаления элементов на обоих концах со сложностью O(1).

person blue_note    schedule 09.12.2016

Память массивов numpy хорошо описана в его документах и ​​много обсуждалась здесь. Раскладка памяти списка также обсуждалась, хотя обычно просто отличается от numpy.

Массив numpy имеет буфер данных фиксированного размера. «рост» требует создания нового массива и копирования в него данных. np.concatenate делает это в скомпилированном коде. np.append, а также все функции stack используют concatenate.

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

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

Рекомендуемый способ итеративного создания массива — построить список с помощью append и один раз создать массив в конце. Повторные np.append или np.concatenate относительно дороги.

deque было упомянуто. Я мало знаю о том, как он хранит свои данные. В документах говорится, что он может добавлять элементы в начале так же легко, как и в конце, но произвольный доступ медленнее, чем для списка. Это означает, что он хранит данные в каком-то связанном списке, поэтому для поиска элемента nth требуется пройти по ссылкам n-1 перед ним. Таким образом, существует компромисс между простотой роста и скоростью доступа.

Добавление элементов в начало списка требует создания нового списка указателей с новыми указателями в начале. Таким образом, добавление и удаление элементов в начале обычного списка намного дороже, чем в конце.

Рекомендация программного обеспечения не входит в основную цель SO. Другие могут внести предложения, но не удивляйтесь, если это закроют.

Существуют форматы файлов, такие как HDF5, предназначенные для больших наборов данных. Они обеспечивают рост с помощью таких функций, как «дробление». И есть все виды пакетов баз данных.

person hpaulj    schedule 09.12.2016
comment
Прошу прощения, но это не рекомендация по программному обеспечению, и если рекомендация о том, как что-то делать, является рекомендацией по программному обеспечению, то 99% статей SO должны быть закрыты. Хотя я благодарю вас за предоставленную информацию, это предложение очень неконструктивно, раздражает и является соломенной чучелом. Пожалуйста, удалите его. - person The Quantum Physicist; 09.12.2016
comment
Кстати, я использую HDF5 для того же проекта, но я говорю о вещах в памяти, а не в файлах. - person The Quantum Physicist; 09.12.2016
comment
То, как вы используете данные, так же важно, как и то, как вы их увеличиваете. Обычно вы используете его много раз, но выращиваете только один раз. Я бы склонялся к использованию добавления списка для создания массивов из небольших частей (число/строка за раз) и объединения массивов для объединения больших массивов в более крупные - до тех пор, пока я не начну сталкиваться с ограничениями памяти. - person hpaulj; 09.12.2016
comment
В настоящее время мое решение состоит в том, чтобы изменить размер дважды, до и после добавления данных. Сначала я изменяю размер до максимально ожидаемого размера, а затем возвращаю размер, когда закончу добавлять. Это быстрое и грязное решение, которое я получу быстрее всего. - person The Quantum Physicist; 09.12.2016
comment
Вы говорите об использовании метода x.resize (на месте) и немедленном присвоении новых значений? Я не использовал это раньше. Похоже, вам нужно быть осторожным при использовании массива между resize операциями. И это может быть трудно использовать с чем-либо, кроме массива 1d. np.resize делает копию. - person hpaulj; 10.12.2016
comment
Нет. Я добавляю один массив нулей (np.zeros), который заполняет максимальный размер, который мне может понадобиться (около 30 тыс. элементов), затем постепенно назначаю нужные мне элементы, а затем в конечном итоге удаляю лишние нули. Я знаю, что ненужное копирование — это проблема, но, по крайней мере, у меня нет n*O(n). Это самое мирное и простое решение для моей нынешней ситуации. - person The Quantum Physicist; 10.12.2016
comment
Вот такие подробности вы должны были указать в своем первоначальном вопросе. Это сэкономило бы мне много работы при написании моего ответа. :) - person hpaulj; 10.12.2016