pandas 数据帧上的分位数归一化
quantile normalization on pandas dataframe
简单来说,如何对 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中的方法不对。
好的我自己实现了效率比较高的方法
完成后,这个逻辑似乎有点简单,但无论如何,我决定在这里 post 因为任何人都感到困惑,就像我无法通过 google 搜索可用代码时一样。
代码在github:Quantile Normalize
使用来自 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
在每一行上使用中位数而不是均值可能更稳健(基于来自 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
下面的代码给出了与 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())
(将第一列标准化为第二列作为上例中的参考分布。)
我是 pandas 的新手,问题来晚了,但我认为答案也可能有用。它建立在伟大的 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 示例添加了一些 'meta-data' (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 包 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 所指出的,none 这里的解决方案考虑了关系。我制作了一个名为 qnorm which handles ties, and correctly recreates the Wikipedia quantile normalization example:
的 python 包
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 或 conda 完成安装
pip install qnorm
或
conda config --add channels conda-forge
conda install qnorm
这是一个小调整,但我想很多人已经注意到@ayhan 的 .
中的微妙 'flaw'
我对其进行了一些小的调整,得到了 'correct' 的答案,而不必为这样一个极其简单的功能求助于任何外部库。
唯一需要调整的是 [Add interpolated values
] 部分。
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
简单来说,如何对 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中的方法不对。
好的我自己实现了效率比较高的方法
完成后,这个逻辑似乎有点简单,但无论如何,我决定在这里 post 因为任何人都感到困惑,就像我无法通过 google 搜索可用代码时一样。
代码在github:Quantile Normalize
使用来自 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
在每一行上使用中位数而不是均值可能更稳健(基于来自 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
下面的代码给出了与 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())
(将第一列标准化为第二列作为上例中的参考分布。)
我是 pandas 的新手,问题来晚了,但我认为答案也可能有用。它建立在伟大的
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 示例添加了一些 'meta-data' (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 包 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 所指出的,none 这里的解决方案考虑了关系。我制作了一个名为 qnorm which handles ties, and correctly recreates the Wikipedia quantile normalization example:
的 python 包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 或 conda 完成安装
pip install qnorm
或
conda config --add channels conda-forge
conda install qnorm
这是一个小调整,但我想很多人已经注意到@ayhan 的
我对其进行了一些小的调整,得到了 'correct' 的答案,而不必为这样一个极其简单的功能求助于任何外部库。
唯一需要调整的是 [Add interpolated values
] 部分。
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