Массивы Numpy со смешанными изменяемыми и неизменяемыми значениями

#python #numpy

#python #numpy

Вопрос:

Меня интересует лучший / быстрый способ выполнения операций с массивом (точка, внешний, добавить и т.д.), Игнорируя некоторые значения в массиве. Меня больше всего интересуют случаи, когда некоторые (возможно, 50%-30%) значения игнорируются и фактически равны нулю при умеренно больших массивах, возможно, от 100 000 до 1 000 000 элементов. Есть несколько решений, о которых я могу подумать, но, похоже, ни одно из них не извлекает реальной пользы из возможных преимуществ игнорирования некоторых значений. Например:

 import numpy as np
A = np.ones((dim, dim)) # the array to modify
B = np.random.random_integers(0, 1, (dim, dim)) # the values to ignore are 0
C = np.array(B, dtype = np.bool)
D = np.random.random((dim, dim)) # the array which will be used to modify A

# Option 1: zero some values using multiplication.
# some initial tests show this is the fastest
A  = B * D

# Option 2: use indexing
# this seems to be the slowest
A[C]  = D[C]

# Option 3: use masked arrays
A = np.ma.array(np.ones((dim, dim)), mask = np.array(B - 1, dtype = np.bool))
A  = D
  

правка1:

Как предположил киборг, другим вариантом могут быть разреженные массивы. К сожалению, я не очень хорошо знаком с пакетом и не могу получить преимущества в скорости, которые я мог бы получить. Например, если у меня есть взвешенный график с ограниченной связностью, определяемый разреженной матрицей A , другой разреженной матрицей B , которая определяет связность (1 = подключено, 0 = не подключено), и плотная матрица numpy C , я хотел бы иметь возможность делать что-то вроде A = A B.multiply(C) и использовать преимущества разреженности A и B .

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

1. Остается ли B неизменным во время подачи вашей заявки или он может сильно измениться в ходе обработки? я прошу, чтобы, если это не изменилось, вы могли использовать подход, аналогичный этому Маскированные массивы для сжатых массивов . Я предполагаю, что переход по индексу — это часть, которая занимает много времени, судя по результатам варианта 2

2. интересно, что вы нашли в masked array?

3. Для целей, которые я имею в виду, B останется фиксированным. Сжатый массив звучит примерно так, но, похоже, никаких последующих действий не последует, и, похоже, он использует замаскированные массивы для всей арифметики за кулисами. Я сомневаюсь, что это было бы лучше, но кто знает. Маскированный массив был примерно в 4 раза медленнее, чем простое использование умножения для обнуления значений в простых тестах, которые я проводил.

4. A = B.multiply(C) это правильный поступок. Почему вы говорите, что не получаете преимущества?

5. if B разрежен и C плотен B.multiply(C) — это плотная матрица, и я полагаю, что любые преимущества использования разреженных матриц будут преодолены из-за необходимости постоянно преобразовывать результат обратно в разреженную матрицу перед добавлением к A . Может быть, есть лучший способ для этого, но, как я уже сказал, я не очень хорошо знаком с scipy.sparse. Кроме того, мой синтаксис был неправильным. = не определено для разреженных матриц в scipy, оно должно быть A = A B.multiply(C)

Ответ №1:

С разреженной матрицей вы можете получить улучшение, если плотность составляет менее 10%. Разреженная матрица может быть быстрее, в зависимости от того, учитываете ли вы время, необходимое для построения матрицы.

 import timeit

setup=
'''
import numpy as np
dim=1000
A = np.ones((dim, dim)) # the array to modify
B = np.random.random_integers(0, 1, (dim, dim)) # the values to ignore are 0
C = np.array(B, dtype = np.bool)
D = np.random.random((dim, dim)) # the array which will be used to modify A
'''

print('mult    ' str(timeit.timeit('A  = B * D', setup, number=3)))

print('index   ' str(timeit.timeit('A[C]  = D[C]', setup, number=3)))

setup3 = setup 
''' 
A = np.ma.array(np.ones((dim, dim)), mask = np.array(B - 1, dtype = np.bool))
'''
print('ma      '   str(timeit.timeit('A  = D', setup3, number=3)))

setup4 = setup 
''' 
from scipy import sparse
S = sparse.csr_matrix(C)
DS = S.multiply(D)
'''
print('sparse- ' str(timeit.timeit('A  = DS', setup4, number=3)))

setup5 = setup 
''' 
from scipy import sparse
'''
print('sparse  ' str(timeit.timeit('S = sparse.csr_matrix(C); DS = S.multiply(D); A  = DS', setup4, number=3)))

setup6 = setup 
'''
from scipy import sparse
class Sparsemat(sparse.coo_matrix):
    def __iadd__(self, other):
        self.data  = other.data
        return self
A = Sparsemat(sparse.rand(dim, dim, 0.5, 'coo')) # the array to modify
D = np.random.random((dim, dim)) # the array which will be used to modify A
anz = A.nonzero()
'''
stmt6=
'''
DS = Sparsemat((D[anz[0],anz[1]], anz), shape=A.shape) # new graph based on random weights
A  = DS
'''
print('sparse2 ' str(timeit.timeit(stmt6, setup6, number=3)))
  

Вывод:

 mult    0.0248420299535
index   0.32025789431
ma      0.1067024434
sparse- 0.00996273276303
sparse  0.228869672266
sparse2 0.105496183846
  

Редактировать: Вы можете использовать приведенный выше код ( setup6 ) для расширения scipy.sparse.coo_matrix . Он сохраняет разреженный формат.

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

1. Похоже, что разреженные массивы могут быть разумным вариантом. К сожалению, я просто недостаточно знаком с пакетом, чтобы воспользоваться этим преимуществом, т. Е. У меня всегда получаются плотные матрицы. Я отредактировал свой первоначальный вопрос, чтобы привести более конкретный пример.

2. Матрица с 50%-30% нулей не является разреженной