Pandas:两个数据帧之间的精确字符串匹配,位置

时间:2018-06-08 21:44:09

标签: python pandas dataframe string-matching n-gram

假设我有两个数据帧。

实际上,两个数据帧每个都会有大约一百万行,所以我想找到最有效的比较方法:

  • 每个df2 [“BaseCall”]与每个df1 [“seq”]
  • 返回一个数据框,其中包含每个df1 [“基因”]上的位置列表 哪里找到了df2 [“BaseCall”]

总体目标是计算在基因中找到每个feature_id的次数,并捕获位置信息以供下游使用。

    # break fasta_df sequences and mutation seqs up into kmers
    data = [{"gene":"pik3ca", "start":"179148724", "stop":"179148949","seq":"TTTGCTTTATCTTTTGTTTTTGCTTTAGCTGAAGTATTTTAAAGTCAGTTACAG"},
    {"gene":"brca1", "start":"179148724", "stop":"179148949","seq":"CAATATCTACCATTTGTTAACTTTGTTCTATTATCATAACTACCAAAATTAACAGA"},
    {"gene":"kras1", "start":"179148724", "stop":"179148949","seq":"AAAACCCAGTAGATTTTCAAATTTTCCCAACTCTTCCACCAATGTCTTTTTACATCT"}] 

    # test dataframe with input seq    
    df1 = pd.DataFrame(data)

    data2 = [{"FeatureID":"1_1_15", "BaseCall":"TTTGTT"},
         {"FeatureID":"1_1_15", "BaseCall":"AATATC"},
         {"FeatureID":"1_1_16", "BaseCall":"GTTTTT"},
         {"FeatureID":"1_1_16", "BaseCall":"GTTCTA"},
         ]

    df2= pd.DataFrame(data2)

输出应该类似于:

|  gene  |   feature_id   |   BaseCall   |   Position 
| pik3ca |   1_1_15       |   TTTGTT     |    12
| pik3ca |   1_1_16       |   GTTTTT     |    15
| brca1  |   1_1_16       |   GTTCTA     |    24
| brca1  |   1_1_15       |   AATATC     |    1
| brca1  |   1_1_15       |   TTTGTT     |    12
| brca1  |   1_1_15       |   TTTGTT     |    21

当我在一个seq上只使用一个测试基本调用时,这个ngram函数似乎工作得很好,但是我很难找到使用apply方法的最有效方法,其中一个参数来自两个不同的数据帧。或者可能有更好的方法在两个数据帧之间找到匹配的字符串/位置?

 def ngrams(string, target):
    ngrams = zip(*[string[i:] for i in range(6)])
    output = [''.join(ngram)for ngram in ngrams]
    indices = [(i,x) for i, x in enumerate(output) if x == target]
    return indices

1 个答案:

答案 0 :(得分:1)

使用re.finditer()some Pandas hacking考虑​​可能在给定BaseCall内多次出现同一seq

import re

def match_basecall(pattern, string):
    match = re.finditer(pattern, string)
    start_pos = [m.start() for m in match]
    if not start_pos:
        return None
    return start_pos

matches = df2.BaseCall.apply(lambda bc: df1.seq.apply(lambda x: match_basecall(bc, x)))
matches.columns = df1.gene

merged = matches.merge(df2, left_index=True, right_index=True)

melted = merged.melt(id_vars=["FeatureID", "BaseCall"], 
                     var_name="gene", 
                     value_name="Position").dropna()

melted
  FeatureID BaseCall    gene  Position
0    1_1_15   TTTGTT  pik3ca      [12]
2    1_1_16   GTTTTT  pik3ca      [15]
4    1_1_15   TTTGTT   brca1  [12, 21]
5    1_1_15   AATATC   brca1       [1]
7    1_1_16   GTTCTA   brca1      [24]

多个BaseCall匹配在Position中表示为列表项,但我们所需的输出将每个匹配放在一个单独的行上。我们可以使用apply(pd.Series)将一列列表分成多列,然后stack()将列转换为行:

stacked = (pd.DataFrame(melted.Position.apply(pd.Series).stack())
             .reset_index(level=1, drop=True)
             .rename(columns={0:"Position"}))

final = melted.drop("Position", 1).merge(stacked, left_index=True, right_index=True)

final
  FeatureID BaseCall    gene  Position
0    1_1_15   TTTGTT  pik3ca      12.0
2    1_1_16   GTTTTT  pik3ca      15.0
4    1_1_15   TTTGTT   brca1      12.0
4    1_1_15   TTTGTT   brca1      21.0
5    1_1_15   AATATC   brca1       1.0
7    1_1_16   GTTCTA   brca1      24.0

我们groupby FeatureIDgene可以获得发生总数:

final.groupby(["FeatureID", "gene"]).Position.count()

FeatureID  gene  
1_1_15     brca1     3
           pik3ca    1
1_1_16     brca1     1
           pik3ca    1

注意:每个OP输出,不包括没有匹配的组合 另外,假设此处BaseCall只是一列,并且BasecallBaseCall不同列。