Наиболее частое вхождение (режим) значений массива numpy на основе идентификаторов в другом массиве - PullRequest
2 голосов
/ 24 января 2020

У меня есть двумерный массив, содержащий значения, и я хотел бы рассчитать наиболее частую запись (т.е. режим) из этих данных в соответствии с идентификаторами во втором массиве.

data = np.array([[[ 0, 10, 50, 80, 80],
                  [10, 10, 50, 80, 90],
                  [10, 10, 50, 80, 90],
                  [50, 50, 80, 80, 80]])


ID = np.array([[[ 1,  1, 2, 3, 3],
                  [1, 1, 2, 3, 3],
                  [1, 1, 2, 3, 3],
                  [1, 2, 2, 2, 3]])


#Expected Result is:

[10 50 80]

Наиболее частое значение в массиве данных для ID = 1 равно 10, ID = 2 равно 50 и ID = 3 равно 80. Я играл с np.unique и комбинациями np.bincount и np.argmax, но я не могу понять Как получить результат. Любая помощь?

Ответы [ 4 ]

4 голосов
/ 24 января 2020

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

import numpy as np

# Input data
data = np.array([[[ 0, 10, 50, 80, 80],
                  [10, 10, 50, 80, 90],
                  [10, 10, 50, 80, 90],
                  [50, 50, 80, 80, 80]]])
ID = np.array([[[1, 1, 2, 3, 3],
                [1, 1, 2, 3, 3],
                [1, 1, 2, 3, 3],
                [1, 2, 2, 2, 3]]])
# Find unique data values and group ids with reverse indexing
data_uniq, data_idx = np.unique(data, return_inverse=True)
id_uniq, id_idx = np.unique(ID, return_inverse=True)
# Number of unique data values
n = len(data_uniq)
# Number of ids
m = len(id_uniq)
# Change indices so values of each group are within separate intervals
grouped = data_idx + (n * np.arange(m))[id_idx]
# Count repetitions and reshape
# counts[i, j] has the number of apparitions of the j-th value in the i-th group
counts = np.bincount(grouped, minlength=n * m).reshape(m, n)
# Get the modes from the counts
modes = data_uniq[counts.argmax(1)]
# Print result
for group, mode in zip(id_uniq, modes):
    print(f'Mode of {group}: {mode}')

Вывод:

Mode of 1: 10
Mode of 2: 50
Mode of 3: 80

Быстрый тест для конкретного размера задачи:

import numpy as np
import scipy.stats

def find_group_modes_loop(data, ID):
    # Assume ids are given sequentially starting from 1
    m = ID.max()
    modes = np.empty(m, dtype=data.dtype)
    for id in range(m):
        modes[id] = scipy.stats.mode(data[ID == id + 1])[0][0]
    return modes

def find_group_modes_vec(data, ID):
    # Assume ids are given sequentially starting from 1
    data_uniq, data_idx = np.unique(data, return_inverse=True)
    id_uniq = np.arange(ID.max(), dtype=data.dtype)
    n = len(data_uniq)
    m = len(id_uniq)
    grouped = data_idx + (n * np.arange(m))[ID.ravel() - 1]
    counts = np.bincount(grouped, minlength=n * m).reshape(m, n)
    return data_uniq[counts.argmax(1)]

# Make data
np.random.seed(0)
data = np.random.randint(0, 1_000, size=10_000_000)
ID = np.random.randint(1, 100, size=10_000_000)
print(np.all(find_group_modes_loop(data, ID) == find_group_modes_vec(data, ID)))
# True
%timeit find_group_modes_loop(data, ID)
# 212 ms ± 647 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit find_group_modes_vec(data, ID)
# 122 ms ± 3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Так что, по крайней мере, в некоторых случаях векторизованное решение может быть значительно быстрее, чем зацикливание.

2 голосов
/ 24 января 2020

Я применил этот подход в numpy, надеюсь, это решит вашу проблему.

n,f=np.unique(data[np.where(ID == 1)],return_counts=True)

Вывод: (array([ 0, 10, 50]), array([1, 5, 1]))

Вывод кортеж значений и их соответствующих частот res = [] for id in np.unique(ID): n,f = np.unique(data[np.where(ID == id)],return_counts=True) res.append(n[np.argmax(f)])

2 голосов
/ 24 января 2020

Один из подходов заключается в использовании режимов scipy mode

from scipy.stats import mode

uniq_ids = np.unique(ID)
modes = []

for id in uniq_ids:
    modes.append(mode(data[ID == id])[0][0])

[10 50 80]

1 голос
/ 24 января 2020

Если вам нужно чистое решение numpy, вы можете заново изобрести колесо в @ Kenan's l oop:

def mode(x):
    n, c = np.unique(x, return_counts=True)
    return n[np.argmax(c)]

modes = [mode(data[ID == id]) for id in np.unique(IDs)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...