我有一个DataFrame
,其中有两列:sequence
和id
,例如:
import pandas as pd
data = {"id":["seq1", "seq2", "seq3"], "sequence":["ATCTGC", "AACTGC", "AACTCC"]}
df = pd.DataFrame(data)
(这是+ 20k序列的实际数据集的一个小例子)
我正试图以有效的方式获得序列距离矩阵。距离被理解为每个序列之间不同字符的数量
为此,我需要在sequence
列中应用所有值的功能。
我目前的代码如下:
def count_differences( seq, df ):
return df.apply(lambda x: sum(1 for i, j in zip(x["sequence"], seq) if i != j), axis=1)
df2 = df.apply(lambda x: count_differences( x["sequence"], df), axis=1)
df2 = df2.rename(df["id"], axis="columns").rename(df["id"], axis="rows")
# seq1 seq2 seq3
# seq1 0 1 2
# seq2 1 0 1
# seq3 2 1 0
这是申请中的申请。它工作正常,但是当我在所有序列上运行时,它确实需要花费很多时间。
有更有效的方法吗?我一直试图看看是否可以通过Series.map
来加快速度,但到目前为止我还没有找到任何解决方案。
答案 0 :(得分:1)
这是一个想法,涉及下降到numpy
。
有几个步骤,但由于基础工作是通过numpy
数字数组完成的,因此效率可能更高。
import pandas as pd, numpy as np
data = {"id":["seq1", "seq2", "seq3"], "sequence":["ATCTGC", "AACTGC", "AACTCC"]}
df = pd.DataFrame(data)
a = np.array(list(map(list, df['sequence'])))
values = np.unique(a, return_inverse=True)[1].reshape(a.shape)
n = len(a)
d = {(i, j): np.sum(a[i]!=a[j]) for i in range(n) for j in range(n) if j > i}
res = np.zeros((n, n))
keys = list(zip(*d.keys()))
res[keys[0], keys[1]] = list(d.values())
res += res.T
df_res = pd.DataFrame(res, columns=data['id'], index=data['id'], dtype=int)
# seq1 seq2 seq3
# seq1 0 1 2
# seq2 1 0 1
# seq3 2 1 0
<强>解释强>
numpy
数组,每个元素都是一个字母。np.unique
对数组进行分解(即将每个字母与数字相关联)。np.sum
查找分解数组中行之间的字母差异,并将结果添加到字典中。由于您的结果是三角形,因此只执行一半的计算。numpy
数组,并添加反向以使三角形数组完整。效果基准
我看到性能提升了约7倍。
%timeit original(df) # 3.32s
%timeit jp(df) # 461ms
import pandas as pd, numpy as np
data = {"id":["seq1", "seq2", "seq3"], "sequence":["ATCTGC", "AACTGC", "AACTCC"]}
df = pd.DataFrame(data)
df = pd.concat([df]*100)
def original(df):
def count_differences( seq, df ):
return df.apply(lambda x: sum(1 for i, j in zip(x["sequence"], seq) if i != j), axis=1)
df2 = df.apply(lambda x: count_differences( x["sequence"], df), axis=1)
return df2
def jp(df):
a = np.array(list(map(list, df['sequence'])))
values = np.unique(a, return_inverse=True)[1].reshape(a.shape)
n = len(a)
d = {(i, j): np.sum(a[i]!=a[j]) for i in range(n) for j in range(n) if j > i}
res = np.zeros((n, n))
keys = list(zip(*d.keys()))
res[keys[0], keys[1]] = list(d.values())
res += res.T
df_res = pd.DataFrame(res, columns=range(len(df['id'])), index=range(len(df['id'])), dtype=int)
return df_res