Ускорьте превращение вероятностей в бинарные функции - PullRequest
3 голосов
/ 29 апреля 2019

У меня есть фрейм данных с 3 столбцами, в каждой строке у меня есть вероятность того, что в этой строке объект T имеет значения 1, 2 и 3

import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({"T1" : [0.8,0.5,0.01],"T2":[0.1,0.2,0.89],"T3":[0.1,0.3,0.1]})

Для строки 0 T равен 1 сВероятность 80%, 2 с 10% и 3 с 10%

Я хочу смоделировать значение T для каждой строки и изменить столбцы T1, T2, T3 на двоичные объекты.У меня есть решение, но оно должно зацикливаться на строках фрейма данных, оно очень медленное (мой реальный фрейм данных содержит более 1 миллиона строк):

possib = df.columns
for i in range(df.shape[0]):
    probas = df.iloc[i][possib].tolist()
    choix_transp = np.random.choice(possib,1, p=probas)[0]
    for pos in possib:
        if pos==choix_transp:
            df.iloc[i][pos] = 1
        else:
            df.iloc[i][pos] = 0

Есть ли способ векторизации этого кода?

Спасибо!

Ответы [ 2 ]

4 голосов
/ 29 апреля 2019

Вот пример, основанный на векторизации random.choice с заданной матрицей вероятностей -

def matrixprob_to_onehot(ar):
    # Get one-hot encoded boolean array based on matrix of probabilities
    c = ar.cumsum(axis=1)
    idx = (np.random.rand(len(c), 1) < c).argmax(axis=1)
    ar_out = np.zeros(ar.shape, dtype=bool)
    ar_out[np.arange(len(idx)),idx] = 1
    return ar_out

ar_out = matrixprob_to_onehot(df.values)
df_out = pd.DataFrame(ar_out.view('i1'), index=df.index, columns=df.columns)

Проверка с большим набором данных для вероятностей -

In [139]: df = pd.DataFrame({"T1" : [0.8,0.5,0.01],"T2":[0.1,0.2,0.89],"T3":[0.1,0.3,0.1]})

In [140]: df
Out[140]: 
     T1    T2   T3
0  0.80  0.10  0.1
1  0.50  0.20  0.3
2  0.01  0.89  0.1

In [141]: p = np.array([matrixprob_to_onehot(df.values) for i in range(100000)]).argmax(2)

In [142]: np.array([np.bincount(p[:,i])/100000.0 for i in range(len(df))])
Out[142]: 
array([[0.80064, 0.0995 , 0.09986],
       [0.50051, 0.20113, 0.29836],
       [0.01015, 0.89045, 0.0994 ]])

In [145]: np.round(_,2)
Out[145]: 
array([[0.8 , 0.1 , 0.1 ],
       [0.5 , 0.2 , 0.3 ],
       [0.01, 0.89, 0.1 ]])

Синхронизация 1000,000 строк -

# Setup input
In [169]: N = 1000000
     ...: a = np.random.rand(N,3)
     ...: df = pd.DataFrame(a/a.sum(1,keepdims=1),columns=[['T1','T2','T3']])

# @gmds's soln
In [171]: %timeit pd.get_dummies((np.random.rand(len(df), 1) > df.cumsum(axis=1)).idxmin(axis=1))
1 loop, best of 3: 4.82 s per loop

# Soln from this post
In [172]: %%timeit 
     ...: ar_out = matrixprob_to_onehot(df.values)
     ...: df_out = pd.DataFrame(ar_out.view('i1'), index=df.index, columns=df.columns)
10 loops, best of 3: 43.1 ms per loop
2 голосов
/ 29 апреля 2019

Мы можем использовать numpy для этого:

result = pd.get_dummies((np.random.rand(len(df), 1) > df.cumsum(axis=1)).idxmin(axis=1))

Это генерирует один столбец случайных значений и сравнивает его с накопленным по столбцам суммой данных, что приводит к DataFrame значениям, где первое значение False показывает, в какое «ведро» попадает случайное значение С помощью idxmax мы можем получить индекс этого сегмента, который мы затем можем конвертировать обратно с помощью pd.get_dummies.

Пример:

import numpy as np
import pandas as pd

np.random.seed(0)
data = np.random.rand(10, 3)
normalised = data / data.sum(axis=1)[:, np.newaxis]

df = pd.DataFrame(normalised)
result = pd.get_dummies((np.random.rand(len(df), 1) > df.cumsum(axis=1)).idxmin(axis=1))

print(result)

Выход:

   0  1  2
0  1  0  0
1  0  0  1
2  0  1  0
3  0  1  0
4  1  0  0
5  0  0  1
6  0  1  0
7  0  1  0
8  0  0  1
9  0  1  0

Примечание:

Большая часть замедления происходит от pd.get_dummies; если вы используете метод Дивакара pd.DataFrame(result.view('i1'), index=df.index, columns=df.columns), он становится намного быстрее.

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