Эффективный способ внешнего умножения двух больших одномерных массивов

#python #arrays #numpy #multiplication

#питон #массивы #тупой #умножение

Вопрос:

Я пытаюсь умножить два массива на каждую возможную комбинацию элементов. Например

 A = [1, 2, 3]
B = [1, 2, 3]
# Result -> [1, 2, 3, 2, 4, 6, 3, 6, 9]
 

Мои массивы имеют длину 10000. Я уже пробовал такой подход:

 np.multiply.outer(data, data2)
 

Однако я получаю эту ошибку:

 anaconda3libsite-packagespandascoreseries.py in __array_ufunc__(self, ufunc, method, *inputs, **kwargs)
    724 
    725         inputs = tuple(extract_array(x, extract_numpy=True) for x in inputs)
--> 726         result = getattr(ufunc, method)(*inputs, **kwargs)
    727 
    728         name = names[0] if len(set(names)) == 1 else None

MemoryError: Unable to allocate 74.5 GiB for an array with shape (100000, 100000) and data type float64
 

Как это сделать?

Комментарии:

1. Да, np.multply.outer это то, что вы хотите. Возможно, вам нужно найти компьютер с большим 74.5 GB объемом оперативной памяти :-). Или вам нужно будет сохранить ваши данные на диск, а не в память.

2. как я могу заставить сделать это в памяти вместо этого?

3. outer отлично работает с этими двумя примерами массивов, не так ли, получая результат (3,3). Проблема не в multiply.outer этом, а в размере массивов. Вам действительно нужно формировать внешний продукт таких больших массивов? 100000*100000*8 = 80 Г

Ответ №1:

Ваши матрицы разрежены? означает, что многие ячейки имеют значение = 0? Если это так, вы могли бы использовать разреженную матрицу scipy. Этот модуль очень эффективно работает с большими массивами. Я считаю, что этот пример соответствует тому, о чем вы просите: https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.kron.html#scipy.sparse.kron

Комментарии:

1. У меня есть два предложения: — Похоже, что этот тип умножения может быть выполнен кусками? как насчет умножения одной строки или подмножества A за раз? аналогично mathworks.com/matlabcentral/answers /… — Каково максимальное значение в массивах? Вы могли бы, например, изменить свой dtype массива на np.uint8, что сэкономит вам много памяти, но будьте осторожны, потому что этот dtype не допускает никакого значения, превышающего 2^8. (2^16, 2^32 и 2 ^ 64 также доступны) numpy.org/doc/stable/user/basics.types.html

2. значения — float64, я пробовал uint8, однако значения округляются до 0. Есть ли решение с помощью float?

3. Вы можете попробовать numpy.half / numpy.float16

4. Изменение dtype просто откладывает ошибку памяти. Трудно применять эти меньшие dtypes во всех вычислениях и трудно ограничить количество временных буферов и т. Д.

Ответ №2:

Вот фрагментированное решение, которое должно выгружаться на диск в правильном порядке:

 a = np.arange(10000)
b = a[::-1]   # 'cause why not

chunk_size = 500
with open('output.dat', 'w') as f:
    for c in a.split(np.arange(chunk_size, a.size   1, chunk_size)):
        np.tofile(np.multiply.outer(c, b))
 

Каждый c из них должен быть view into a , поэтому единственные новые данные, которые вы создаете, — это with np.multiply.outer(c, b) . Вы можете управлять размером промежуточного вывода, изменяя chunk_size его таким образом, чтобы операция помещалась в память. Двоичные данные output.dat будут выводом, который вы хотите сохранить, в порядке следования строк (C).