Ускорение реализации сходства канонических улыбок соединений с помощью rdkit - PullRequest
0 голосов
/ 15 февраля 2020

Этот подражатель биоинформатик нуждается в вашей помощи. Код ниже находит сходство канонических улыбок составов, используя rdkit. После некоторого исследования я понимаю, что это должно быть O (n)! (или нет?) потому что для небольшого файла из 944 записей это заняло 20 минут, а для самого большого, который составляет 330 000 записей, работало более 30 часов. Теперь, я теперь, что одна из его проблем состоит в том, что он не сравнивает элементы только один раз, так что это один из факторов, который замедляет его. Я прочитал здесь, что вы можете использовать библиотеку itertools для быстрого сравнения, но как лучше сделать этот код лучше? Буду признателен за любую помощь, пока я пытаюсь научиться:)

from rdkit import Chem
from rdkit import DataStructs
from rdkit.Chem import AllChem
import pandas as pd


l =[]
s1 = []
s2 = []
d1 = []
d2 = []
with open('input_file.csv', 'r') as f:
    df = pd.read_csv(f, delimiter = ',', lineterminator = '\n', header = 0)
    for i in range(0, df.shape[0]):
        l.append(df.iloc[i, 1])


for i in range(0, df.shape[0]):
    for j in range(0, df.shape[0]):
        m1 = Chem.MolFromSmiles(df.iloc[i, 1])
        fp1 = AllChem.GetMorganFingerprint(m1,2)
        m2 = Chem.MolFromSmiles(df.iloc[j, 1])
        fp2 = AllChem.GetMorganFingerprint(m2,2)
        sim = DataStructs.DiceSimilarity(fp1,fp2)
        if sim >= 0.99:
            s1.append(i)
            s2.append(j)
for k in range(0, len(s1)):
    if df.iloc[s1[k], 0] != df.iloc[s2[k], 0]:
        d1.append(df.iloc[s1[k], 0])
        d2.append(df.iloc[s2[k], 0])
if len(d1) != 0:
    with open('outputfile.tsv', 'a') as f2:
        for o in range(0, len(d1)):
            f2.write(str(d1[o]) + '\t' + str(d2[0]) + '\n')

1 Ответ

2 голосов
/ 15 февраля 2020

Я понятия не имею, что должен делать алгоритм, поэтому я не буду его комментировать. Но вы заявляете, что:

После некоторых исследований я понимаю, что это должно быть O (n)!

Что представляет собой n? Если временная сложность алгоритма линейна относительно количества строк в вашем наборе данных, то ваша реализация должна быть неправильной. В вашем коде есть два вложенных цикла, оба с длиной n, что означает, что ваш алгоритм в лучшем случае находится в O(n^2) (не считая того, что делают другие функции внутри l oop).

Вот несколько советов, как ускорить код до определенной степени (обычно при работе с pandas).

Вы должны избегать выполнения итераций самостоятельно и избегать превращения pandas структур данных в python списки. Вот пример:

for i in range(0, df.shape[0]):
        l.append(df.iloc[i, 1])

Если вам действительно нужно сохранить это в другой переменной, тогда вы можете использовать

l = df.iloc[:, 1].copy()

Это будет быстрее и не превратит эту серию в список (но я не вижу, чтобы l использовался где-либо в вашем коде, поэтому вам, вероятно, следует полностью его удалить).

Другой пример - когда вы вычисляете эти функции внутри вложенного l oop ( опять же, я не знаю, что они делают, но это на самом деле не имеет значения).

for i ...
    for j ...
        m1 = Chem.MolFromSmiles(df.iloc[i, 1])
        fp1 = AllChem.GetMorganFingerprint(m1,2) 
        m2 = Chem.MolFromSmiles(df.iloc[j, 1])
        fp2 = AllChem.GetMorganFingerprint(m2,2)

Во-первых, вы вычисляете одни и те же значения дважды, что может занять много времени, и вы делаете это в рамках своего custom l oop, что тоже не лучшая идея.

Вместо этих 4 строк (6, включая операторы l oop), вы можете создать новый столбец с fp значениями:

df["fp"] = df.iloc[:, i].copy()
df["fp"].apply(lambda x: AllChem.GetMorganFingerprint(Chem.MolFromSmiles(x), 2))

Таким образом, вам не нужно вычислять значения дважды, и вам не нужно писать свой собственный l oop (по крайней мере, для этой части).

На этом этапе вам необходимо выяснить, как работает упомянутый алгоритм O(n), но я предполагаю, что он может быть преобразован в чисто векторные операции, что, вероятно, будет наиболее эффективной реализацией.

...