2016-06-21 51 views
9

In parole povere, come applicare la normalizzazione quantile su un grande dataframe di Pandas (probabilmente 2.000.000 di righe) in Python?normalizzazione quantile su pandas dataframe

PS. So che c'è un pacchetto di nome rpy2 che potrebbe correre R in sottoprocesso, utilizzando normalizzare quantile in R. Ma la verità è che R non può calcolare il risultato corretto quando uso il set di dati, come di seguito:

5.690386092696389541e-05,2.051450375415418849e-05,1.963190184049079707e-05,1.258362869906251862e-04,1.503352476021528139e-04,6.881341586355676286e-06 
8.535579139044583634e-05,5.128625938538547123e-06,1.635991820040899643e-05,6.291814349531259308e-05,3.006704952043056075e-05,6.881341586355676286e-06 
5.690386092696389541e-05,2.051450375415418849e-05,1.963190184049079707e-05,1.258362869906251862e-04,1.503352476021528139e-04,6.881341586355676286e-06 
2.845193046348194770e-05,1.538587781561563968e-05,2.944785276073619561e-05,4.194542899687506431e-05,6.013409904086112150e-05,1.0322e-05 

Edit :

Quello che voglio:

Visti i dati sopra esposti, come applicare la normalizzazione quantile seguenti passi in https://en.wikipedia.org/wiki/Quantile_normalization.

ho trovato un pezzo di codice in Python dichiarando che potrebbe calcolare la normalizzazione quantile:

import rpy2.robjects as robjects 
import numpy as np 
from rpy2.robjects.packages import importr 
preprocessCore = importr('preprocessCore') 


matrix = [ [1,2,3,4,5], [1,3,5,7,9], [2,4,6,8,10] ] 
v = robjects.FloatVector([ element for col in matrix for element in col ]) 
m = robjects.r['matrix'](v, ncol = len(matrix), byrow=False) 
Rnormalized_matrix = preprocessCore.normalize_quantiles(m) 
normalized_matrix = np.array(Rnormalized_matrix) 

Il codice funziona bene con i dati di esempio utilizzati nel codice, tuttavia quando provo con i dati forniti sopra il risultato è andato storto.

Poiché ryp2 fornisce un'interfaccia per eseguire R nel sottoprocesso di Python, lo provo di nuovo in R direttamente e il risultato era ancora sbagliato. Di conseguenza, penso che la ragione sia che il metodo in R è sbagliato.

+0

ho rimosso il tag "R" poiché tu (1) non stai usando R e (2) non vuoi R nella risposta. Ma se dici "R non può calcolare il risultato corretto", suona come se tu stessi denigrando R (a che scopo?) O vuoi che qualcuno corregga il tuo codice non pubblicato. Ad ogni modo, forse sto fraintendendo quello che vuoi: la normalizzazione quantile ha bisogno di una distribuzione di origine e di destinazione e non sono certo che tu stia fornendo qui. Puoi chiarire, per favore? – r2evans

+0

@ r2evans Grazie per il tuo commento e ho già modificato la domanda. Cordiali saluti, il codice che ho cercato su google come sottoprocesso di Python. Dopo aver eseguito R direttamente ho scoperto che il risultato era sbagliato. Inoltre, non sono sicuro di cosa intendi per "distribuzione target". Secondo Wiki, il calcolo della normalizzazione quantile non implica quel termine. La domanda, spero di averlo chiarito, è di applicare la normalizzazione quantile sui dati che ho dato. –

+0

Hai ragione, il mio termine di "bersaglio" non è molto buono. I riferimenti wiki * "rendendo due distribuzioni identiche" *, quindi mi chiedevo quali fossero le tue due distribuzioni. Ora che hai fornito codice aggiuntivo (e dati, definiti come 'matrix'), sono confuso su quali siano i tuoi dati effettivi a livello di quantizzazione. (Forse una domanda stupida, ma è possibile che la matrice sia trasposta rispetto a ciò di cui hai effettivamente bisogno?) – r2evans

risposta

2

Ok ho implementato il metodo di efficienza relativamente elevata.

Dopo aver terminato, questa logica sembra abbastanza semplice ma, comunque, ho deciso di postarla qui per chiunque si senta confuso come lo sono io quando non sono riuscito a cercare il codice disponibile.

Il codice è in github: Quantile Normalize

10

Utilizzando l'esempio set di dati da Wikipedia article:

df = pd.DataFrame({'C1': {'A': 5, 'B': 2, 'C': 3, 'D': 4}, 
        'C2': {'A': 4, 'B': 1, 'C': 4, 'D': 2}, 
        'C3': {'A': 3, 'B': 4, 'C': 6, 'D': 8}}) 

df 
Out: 
    C1 C2 C3 
A 5 4 3 
B 2 1 4 
C 3 4 6 
D 4 2 8 

Per ogni rango, il valore medio può essere calcolato con la seguente:

rank_mean = df.stack().groupby(df.rank(method='first').stack().astype(int)).mean() 

rank_mean 
Out: 
1 2.000000 
2 3.000000 
3 4.666667 
4 5.666667 
dtype: float64 

Quindi la Serie risultante, rank_mean, può essere utilizzata come mappatura per i ranghi per ottenere i risultati normalizzati:

df.rank(method='min').stack().astype(int).map(rank_mean).unstack() 
Out: 
     C1  C2  C3 
A 5.666667 4.666667 2.000000 
B 2.000000 2.000000 3.000000 
C 3.000000 4.666667 4.666667 
D 4.666667 3.000000 5.666667 
+1

uso elegante di 'groupby',' map' e 'stacking/unstacking'. sei uno sviluppatore di 'pandas'? –

+3

Grazie. No, sono solo un utente abituale. – ayhan

0

Forse più robusto per utilizzare la mediana su ogni riga anziché medio (sulla base code da Shawn. L):

def quantileNormalize(df_input): 
    df = df_input.copy() 
    #compute rank 
    dic = {} 
    for col in df: 
     dic[col] = df[col].sort_values(na_position='first').values 
    sorted_df = pd.DataFrame(dic) 
    #rank = sorted_df.mean(axis = 1).tolist() 
    rank = sorted_df.median(axis = 1).tolist() 
    #sort 
    for col in df: 
     # compute percentile rank [0,1] for each score in column 
     t = df[col].rank(pct=True, method='max').values 
     # replace percentile values in column with quantile normalized score 
     # retrieve q_norm score using calling rank with percentile value 
     df[col] = [ np.nanpercentile(rank, i*100) if ~np.isnan(i) else np.nan for i in t ] 
    return df 
0

Il codice qui sotto dà risultato identico come preprocessCore::normalize.quantiles.use.target e lo trovo più semplice più chiaro che le soluzioni di cui sopra. Anche le prestazioni dovrebbero essere buone fino a lunghezze di array enormi.

import numpy as np 

def quantile_normalize_using_target(x, target): 
    """ 
    Both `x` and `target` are numpy arrays of equal lengths. 
    """ 

    target_sorted = np.sort(target) 

    return target_sorted[x.argsort().argsort()] 

volta che hai un pandas.DataFrame facile da fare:

quantile_normalize_using_target(df[0].as_matrix(), 
           df[1].as_matrix()) 

(Normalizzare il primo columnt alla seconda come una distribuzione di riferimento nell'esempio di cui sopra.)

0

Sono nuovo per i panda e in ritardo alla domanda, ma penso che la risposta potrebbe anche essere utile. Esso si basa fuori del grande answer da @ayhan:

def quantile_normalize(dataframe, cols, pandas=pd): 

    # copy dataframe and only use the columns with numerical values 
    df = dataframe.copy().filter(items=cols) 

    # columns from the original dataframe not specified in cols 
    non_numeric = dataframe.filter(items=list(filter(lambda col: col not in cols, list(dataframe)))) 


    rank_mean = df.stack().groupby(df.rank(method='first').stack().astype(int)).mean() 

    norm = df.rank(method='min').stack().astype(int).map(rank_mean).unstack() 


    result = pandas.concat([norm, non_numeric], axis=1) 
    return result 

la differenza principale qui è più vicino ad alcune applicazioni del mondo reale. Spesso hai solo matrici di dati numerici, nel qual caso la risposta originale è sufficiente.

A volte ci sono anche dati basati sul testo. Ciò consente di specificare le colonne cols dei dati numerici e eseguirà la normalizzazione quantile su tali colonne. Alla fine unirà le colonne non numeriche (o non normalizzate) dal riquadro dati originale.

ad es. se si aggiunge un po 'di meta-dati' (char) per l'esempio wiki:

df = pd.DataFrame({ 
    'rep1': [5, 2, 3, 4], 
    'rep2': [4, 1, 4, 2], 
    'rep3': [3, 4, 6, 8], 
    'char': ['gene_a', 'gene_b', 'gene_c', 'gene_d'] 
}, index = ['a', 'b', 'c', 'd']) 

si può quindi chiamare

quantile_normalize(t, ['rep1', 'rep2', 'rep3']) 

per ottenere

rep1  rep2  rep3  char 
a 5.666667 4.666667 2.000000 gene_a 
b 2.000000 2.000000 3.000000 gene_b 
c 3.000000 4.666667 4.666667 gene_c 
d 4.666667 3.000000 5.666667 gene_d