2016-06-21 21 views
9

簡單地說,如何在Python中對大熊貓數據框(可能是2,000,000行)應用分位數規範化?關於熊貓數據框的分位數規範

PS。我知道有一個包名爲rpy2可能在子跑R,使用R.位數正常化,但事實是,當我用數據設置如下R可以不計算正確的結果:

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 

編輯:

我想要什麼:上面顯示

給出的數據,如何申請位數標準化按照https://en.wikipedia.org/wiki/Quantile_normalization步驟。

我發現一段代碼在Python宣稱它可以計算位數歸一化:

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) 

的代碼正常工作與在代碼中使用的樣本數據,但是當我測試它與數據定結果出錯了。

由於ryp2提供了一個在python子進程中運行R的接口,我直接在R中再次測試它,結果仍然是錯誤的。因此我認爲原因在於R中的方法是錯誤的。

+0

我刪除了 「R」 標籤因爲你(1)沒有使用R和(2)不想在答案中使用R.但是如果你說「R無法計算正確的結果」,這聽起來像是要麼貶低R(爲了什麼目的?),要麼讓某人糾正你未發佈的代碼。無論哪種方式,也許我誤解你想要的東西:分位數標準化需要源和目標分佈,我不確定你在這裏提供什麼。你能澄清一下嗎? – r2evans

+0

@ r2evans感謝您的評論,我已經編輯了這個問題。僅供參考,我使用的代碼將R作爲Python的子進程運行。直接運行R後,我發現結果是錯誤的。另外,我不確定'目標分佈是什麼意思。根據Wiki,分位數歸一化的計算不涉及該詞。希望我明確表達的問題是對我給出的數據應用分位數標準化。 –

+0

你說得對,我的「目標」一詞並不是很好。維基參考文獻*「使兩個分佈相同」*,所以我想知道你的兩個分佈是什麼。現在你提供了額外的代碼(和數據,定義爲矩陣),我很困惑你的實際數據是定量的。 (也許是一個愚蠢的問題,但是矩陣是否可能與你實際需要的相比進行轉置?) – r2evans

回答

2

好吧我自己實施的方法效率比較高。

完成後,這個邏輯看起來很容易,但無論如何,我決定在這裏發佈它,讓任何人都感到困惑,就像當我無法使用可用的代碼時一樣。

的代碼在github上:Quantile Normalize

10

使用示例數據集從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 

對於每個等級,平均值可以被計算如下:

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 

然後由此產生的系列rank_mean可以用作排名以得到歸一化結果的映射:

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

優雅地使用'groupby','map'和'stacking/unstacking'。你是'熊貓'開發者嗎? –

+3

謝謝。不,我只是一個普通用戶。 – ayhan

0

可能使用每行中位數而非平均值更強大(基於Shawn的code。 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

下面的代碼給出了相同的結果爲preprocessCore::normalize.quantiles.use.target,我覺得它比上面的解決方案簡單清晰。此外,性能應該可以達到巨大的陣列長度。

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

一旦有了pandas.DataFrame容易做到:

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

(正火第一columnt到第二個如在上面的例子中的參考分佈。)

0

我是熊貓的新手,但問題遲了,但我想答案也可能有用。它建立關從@ayhan偉大answer的:

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 

這裏的主要區別是更接近一些真實世界的應用。通常你只需要數字數據的矩陣,在這種情況下,原始答案就足夠了。

有時候你也有基於文本的數據。這使您可以指定數字數據的列cols,並在這些列上運行分位數標準化。最後,它會將原始數據框中的非數字(或未規範化)列合併回來。

例如如果你添加了一些 '元數據'(char)的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']) 

,你可以調用

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

得到

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