python dataframe 分位数_关于python:对熊猫数据框的分位数归一化

简而言之,如何在Python中的大型Pandas数据帧(大约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.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中的方法是错误的。

我删除了" R"标签,因为您(1)使用R并没有(2)不想在答案中使用R。但是,如果您说" R无法计算出正确的结果",则听起来好像您是在贬低R(目的是什么?),还是希望有人更正您未发布的代码。无论哪种方式,也许我都会误解您想要的东西:分位数归一化需要源和目标分布,而我不确定您在此提供的内容。你能澄清一下吗?

@ r2evans感谢您的评论,我已经编辑了问题。仅供参考,我用Google搜索的代码将R作为Python的子进程运行。直接运行R之后,我发现结果是错误的。此外,我不确定目标分配是什么意思。根据Wiki,分位数归一化的计算不涉及该术语。希望我已经阐明的问题是对我给出的数据应用分位数归一化。

您是对的,我对"目标"的称呼不是很好。 Wiki引用"使两个分布相同",因此我想知道您的两个分布是什么。现在,您提供了其他代码(和数据,定义为matrix),Im感到困惑的是您的实际数据需要进行量化处理。 (也许是一个愚蠢的问题,但是与您实际需要的相比,是否有可能对矩阵进行了转置?)

@ r2evans对造成的混乱,我深表歉意。仅供参考,实际数据是(2119055,124)矩阵。我上面提供的数据只是测试的一小部分。是的,我确实考虑过移调的问题。如您所见,在示例代码中,矩阵为(3,5),但归一化结果为(5,3),因此我总结出要使用此代码,我需要首先对矩阵进行转置。更清楚地说,我的数据是(4,6),并使用代码我将转置的数据即(6,4)分配给变量matrix,然后继续。

使用维基百科文章中的示例数据集:

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

然后,所得的Series 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

优雅地使用groupby,map和stackingunstacking。 您是pandas开发人员吗?

谢谢。 不,我只是普通用户。

@ayhan为什么在第一行和第二行中使用不同的排名方法,即first与min?

好吧,我自己实施了效率相对较高的方法。

完成后,这种逻辑似乎很简单,但是无论如何,我决定将其发布在这里,因为任何人都感到困惑,就像我无法搜索可用代码时那样。

代码在github中:Quantile Normalize

我是熊猫的新手,对这个问题迟到了,但是我认为答案也可能有用。它基于@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

下面的代码给出与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())

(在上面的示例中,将第一列和第二列标准化为参考分布。)

在每一行上使用中位数而不是平均值可能会更可靠(基于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