Замена записей массива NumPy их частотами / значениями из словаря

Проблема: из двух входных массивов я хочу вывести массив с частотой True значений (от input_2), соответствующих каждому значению input_1.

import numpy as np # import everything from numpy from scipy.stats import itemfreq input_1 = np.array([3,6,6,3,6,4]) input_2 = np.array([False, True, True, False, False, True]) 

Для этого примера вывод, который я хочу:

 output_1 = np.array([0,2,2,0,2,1]) 

Мой текущий подход включает в себя редактирование input_1, поэтому остаются только значения, соответствующие True:

 locs=np.where(input_2==True,input_1,0) 

Затем подсчитывая частоту каждого ответа, создавая словарь и заменяя соответствующие клавиши ввода_1 на значения (истинные частоты).

 loc_freq = itemfreq(locs) dic = {} for key,val in loc_freq: dic[key]=val print dic for k, v in dic.iteritems(): input_1[input_1==k]=v 

который выводит [3,2,2,3,2,1].

Проблема здесь двоякая: 1) это все еще ничего не делает с ключами, которые не находятся в словаре (и поэтому их следует изменить на 0). Например, как я могу превратить 3s в 0s? 2) Это кажется очень неэффективным / неэффективным. Есть ли лучший способ приблизиться к этому?

np.bincount – это то, что вы ищете.

 output_1 = np.bincount(input_1[input_2])[input_1] 

Решение @memecs правильно, +1. Однако он будет очень медленным и займет много памяти, если значения в input_1 действительно велики, то есть они не являются индексами массива, но говорят, что это секунды или некоторые другие целочисленные данные, которые могут принимать очень большие значения.

В этом случае у вас есть np.bincount(input_1[input_2]).size равен наибольшему целому числу в input_1 с True значением в input_2 .

Гораздо быстрее использовать unique и bincount . Мы используем первое, чтобы извлечь индексы уникальных элементов input_1 , а затем использовать bincount для подсчета того, как часто эти индексы появляются в том же массиве и взвешивают их 1 или 0 на основе значения массива input_2 ( True или False ) :

 # extract unique elements and the indices to reconstruct the array unq, idx = np.unique(input_1, return_inverse=True) # calculate the weighted frequencies of these indices freqs_idx = np.bincount(idx, weights=input_2) # reconstruct the array of frequencies of the elements frequencies = freqs_idx[idx] print(frequencies) 

Это решение очень быстро и имеет минимальное влияние на память. Кредит отправляется на @Jaime, см. Его комментарий ниже. Ниже я сообщаю свой оригинальный ответ, используя unique по-другому.

ДРУГИЕ ВОЗМОЖНОСТИ

Возможно, быстрее пойти на другое решение, используя unique :

 import numpy as np input_1 = np.array([3, 6, 6, 3, 6, 4]) input_2 = np.array([False, True, True, False, False, True]) non_zero_hits, counts = np.unique(input_1[input_2], return_counts=True) all_hits, idx = np.unique(input_1, return_inverse=True) frequencies = np.zeros_like(all_hits) #2nd step, with broadcasting idx_non_zero_hits_in_all_hits = np.where(non_zero_hits[:, np.newaxis] - all_hits == 0)[1] frequencies[idx_non_zero_hits_in_all_hits] = counts print(frequencies[idx]) 

Это имеет недостаток, который потребует большой памяти, если количество уникальных элементов в input_1 с True значением в input_2 много, из-за созданного и переданного 2D-массива. Чтобы уменьшить объем памяти, вы можете использовать цикл for вместо второго шага алгоритма:

 #2nd step, but with a for loop. for j, val in enumerate(non_zero_hits): index = np.where(val == all_hits)[0] frequencies[index] = counts[j] print(frequencies[idx]) 

Это второе решение имеет очень небольшой объем памяти, но требует цикла for . Это зависит от вашего типичного размера ввода данных и значений, решение которых будет лучше всего.

Реализованное в настоящее время решение для бинтонов довольно элегантно, но пакет numpy_indexed обеспечивает более общие решения таких проблем:

 import numpy_indexed as npi idx = npi.as_index(input_1) unique_labels, true_count_per_label = npi.group_by(idx).sum(input_2) print(true_count_per_label[idx.inverse])