Как выбрать элементы из матрицы случайно взвешенных - PullRequest
1 голос
/ 25 февраля 2020

Я довольно новичок в python и у меня есть некоторые проблемы со случайностью.

Я ищу что-то подобное тогда RandomChoice в Mathematica. Я создаю матрицу размерности, скажем, 10x3 со случайными числами, большими 0. Давайте назовем общую сумму каждой строки s_i для i = 0, ..., 9

Позже я хочу выбрать для каждой строки 2 из 3-х элементов (без повторений) с взвешенной вероятностью (по этой причине я перешел с Mathematica на Python), и я думаю, что приведенный выше код можно значительно улучшить

Заранее благодарен за любые советы / помощь

РЕДАКТИРОВАТЬ: у меня теперь есть следующий, который работает, но не выглядит хорошим ходом для меня

#pre-defined lists 
nn=3
aa=np.random.uniform(1000, 2500, (nn,3))
help1=[0,1,2]
help2=aa.sum(axis=1)
#now I create a weigthed prob list and fill it
help3=np.zeros((nn,3)) 
for i in range(nn):
    help3[i,0]=aa[i,0]/help2[i]
    help3[i,1]=aa[i,1]/help2[i]
    help3[i,2]=aa[i,2]/help2[i]
#every timestep when I have to choose 2 out of 3
help5=np.zeros((nn,2))
for i in range(nn):
    #cc=random.sample(help1,2)
    help4=np.random.choice(help1, 2, replace=False, p=[help3[i,0], help3[i,1], help3[i,2]])
    help5[i,0]=aa[i,cc[0]]
    help5[i,1]=aa[i,cc[1]]
print(help5)

1 Ответ

0 голосов
/ 25 февраля 2020

Как указано в комментариях, 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 самое удобное и поддерживаемое решение, второе будет полезно только в том случае, если вам нужно сделать это в большем масштабе.

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