熊猫系列:将所有值与所有

时间:2018-03-12 12:41:56

标签: python pandas

我有一个DataFrame,其中有两列:sequenceid,例如:

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来加快速度,但到目前为止我还没有找到任何解决方案。

1 个答案:

答案 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