La generazione di variabili casuali Discrete con specificato pesi utilizza SciPy o NumPy

Sto cercando una semplice funzione che può generare una matrice di determinati valori casuali basato sul loro corrispondente (anche specificato) la probabilità. Ho solo bisogno di generare valori float, ma non vedo perché non dovrebbe essere in grado di generare alcun scalare. Posso pensare a molti modi di questo edificio dalle funzioni esistenti, ma penso che probabilmente ho appena perso un evidente SciPy o NumPy funzione.

E. g.:

>>> values = [1.1, 2.2, 3.3]
>>> probabilities = [0.2, 0.5, 0.3]
>>> print some_function(values, probabilities, size=10)
(2.2, 1.1, 3.3, 3.3, 2.2, 2.2, 1.1, 2.2, 3.3, 2.2)

Nota: ho trovato scipy.le statistiche.rv_discrete, ma non capisco come funziona. In particolare, non capisco che cosa questa (sotto) significa né che cosa si dovrebbe fare:

numargs = generic.numargs
[ <shape(s)> ] = ['Replace with resonable value', ]*numargs

Se rv_discrete è quello che ho dovrebbe essere in uso, si potrebbe si prega di fornire me con un semplice esempio e una spiegazione di cui sopra “forma” dichiarazione?

InformationsquelleAutor TimY | 2012-07-07

 

5 Replies
  1. 56

    Disegno da una distribuzione discreta, è integrato direttamente in numpy.
    La funzione viene chiamata casuale.scelta (difficili da trovare, senza alcun riferimento a distribuzioni discrete in numpy docs).

    elements = [1.1, 2.2, 3.3]
    probabilities = [0.2, 0.5, 0.3]
    np.random.choice(elements, 10, p=probabilities)
    • Grande! Ma, la sintassi corretta è: np.casuale.scelta(elementi, 10, p=list(probabilità))
    • Bello. Credo che questa versione è venuto fuori dopo che ho postato la mia domanda originale (penso che questo sia stato rilasciato per la prima volta in versione 1.7.0 che, credo, è venuto nel 2013).
    • Molto bello! Sembra funzionare anche senza il casting per la lista: np.casuale.scelta(elementi, 10, p=probabilità)).
    • Oltre ai commenti da Sina e zeycus, elements e probabilites avrebbe potuto essere ordinaria lists invece di numpy.arrays e il codice funziona lo stesso.
  2. 25

    Qui è una breve e relativamente semplice funzione che restituisce valori ponderati, utilizza NumPy è digitize, accumulate, e random_sample.

    import numpy as np
    from numpy.random import random_sample
    
    def weighted_values(values, probabilities, size):
        bins = np.add.accumulate(probabilities)
        return values[np.digitize(random_sample(size), bins)]
    
    values = np.array([1.1, 2.2, 3.3])
    probabilities = np.array([0.2, 0.5, 0.3])
    
    print weighted_values(values, probabilities, 10)
    #Sample output:
    [ 2.2  2.2  1.1  2.2  2.2  3.3  3.3  2.2  3.3  3.3]

    Funziona in questo modo:

    1. Primo utilizzo accumulate creiamo bidoni.
    2. Poi dobbiamo creare un gruppo di numeri casuali (tra 0, e 1) utilizzando random_sample
    3. Usiamo digitize per vedere che i raccoglitori di questi numeri cadere.
    4. E restituire i valori corrispondenti.
    • Sì, questo è fondamentalmente quello che stavo pensando, ma ho pensato che ci potrebbe essere una funzione built-in che fa esattamente questo. Dal suono di esso, non c’è nessuna tale cosa. Devo ammettere che non avrebbe fatto la stessa eleganza. – Grazie
    • NumPy offre direttamente numpy.cumsum(), che può essere utilizzato invece di np.add.accumulate() (np.add() non è molto utilizzata, così mi consiglia di utilizzare cumsum()).
    • +1 per l’utile numpy.digitize()! Tuttavia, SciPy offre in realtà una funzione che risponde direttamente alla domanda—vedi la mia risposta.
    • PS:… Come notato da Tim_Y, utilizzando SciPy funzione è molto più lento rispetto all’uso “manuale” soluzione (su 10k elementi).
    • Fare le probabilità devono essere normalizzati per questo ?
    • Sì, le probabilità sono normalizzati, perché random_sample() restituisce i numeri in [0; 1), in modo che i bidoni non può estendersi al di là di questa gamma (se la probabilità che si somma a più di 1).

  3. 15

    Stavi andando in una buona direzione: il built-in scipy.stats.rv_discrete() abbastanza crea direttamente una variabile casuale discreta. Qui è come funziona:

    >>> from scipy.stats import rv_discrete  
    
    >>> values = numpy.array([1.1, 2.2, 3.3])
    >>> probabilities = [0.2, 0.5, 0.3]
    
    >>> distrib = rv_discrete(values=(range(len(values)), probabilities))  # This defines a Scipy probability distribution
    
    >>> distrib.rvs(size=10)  # 10 samples from range(len(values))
    array([1, 2, 0, 2, 2, 0, 2, 1, 0, 2])
    
    >>> values[_]  # Conversion to specific discrete values (the fact that values is a NumPy array is used for the indexing)
    [2.2, 3.3, 1.1, 3.3, 3.3, 1.1, 3.3, 2.2, 1.1, 3.3]

    La distribuzione distrib sopra torna così in indici dal values elenco.

    Più in generale, rv_discrete() prende una sequenza di intero valori in i primi elementi della sua values=(…,…) argomento e restituisce questi valori, in questo caso; non c’è bisogno di convertire specifici (float) i valori. Qui è un esempio:

    >>> values = [10, 20, 30]
    >>> probabilities = [0.2, 0.5, 0.3]
    >>> distrib = rv_discrete(values=(values, probabilities))
    >>> distrib.rvs(size=10)
    array([20, 20, 20, 20, 20, 20, 20, 30, 20, 20])

    dove (intero) i valori di input sono restituite direttamente con la probabilità desiderata.

    • NOTA: ho provato a correre timeit su di esso, e sembra essere un buon 100 volte più lento di fraxel puramente numpy versione. Non è per caso sapete il perché?
    • Wow, interessante! Su 10k elementi, ho anche ottenere un fattore di 300x più lento. Ho dato una rapida occhiata al codice: ci sono molti controlli eseguiti, ma credo che non si può spiegare una grande differenza nel tempo di esecuzione; non sono andato abbastanza in profondità nel Scipy codice sono stati in grado di vedere dove la differenza potrebbe venire da…
    • la mia ingenua ipotesi è che la lentezza è dovuta al maggior lavoro svolto in puro Python, meno lavoro svolto (sotto il cofano) in C. (matematica/scientifica pacchetti Python tendono ad avvolgere il codice C.)
    • supponiamo che io inizio con un’equazione per la mia distribuzione di probabilità. sembra stupido dover utilizzare per generare una probabilità per ogni valore, mangimi per rv_discrete, e poi tornare da rv_discrete un’approssimazione della distribuzione ho iniziato con. c’è un modo per utilizzare equazioni definite dall’utente direttamente con scipy?
    • Credo che la tua equazione modelli di una continua variabile casuale, invece di un discreto (che è oggetto della presente domanda), in modo da passare attraverso una variabile discreta, potrebbe infatti non essere le migliori (a meno che l’approssimazione di una distribuzione discreta, ti aiuta a ottenere più veloce l’esecuzione di codice). Si potrebbe desiderare di guardare a come SciPy gestisce le variabili continue, per esempio a partire da scicomp.stackexchange.com/a/1659/9996.
    • no, io sto usando una variabile casuale discreta. non so perché pensi che io non lo sono. si scopre sto usando una variabile casuale di Poisson, e c’è una funzione in numpy per il prelievo di campioni da una distribuzione di Poisson (np.random.poisson). lo stesso sono sicuro che è vero per la maggior parte delle distribuzioni. la mia domanda rimane senza risposta, anche se, per di più idiosincratica distribuzioni.
    • Ora vedo che si aveva in mente il caso di una distribuzione discreta con un infinito numero di valori possibili (che non si adatta a questa domanda). rv_discrete() non dispone di un’opzione per questo. Io non sono sicuro di quale sia il metodo standard per fare questo. (Posso solo pensare a un po ‘ complicato variazioni del solito metodo che trasforma una variabile casuale uniforme in una variabile con una non uniforme distribuzione, dove la probabilità cumulativa viene calcolata solo per i valori più comuni e si estese oltre che, quando necessario.)

  4. 4

    Si potrebbe anche usare Lea, un puro Python pacchetto dedicato alle distribuzioni di probabilità discrete.

    >>> distrib = Lea.fromValFreqs((1.1,2),(2.2,5),(3.3,3))
    >>> distrib
    1.1 : 2/10
    2.2 : 5/10
    3.3 : 3/10
    >>> distrib.random(10)
    (2.2, 2.2, 1.1, 2.2, 2.2, 2.2, 1.1, 3.3, 1.1, 3.3)

    Et voilà!

  5. 3

    Più semplice fai da te modo sarebbe quello di riassumere le probabilità in una distribuzione cumulativa.
    In questo modo, è possibile dividere l’intervallo unitario in sotto-intervalli di lunghezza uguale all’originale probabilità. Ora generare un numero casuale uniforme in [0,1), e per la quale intervallo di terre.

Lascia un commento