нормализация квантилей на фрейме данных pandas
проще говоря, как применить нормализацию квантилей на большом фрейме данных Pandas (вероятно, 2,000,000 строк) в Python?
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
Edit:
что хочется:
учитывая данные, показанные выше, как применить нормализацию квантиля следующие шаги в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 предоставляет интерфейс для запуска R в подпроцессе python, я тестирую его снова в R напрямую, и результат был все еще неправильный. В результате я думаю, что причина в том, что метод в R ошибочен.
5 ответов
использование примера набора данных из статья в Википедии:
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
Ok Я сам реализовал метод относительно высокой эффективности.
после окончания, эта логика кажется легкой, но, в любом случае, я решил опубликовать ее здесь для любого, кто чувствует себя смущенным, как я был, когда я не мог погуглить доступный код.
код находится в github:Квантильной Нормализации
возможно, более надежный для использования медианы в каждой строке, а не Среднего (на основе код от Шон. 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
приведенный ниже код дает идентичный результат как 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 ко второму в качестве опорного распределения в примере выше.)
Я новичок в панд и поздний вопрос, но я думаю, что ответ тоже может быть полезен. Он строится из Великого ответ С @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
вашего числовые данные и будет выполняться нормализация квантиля на этих столбцах. В конце он объединит нечисловые (или не нормализуемые) столбцы из исходного фрейма данных.
например, если вы добавили некоторые мета-данные' (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