web-dev-qa-db-ja.com

pandasデータフレームでの分位正規化

簡単に言えば、Pythonの大きなPandasデータフレーム(おそらく2,000,000行)に分位正規化を適用する方法は?

PS。 Rで分位正規化を使用してサブプロセスでRを実行できるrpy2という名前のパッケージがあることは知っています。しかし、実際には、次のようにデータセットを使用すると、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.032201237953351358e-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のメソッドが間違っていることが原因だと思います。 。

11
Shawn. L

わかりました、私は比較的高効率の方法を自分で実装しました。

終了後、このロジックは簡単に思えますが、とにかく、利用可能なコードをグーグルで検索できなかったときのように混乱していると感じる人のために、ここに投稿することにしました。

コードはgithubにあります: Quantile Normalize

7
Shawn. L

ウィキペディアの記事 からのサンプルデータセットを使用する:

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
18
ayhan

注目に値することの1つは、ayhanとshawnの両方のコードがタイに小さいランク平均を使用することですが、Rパッケージprocesscoreのnormalize.quantiles()を使用すると、タイにランク平均の平均が使用されます。

上記の例を使用すると:

> df

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

> normalize.quantiles(as.matrix(df))

         C1        C2        C3
A  5.666667  5.166667  2.000000
B  2.000000  2.000000  3.000000
C  3.000000  5.166667  4.666667
D  4.666667  3.000000  5.666667
2
msg

以下のコードは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())

(上記の例の参照分布として、最初の列を2番目の列に正規化します。)

0
deeenes

私はpandasに不慣れで、質問に遅れていますが、答えも役立つかもしれないと思います。それは素晴らしい answer from @ 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

ここでの主な違いは、実際のアプリケーションに近いことです。多くの場合、数値データの行列があるだけです。その場合、元の答えで十分です。

テキストベースのデータもそこにある場合があります。これにより、数値データの列colsを指定し、それらの列で分位正規化を実行できます。最後に、元のデータフレームから非数値(または正規化されない)列をマージして戻します。

例えばwikiの例に「メタデータ」(char)を追加した場合:

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
0
SumNeuron

平均ではなく各行の中央値を使用する方がおそらくより堅牢です(Shawn。Lの code に基づく):

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
xspensiv