2016-06-21 2 views
9

Проще говоря, как применить нормировку квантилей на большом кадре данных Pandas (возможно, 200000 строк) в Python?квантильная нормализация на pandas dataframe

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.0322e-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 предоставляет интерфейс для запуска R в подпроцессе python, я проверяю его снова в R напрямую, и результат все еще не прав. В результате я считаю, что причина в том, что метод в R неверен.

+0

Я удалил "R" метки поскольку вы (1) не используете R и (2) не хотите R в ответе. Но если вы скажете, что «R не может вычислить правильный результат», это звучит так, будто вы либо пренебрегаете R (с какой целью?), Либо хотите, чтобы кто-то исправил ваш неопубликованный код. В любом случае, возможно, я не понимаю, что вы хотите: нормализация квантилей требует источника и целевого распределения, и я не уверен, что вы предоставляете здесь. Можете ли вы пояснить, пожалуйста? – r2evans

+0

@ r2evans Спасибо за ваш комментарий, и я уже отредактировал этот вопрос. FYI, код, который я googled, запускает R как подпроцесс Python. После запуска R я обнаружил, что результат был неправильным. Кроме того, я не уверен, что вы подразумеваете под «целевым распределением». Согласно Wiki, вычисление нормировки квантиля не включает этот термин. Вопрос, надеюсь, я ясно дал понять, заключается в применении количественной нормализации данных, которые я дал. –

+0

Вы правы, мой термин «цель» на самом деле не очень хорош. Ссылки на wiki * «два одинаковых дистрибутива» *, поэтому мне было интересно, каковы ваши два дистрибутива. Теперь, когда вы предоставили дополнительный код (и данные, определенные как 'matrix'), я смущен тем, что ваши фактические данные должны быть квантово-нормированными. (Возможно, глупый вопрос, но возможно ли, что матрица транспонирована по сравнению с тем, что вам действительно нужно?) – r2evans

ответ

2

Хорошо, я реализовал метод самостоятельно относительно высокой эффективности.

После окончания этой логики кажется легкой, но, во всяком случае, я решил опубликовать ее здесь, чтобы кто-то чувствовал себя смущенным, как я был, когда не мог найти доступный код.

Код находится в GitHub: Quantile Normalize

10

Используя пример набора данных из 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 
+1

элегантное использование 'groupby',' map' и 'stacking/unstacking'. Вы разработчик 'pandas'? –

+3

Спасибо. Нет, я просто обычный пользователь. – ayhan

0

Возможно, более надежным, чтобы использовать медиану на каждой строке, а не средним (на основе code от Шона. 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 
0

Приведенный ниже код дает одинаковый результат как 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 на второй в качестве распределения эталонного в приведенном выше примере.)

0

Я новичок в пандах и на прошлой неделе, но я думаю, что ответ также может быть полезен. Он строит прочь большой answer из @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) к примеру вики:

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