Использование np.apply_along_axis, но на определенных индексах - PullRequest
0 голосов
/ 11 июля 2020

У меня есть массив numpy, X, формы (200, 200, 1500). У меня также есть функция func, которая по существу возвращает среднее значение массива (она делает еще несколько вещей, но все они - numpy операции, вы можете думать об этом как np.mean). Теперь, если я хочу применить эту функцию ко второму массиву, я могу просто сделать np.apply_along_axis(func, 2, X). Но у меня также есть массив правды формы (200, 200, 1500). Я хочу применить func только к тем местам, где в массиве истинности есть True. Таким образом, он будет игнорировать любые места, где массив истинности ложен. Итак, возвращаясь к примеру np.mean, для каждого индекса массива по второй оси будет использоваться среднее значение, но игнорировать некоторый произвольный набор индексов.

Итак, на практике моим решением было бы преобразовать X в новый массив Y с формой (200, 200), но элементы массива являются списками. Это будет сделано с использованием массива истинности. Затем примените func к каждому списку в массиве. Проблема в том, что это кажется очень трудоемким, и я чувствую, что для этого существует решение, ориентированное на numpy. Есть ли?

Если то, что я сказал со списком массивов, является лучшим способом, как я могу go объединить X и массив истинности, чтобы получить Y?

Любой предложения или комментарии приветствуются.

1 Ответ

1 голос
/ 11 июля 2020
In [268]: X = np.random.randint(0,100,(200,200,1500))                                                

Давайте проверим, как apply работает только с np.mean:

In [269]: res = np.apply_along_axis(np.mean, 2, X)                                                   
In [270]: res.shape                                                                                  
Out[270]: (200, 200)
In [271]: timeit res = np.apply_along_axis(np.mean, 2, X)                                            
1.2 s ± 36.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Эквивалент, использующий итерацию по первым двум измерениям. Я использую reshape, чтобы было легче писать; скорость должна быть примерно такой же с двойным l oop.

In [272]: res1 = np.reshape([np.mean(row) for row in X.reshape(-1,1500)],(200,200))                  
In [273]: np.allclose(res, res1)                                                                     
Out[273]: True
In [274]: timeit res1 = np.reshape([np.mean(row) for row in X.reshape(-1,1500)],(200,200))           
906 ms ± 13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Так что apply может быть удобным, но это не инструмент скорости.

Для скорости в numpy вам необходимо максимально использовать скомпилированный код и избегать ненужных python циклов уровней.

In [275]: res2 = np.mean(X,axis=2)                                                                   
In [276]: np.allclose(res2,res)                                                                      
Out[276]: True
In [277]: timeit res2 = np.mean(X,axis=2)                                                            
120 ms ± 619 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Если использование apply в вашем новом случае сложно, вы не Не теряйте ничего, используя то, что вы понимаете.

masked

In [278]: mask = np.random.randint(0,2, X.shape).astype(bool)                                        

Итерация [272] может быть адаптирована для работы с маской:

In [279]: resM1 = np.reshape([np.mean(row[m]) for row,m in zip(X.reshape(-1,1500),mask.reshape(-1,150
     ...: 0))],X.shape[:2])                                                                          
In [280]: timeit resM1 = np.reshape([np.mean(row[m]) for row,m in zip(X.reshape(-1,1500),mask.reshape
     ...: (-1,1500))],X.shape[:2])                                                                   
1.43 s ± 18.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Это может есть проблемы, если row[m] пуст. np.mean([]) выдает предупреждение и nan значение.

Применение маски к X до того, как любая дальнейшая обработка приведет к потере информации о размерах.

In [282]: X[mask].shape                                                                              
Out[282]: (30001416,)

apply работает только с одним массивом , поэтому будет неудобно (хотя и возможно) использовать его для итерации как X, так и mask. Структурированный массив с полями данных и масок может подойти. Но предыдущие тайминги показывают, что нет преимущества в скорости.

маскированный массив

Обычно я не ожидаю, что маскированные массивы будут обеспечивать скорость, но в этом случае это помогает:

In [285]: xM = np.ma.masked_array(X, ~mask)                                                          
In [286]: resMM = np.ma.mean(xM, axis=2)                                                             
In [287]: np.allclose(resM1, resMM)                                                                  
Out[287]: True
In [288]: timeit resMM = np.ma.mean(xM, axis=2)                                                      
849 ms ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

np.nanmean

Есть набор функций, которые используют np.nan маскировку:

In [289]: Xfloat = X.astype(float)                                                                   
In [290]: Xfloat[~mask] = np.nan                                                                     
In [291]: resflt = np.nanmean(Xfloat, axis=2)                                                        
In [292]: np.allclose(resM1, resflt)                                                                 
Out[292]: True
In [293]: %%timeit 
     ...: Xfloat = X.astype(float) 
     ...: Xfloat[~mask] = np.nan 
     ...: resflt = np.nanmean(Xfloat, axis=2) 
     ...:  
     ...:                                                                                            
2.17 s ± 200 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Это не помогает: (

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