2016-06-21 12 views
9

Po prostu, jak zastosować normalizację kwantyli na dużej ramce danych Pandy (prawdopodobnie 2 000 000 wierszy) w Pythonie?Normalizacja kwantyli na pandach DataFame

PS. Wiem, że jest to pakiet o nazwie rpy2 który może uruchomić R w podproces, używając kwantyli normalize w R. Ale prawda jest taka, że ​​R nie można obliczyć prawidłowy wynik podczas korzystania z zestawu danych jak poniżej:

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 

Edycja :

Czego chcę: pokazane powyżej

Biorąc pod uwagę dane, jak stosować kwantyli normalizacja następujących etapów https://en.wikipedia.org/wiki/Quantile_normalization.

znalazłem kawałek kodu w Pythonie deklarując, że może obliczyć kwantylem normalizację:

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) 

Kod działa poprawnie z przykładowych danych używanych w kodzie, jednak kiedy go przetestować z uwagi na dane powyżej wynik był zły.

Ponieważ ryp2 zapewnia interfejs do uruchamiania R w podprocesie Pythona, testuję go ponownie bezpośrednio w R, a wynik nadal jest nieprawidłowy. W rezultacie myślę, że powodem jest to, że metoda w R jest błędna.

+0

usunąłem "R" tag ponieważ ty (1) nie używasz R i (2) nie chcesz R w odpowiedzi. Ale jeśli powiesz "R nie może obliczyć poprawnego wyniku", brzmi to tak, jakbyś dyskredytował R (do jakiego końca?) Lub chciałbyś, aby ktoś poprawił twój nieopublikowany kod. Tak czy siak, być może nie rozumiem tego, czego chcesz: normalizacja kwantylowa potrzebuje dystrybucji źródłowej i docelowej i nie jestem pewien, co tu robisz. Czy możesz wyjaśnić, proszę? – r2evans

+0

@ r2evans Dzięki za komentarz i już zredagowałem pytanie. FYI, kod, którego używałem, uruchamia R jako podproces Pythona. Po uruchomieniu R bezpośrednio stwierdziłem, że wynik był nieprawidłowy. Poza tym nie jestem pewien, co masz na myśli przez "dystrybucję celu". Zgodnie z Wiki, obliczenia normalizacji kwantyli nie obejmują tego terminu. Pytanie, mam nadzieję, że wyjaśniłem, polega na zastosowaniu normalizacji kwantyli na danych, które przekazałem. –

+0

Masz rację, mój termin "cel" nie jest naprawdę dobry. Odwołania do wiki * "co dwie identyczne dystrybucje" *, więc zastanawiałem się, jakie były twoje dwie dystrybucje. Teraz, gdy dostarczyłeś dodatkowy kod (i dane, zdefiniowane jako "macierz"), jestem zdezorientowany, jeśli chodzi o faktyczne dane, które mają być kwantyfikowane. (Być może głupie pytanie, ale czy to możliwe, że matryca jest transponowana w porównaniu z tym, czego naprawdę potrzebujesz?) – r2evans

Odpowiedz

2

OK Sam wdrożyłem metodę o stosunkowo wysokiej wydajności.

Po skończeniu, ta logika wydaje się trochę łatwa, ale zresztą zdecydowałem się ją opublikować tutaj, ponieważ każdy czuje się zagubiony, tak jak ja, kiedy nie mogłem googotować dostępnego kodu.

Kod jest w github: Quantile Normalize

10

na przykładzie zestawu danych z 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 

Dla każdej rangi, średnia wartość można obliczyć z następujących czynności:

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 

Następnie uzyskana seria, rank_mean, może być wykorzystana jako mapowanie dla rang w celu uzyskania znormalizowanych wyników:

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

eleganckie użycie 'groupby',' map' oraz 'stacking/unstacking'. jesteś programistą 'pandy'? –

+3

Dzięki. Nie, jestem zwykłym użytkownikiem. – ayhan

0

Prawdopodobnie bardziej niezawodny, aby użyć mediany w każdym wierszu zamiast średniej (na podstawie code z 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

Poniższy kod daje identyczny wynik jak preprocessCore::normalize.quantiles.use.target i uważam, że to prostsze niż jaśniejsze powyższych rozwiązań. Wydajność powinna być dobra aż do ogromnej długości tablicy.

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()] 

Gdy masz pandas.DataFrame łatwe do zrobienia:

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

(normalizowanie pierwszy columnt do drugiego jako dystrybucji odniesienia w powyższym przykładzie.)

0

Jestem nowy w pandach i spóźniony na to pytanie, ale myślę, że odpowiedź może być przydatna. Buduje się wielkiego answer od @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 

główną różnicą jest bliżej do niektórych aplikacjach rzeczywistych. Często po prostu macie matryce danych liczbowych, w którym to przypadku oryginalna odpowiedź jest wystarczająca.

Czasami są tam również dane tekstowe. Pozwala to określić kolumny danych liczbowych i przeprowadzi normalizację kwantyli w tych kolumnach. Na koniec zostanie scalone z kolumnami nieliczbowymi (lub nie znormalizowanymi) z oryginalnej ramki danych.

np. jeśli dodano trochę 'meta-danych' (char) na przykład 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']) 

można następnie wywołać

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

dostać

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