Python 数据帧上的分位数归一化

Python 数据帧上的分位数归一化,python,deep-learning,data-science,Python,Deep Learning,Data Science,简单地说,如何在Python中对大数据帧(可能是2000000行)应用分位数规范化 另外,我知道有一个名为rpy2的包可以在子流程中运行R,在R中使用分位数规格化。但事实是,当我使用以下数据集时,R无法计算正确的结果: 5.690386092696389541e-05,2.051450375415418849e-05,1.963190184049079707e-05,1.258362869906251862e-04,1.503352476021528139e-04,6.8813415863556

简单地说,如何在Python中对大数据帧(可能是2000000行)应用分位数规范化

另外,我知道有一个名为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
编辑:

我想要的是:

给定上面显示的数据,如何按照中的步骤应用分位数归一化

我在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中的方法是错误的。

好的,我自己实现了这个方法,效率相对较高

完成后,这个逻辑似乎有点简单,但无论如何,我决定将它发布在这里,因为任何人都会感到困惑,就像我无法用谷歌搜索可用代码一样


代码在github中:

使用以下示例数据集:

对于每个等级,可使用以下公式计算平均值:

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\u 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

使用每行的中位数而不是平均值可能更稳健(基于Shawn.L):


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

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

我对熊猫还不熟悉,但我认为答案也可能有用。它建立在伟大的基础之上:

这里的主要区别是更接近于一些真实世界的应用程序。通常情况下,只有数值数据矩阵,在这种情况下,原始答案就足够了

有时也会有基于文本的数据。这允许您指定数值数据的列
cols
,并对这些列运行分位数规范化。最后,它将从原始数据帧中合并回非数字(或不规范化)列

e、 g.如果您在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

值得注意的一点是,ayhan和shawn的代码都使用较小的秩平均值表示领带,但如果使用R package 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

正如@msg所指出的,这里的任何解决方案都没有考虑到关系。我制作了一个名为的python包,该包处理领带,并正确地重新创建:

可以使用pip或conda进行安装

pip install qnorm


这只是一个小小的调整,但我想很多人都注意到了@ayhan中的微妙“缺陷”

我对它做了一个小的调整,得到了“正确”的答案,同时不必求助于任何外部库来实现如此简单的函数

唯一需要的调整是[
添加插值
]部分

import pandas as pd

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}})

def quant_norm(df):
    ranks = (df.rank(method="first")
              .stack())
    rank_mean = (df.stack()
                   .groupby(ranks)
                   .mean())
    # Add interpolated values in between ranks
    finer_ranks = ((rank_mean.index+0.5).to_list() +
                    rank_mean.index.to_list())
    rank_mean = rank_mean.reindex(finer_ranks).sort_index().interpolate()
    return (df.rank(method='average')
              .stack()
              .map(rank_mean)
              .unstack())
quant_norm(df)

Out[122]: 
         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

我删除了“R”标记,因为您(1)没有使用R,并且(2)不希望答案中包含R。但是如果你说“R不能计算正确的结果”,听起来你要么贬低R(为了什么目的?),要么希望有人纠正你未发布的代码。不管怎样,也许我误解了你想要什么:分位数标准化需要一个源和目标分布,我不确定你在这里提供的是什么。你能澄清一下吗?@r2evans谢谢你的评论,我已经编辑了这个问题。仅供参考,我用谷歌搜索的代码将R作为Python的子进程运行。直接运行R之后,我发现结果是错误的。此外,我不确定你所说的“目标分布”是什么意思。根据维基,分位数标准化的计算不涉及这个术语。希望我能说清楚,问题是对我给出的数据进行分位数归一化。你是对的,我的“目标”一词不是很好。wiki引用了“使两个发行版完全相同”,所以我想知道您的两个发行版是什么。既然您提供了额外的代码(和数据,定义为
矩阵
),我不知道哪一个是要量化的实际数据。(也许这是个愚蠢的问题,但与您实际需要的矩阵相比,矩阵是否可能被转置?@r2evans我为我造成的混乱感到抱歉。仅供参考,实际数据为(2119055124)矩阵。我上面给出的数据只是测试数据的一小部分。是的,我确实考虑过转置问题。正如您可以看到的,在示例代码中,矩阵是(3,5),但归一化结果是(5,3),因此我总结说,要使用此代码,我需要首先转置矩阵。更清楚地说,我的数据是(4,6),为了使用代码,我将把转置的数据,即(6,4)分配给变量
矩阵
,然后继续。优雅地使用
分组方式
映射
,以及
堆叠/取消堆叠
。您是
pandas
开发人员吗?谢谢。不,我只是一个普通用户。@ayhan你为什么在第一和第二处理行中使用不同的排名方法,即
first
vs
min
?只是指出(并自我提升)根据维基百科,这不会产生“正确”的结果。我实现了一个快速的方法,可以产生正确的结果,并且可以用conda或pip安装:这看起来很棒!修正相同秩的值所需的唯一方法是使用平均值和插值
> 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
import pandas as pd
import qnorm

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}})

print(qnorm.quantile_normalize(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
pip install qnorm
conda config --add channels conda-forge
conda install qnorm
import pandas as pd

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}})

def quant_norm(df):
    ranks = (df.rank(method="first")
              .stack())
    rank_mean = (df.stack()
                   .groupby(ranks)
                   .mean())
    # Add interpolated values in between ranks
    finer_ranks = ((rank_mean.index+0.5).to_list() +
                    rank_mean.index.to_list())
    rank_mean = rank_mean.reindex(finer_ranks).sort_index().interpolate()
    return (df.rank(method='average')
              .stack()
              .map(rank_mean)
              .unstack())
quant_norm(df)

Out[122]: 
         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