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

Posted

技术标签:

【中文标题】Pandas:两个数据帧之间的精确字符串匹配,带有位置【英文标题】:Pandas: exact string match, with position, between two dataframes 【发布时间】:2018-11-18 23:51:49 【问题描述】:

假设我有以下两个数据框。

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

每个 df2["BaseCall"] 与每个 df1["seq"] 返回一个数据框,其中包含每个 df1["gene"] 上的位置列表 在哪里找到任何 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

【问题讨论】:

问题:我看不出将数据存储在 dataframe2 中的意义。这不应该只是一个简单的字典,其中键是 BaseCall,值是 FeatureID?类似s = pd.Series(item['BaseCall']: item['FeatureID'] for item in data2) (1) Position 在您的输出中是如何计算的?你能举一个具体的例子吗? (2)这是您完整的预期输出吗?似乎还有其他匹配项。 (3) 是BasecallBaseCall,还是您的数据中实际上有这两种拼写? Df2 是通过长管道中的许多其他步骤创建的。如果这样更有效,我不反对其他数据结构。 安德鲁,位置当前已提交 l 使用 ngrams 函数最后一行显示的枚举函数计算返回字符串的位置,然后我可以从 df1.start 中减去。 是的,这不是一个完整的输出。我不确定额外的信息是否有帮助,我会更新它。 【参考方案1】:

使用re.finditer() 和some Pandas hacking 计算给定seq 中可能多次出现的相同BaseCall

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 没有单独的列。

【讨论】:

非常感谢您的快速回复。不幸的是,下游覆盖分析需要位置,这就是为什么我需要在这里跟踪它。 好的,那么很高兴在您的帖子中明确说明您的输出要求。 (“总体目标”是我的工作目标。)稍后将添加 position 非常感谢@andrew_reece,感谢您耐心等待我的凌乱帖子!

以上是关于Pandas:两个数据帧之间的精确字符串匹配,带有位置的主要内容,如果未能解决你的问题,请参考以下文章

在 pandas 中,如何在具有匹配行和列的 3 个单独数据帧之间建立相关矩阵?

Pandas:使用部分字符串匹配聚合不同数据帧的几列

Pandas - 匹配来自两个数据帧的两列并在 df1 中创建新列

通过在两个 Pandas 数据帧之间迭代来识别相似的值。

Python Pandas - 查找两个数据帧之间的差异

从两个 Pandas DataFrames 向数据帧添加一列,当前使用两个带有条件的循环:有更快的方法吗?