Как сделать более эффективный код для поиска нескольких строк в столбце в pandas - PullRequest
5 голосов
/ 22 января 2020

Я новичок (минус 1 класс по основам), работаю в биолаборатории. У меня есть скрипт, который отправляет данные RNAseq из двух разных типов ячеек и запускает тестирование в другом наборе данных. Это работало для этого приложения, но код выглядит очень скучно sh, и я знаю, что буду много писать подобные сценарии.

Как мне лучше написать следующий код, чтобы сделать его более эффективным?

Цель программы:

  1. сравнить список генов с библиотекой rnaseq двух типов клеток, если библиотека содержит этот ген, выполнить тестирование типа клетки 1 против типа клетки 2
  2. вывод результатов.

:

import pandas as pd
from scipy.stats import ttest_ind
rnatest = {'Gene symbol':["GeneA","GeneB"],"rnaseq1A":[1,1.5],"rnaseq1B":[1.3,1.2],"rnaseq2A":[2.3,2.7],"rnaseq2B":[2,2.6]} 
df = pd.DataFrame(rnatest)
GOIlist = ["GeneA","GeneB"]
GOI = []
mu = [] 
pval = []
for index, row in df.iterrows():
  if row['Gene symbol'] in GOIlist:
    t, p = ttest_ind([row["rnaseq1A"],row["rnaseq1B"]],[row["rnaseq2A"],row["rnaseq2B"]])
    GOI.append(row['Gene symbol'])
    mu.append(t)
    pval.append(p)
df2 = {'Gene symbol':GOI,"tVAL":mu, "pVAL":pval}
df2 = pd.DataFrame(df2)
print(df2)  

1 Ответ

5 голосов
/ 23 января 2020

Преимущество использования pandas заключается в том, что вы можете выполнять операции по столбцам. Они , как правило, более эффективны, чем итерирование по DataFrame с параметром для l oop.

. Я немного изменил ваш df, чтобы показать вам эффект отфильтровывания нужных нам строк .

>>> import pandas as pd
>>> from scipy.stats import ttest_ind
>>> GOIlist = ["GeneA","GeneB"]
>>> rnatest = {'Gene symbol':["GeneA","GeneB", "GeneC"],"rnaseq1A":[1,1.5,2],"rnaseq1B":[1.3,1.2,1.1],"rnaseq2A":[2.3,2.7,3.1],"rnaseq2B":[2,2.6,3.2]} 
>>> df = pd.DataFrame(rnatest)
>>> print(df)

    Gene symbol     rnaseq1A    rnaseq1B    rnaseq2A    rnaseq2B
0   GeneA           1.0         1.3         2.3         2.0
1   GeneB           1.5         1.2         2.7         2.6
2   GeneC           2.0         1.1         3.1         3.2

Теперь, как бы я переписал ваш код:

  1. Используйте set_index, чтобы сделать строку Gene symbol индексом, это ускоряет время поиска (особенно если у вас большие фреймы данных)
  2. Используйте loc для фильтрации строк с символом гена в GOIlist
  3. Создайте два новых столбца pVal и tVal, для которых Вы назначаете выход ttest_ind. Обратите внимание, что нам больше не нужно перебирать строки.
  4. Опционально, удалите столбцы rnaseq*, если вы не хотите видеть их в своих выходных данных.

В коде:

>>> df3 = df.set_index(['Gene symbol'])
>>> df3 = df3.loc[GOIlist]
>>> df3['tVal'], df3['pVal'] = ttest_ind([df3["rnaseq1A"], df3["rnaseq1B"]], [df3["rnaseq2A"], df3["rnaseq2B"]])
>>> df3 = df3.drop(['rnaseq1A', 'rnaseq1B', 'rnaseq2A', 'rnaseq2B'], axis=1)
>>> print(df3)
            tVal        pVal
Gene symbol         
GeneA       -4.714045   0.042174
GeneB       -8.221922   0.014473

Итак, насколько эффективнее теперь этот код?

Если я искусственно увеличу размер нашего DataFrame в 10.000 раз (то есть всего 30.000 строк вместо 3)

n = 10_000
rnatest = {'Gene symbol':["GeneA","GeneB", "GeneC"]*n, "rnaseq1A":[1,1.5,2]*n, "rnaseq1B":[1.3,1.2,1.1]*n, "rnaseq2A":[2.3,2.7,3.1]*n, "rnaseq2B":[2,2.6,3.2]*n} 
df = pd.DataFrame(rnatest)

тогда я могу использовать timeit для измерения времени выполнения кода. Для вашего первоначального подхода я получаю результат:

13.7 s ± 555 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

, в то время как мой подход заканчивается в

45.2 ms ± 1.27 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

, так что * более чем в 300 раз ускорение!

...