Как указано в комментариях, np.random.choice
принимает параметр весов, так что вы можете просто использовать его в al oop:
import numpy as np
# Make input data
np.random.seed(0)
n = 10
aa = np.random.uniform(1000, 2500, (n, 3))
s = np.random.rand(n, 3)
# Normalize weights
s_norm = s / s.sum(1, keepdims=True)
# Output array
out = np.empty((n, 2), dtype=aa.dtype)
# Sample iteratively
for i in range(n):
out[i] = aa[i, np.random.choice(3, size=2, replace=False, p=s_norm[i])]
Это не самый эффективный способ сделать что-то, так как обычно использование векторизованных операций намного быстрее, чем зацикливание. К сожалению, я не думаю, что есть какой-либо способ выбрать из нескольких категориальных распределений одновременно (см. NumPy выпуск # 15201 ). Однако, поскольку вы всегда хотите получить два элемента из трех, вы можете выбрать элемент, который хотите удалить (с инвертированными вероятностями), а затем сохранить два других. Этот фрагмент кода выглядит примерно так:
import numpy as np
# Make input data
np.random.seed(0)
n = 10
aa = np.random.uniform(1000, 2500, (n, 3))
s = np.random.rand(n, 3)
print(s)
# [[0.26455561 0.77423369 0.45615033]
# [0.56843395 0.0187898 0.6176355 ]
# [0.61209572 0.616934 0.94374808]
# [0.6818203 0.3595079 0.43703195]
# [0.6976312 0.06022547 0.66676672]
# [0.67063787 0.21038256 0.1289263 ]
# [0.31542835 0.36371077 0.57019677]
# [0.43860151 0.98837384 0.10204481]
# [0.20887676 0.16130952 0.65310833]
# [0.2532916 0.46631077 0.24442559]]
# Invert weights
si = 1 / s
# Normalize
si_norm = si / si.sum(1, keepdims=True)
# Accumulate
si_cum = np.cumsum(si_norm, axis=1)
# Sample according to inverted probabilities
t = np.random.rand(n, 1)
idx = np.argmax(t < si_cum, axis=1)
# Get non-sampled indices
r = np.arange(3)
m = r != idx[:, np.newaxis]
choice = np.broadcast_to(r, m.shape)[m].reshape(n, -1)
print(choice)
# [[1 2]
# [0 2]
# [0 2]
# [1 2]
# [0 2]
# [0 2]
# [0 1]
# [1 2]
# [0 2]
# [1 2]]
# Get corresponding data
out = np.take_along_axis(aa, choice, 1)
Одним из возможных недостатков этого является то, что выбранные элементы всегда будут в порядке (то есть для данной строки вы можете получить пары индексов (0, 1)
, (0, 2)
или (1, 2)
, но не (1, 0)
, (2, 0)
или (2, 1)
).
Конечно, если вам действительно нужно всего несколько сэмплов, тогда, вероятно, l oop самое удобное и поддерживаемое решение, второе будет полезно только в том случае, если вам нужно сделать это в большем масштабе.