Я сохранил пару массивов numpy с помощью np.save(), и вместе они довольно огромны.
Можно ли загрузить их все как файлы с отображением в памяти, а затем объединить и разрезать их все, даже не загружая ничего в память?
Я сохранил пару массивов numpy с помощью np.save(), и вместе они довольно огромны.
Можно ли загрузить их все как файлы с отображением в памяти, а затем объединить и разрезать их все, даже не загружая ничего в память?
Использование numpy.concatenate
очевидно загружает массивы в память. Чтобы избежать этого, вы можете легко создать третий массив memmap
в новом файле и прочитать значения из массивов, которые вы хотите объединить. Более эффективным способом является добавление новых массивов в уже существующий файл на диске.
В любом случае вы должны выбрать правильный порядок для массива (основной ряд или столбец).
В следующих примерах показано, как выполнить конкатенацию по оси 0 и оси 1.
1) объединить вдоль axis=0
a = np.memmap('a.array', dtype='float64', mode='w+', shape=( 5000,1000)) # 38.1MB
a[:,:] = 111
b = np.memmap('b.array', dtype='float64', mode='w+', shape=(15000,1000)) # 114 MB
b[:,:] = 222
Вы можете определить третий массив, читающий тот же файл, что и первый объединяемый массив (здесь a
) в режиме r+
(чтение и добавление), но с формой конечного массива, которую вы хотите получить после объединения, например:
c = np.memmap('a.array', dtype='float64', mode='r+', shape=(20000,1000), order='C')
c[5000:,:] = b
Объединение по axis=0
не требует прохождения order='C'
, потому что это уже порядок по умолчанию.
2) объединить вдоль axis=1
a = np.memmap('a.array', dtype='float64', mode='w+', shape=(5000,3000)) # 114 MB
a[:,:] = 111
b = np.memmap('b.array', dtype='float64', mode='w+', shape=(5000,1000)) # 38.1MB
b[:,:] = 222
Массивы, сохраненные на диске, фактически сглажены, поэтому, если вы создаете c
с mode=r+
и shape=(5000,4000)
без изменения порядка массива, 1000
первых элементов из второй строки в a
перейдут к первому в строке в c
. Но вы можете легко избежать этой передачи order='F'
(основной столбец) в memmap
:
c = np.memmap('a.array', dtype='float64', mode='r+',shape=(5000,4000), order='F')
c[:, 3000:] = b
Здесь у вас есть обновленный файл «a.array» с результатом конкатенации. Вы можете повторить этот процесс для конкатенации парами по два.
Связанные вопросы:
Возможно, альтернативное решение, но у меня также был один многомерный массив, распределенный по нескольким файлам, которые я хотел только читать. Я решил эту проблему с помощью конкатенации Dask.
import numpy as np
import dask.array as da
a = np.memmap('a.array', dtype='float64', mode='r', shape=( 5000,1000))
b = np.memmap('b.array', dtype='float64', mode='r', shape=(15000,1000))
c = da.concatenate([a, b], axis=0)
Таким образом можно избежать хакерского дополнительного дескриптора файла. Затем массив dask можно нарезать и работать с ним почти как с любым массивом numpy, а когда приходит время вычислять результат, вызывается compute
.
Обратите внимание, что есть два предостережения:
c[::2] = 0
невозможно, поэтому в таких случаях необходимы творческие решения.store
. Этот метод снова может принимать массив memmapped
.Если вы используете order='F'
, это приведет к другой проблеме, из-за которой, когда вы загрузите файл в следующий раз, он выйдет из строя, даже если вы пройдете order='F
. Итак, мое решение ниже, я много тестировал, все работает нормально.
fp = your old memmap...
shape = fp.shape
data = your ndarray...
data_shape = data.shape
concat_shape = data_shape[:-1] + (data_shape[-1] + shape[-1],)
print('cancat shape:{}'.format(concat_shape))
new_fp = np.memmap(new_file_name, dtype='float32', mode='r+', shape=concat_shape)
if len(concat_shape) == 1:
new_fp[:shape[0]] = fp[:]
new_fp[shape[0]:] = data[:]
if len(concat_shape) == 2:
new_fp[:, :shape[-1]] = fp[:]
new_fp[:, shape[-1]:] = data[:]
elif len(concat_shape) == 3:
new_fp[:, :, :shape[-1]] = fp[:]
new_fp[:, :, shape[-1]:] = data[:]
fp = new_fp
fp.flush()