使用Python提取Newick格式的分支长度

5 投票
3 回答
2559 浏览
提问于 2025-04-18 03:24

我在Python中有一个列表,里面只有一个项目,这个项目是用Newick格式写的树,内容如下:

['(BMNH833953:0.16529463651919140688,(((BMNH833883:0.22945757727367316336,(BMNH724182a:0.18028180766761139897,(BMNH724182b:0.21469677818346077913,BMNH724082:0.54350916483644962085):0.00654573856803835914):0.04530853441176059537):0.02416511342888815264,(((BMNH794142:0.21236619242575086042,(BMNH743008:0.13421900772403019819,BMNH724591:0.14957653992840658219):0.02592135486124686958):0.02477670174791116522,BMNH703458a:0.22983459269245612444):0.00000328449424529074,BMNH703458b:0.29776257618061197086):0.09881729077887969892):0.02257522897558370684,BMNH833928:0.21599133163597591945):0.02365043128986757739,BMNH724053:0.16069861523756587274):0.0;']

在树的格式中,它看起来是这样的:

在这里输入图片描述

我正在尝试写一些代码,去查找这个列表中的项目,并返回那些通过长度为0的分支连接的ID(格式是BMNHxxxxxx,红色高亮的部分)。我考虑过使用正则表达式,比如:

JustTree = []
with JustTree as f:
    for match in re.finditer(r"(?<=Item\sA)(?:(?!Item\sB).){50,}", subject, re.I):
        f.extend(match.group()+"\n") 

这个想法是参考了另一个StackOverflow的回答,其中项目A是':',因为分支长度总是在':'后面,而项目B可以是','、')'或';',因为这三种字符是分隔符,但我对正则表达式不够熟悉,无法做到这一点。

在这种情况下,我希望代码能输出['BMNH703458a', 'BMNH703458b'],如果我能把这个改成也包括通过用户定义的分支长度(比如0.01)连接的ID,那就太有用了。

如果有人有任何建议,或者能指引我找到有用的答案,我将非常感激。

3 个回答

2

有几个Python库可以支持newick格式。ETE工具包可以读取newick字符串,并把树当作Python对象来操作:

from ete2 import Tree
tree = Tree(newickFile)
print tree

可以选择几种newick的子格式,即使分支的距离是用科学计数法表示的,它们也能被正确解析。

from ete2 import Tree
tree = Tree("(A:3.4, (B:0.15E-10,C:0.0001):1.5E-234);")
2

我知道你的问题已经有人回答了,不过如果你想把数据以嵌套列表的形式呈现,而不是简单的字符串,可以参考下面的代码:

import re
import pprint

a="(BMNH833953:0.16529463651919140688,(((BMNH833883:0.22945757727367316336,(BMNH724182a:0.18028180766761139897,(BMNH724182b:0.21469677818346077913,BMNH724082:0.54350916483644962085):0.00654573856803835914):0.04530853441176059537):0.02416511342888815264,(((BMNH794142:0.21236619242575086042,(BMNH743008:0.13421900772403019819,BMNH724591:0.14957653992840658219):0.02592135486124686958):0.02477670174791116522,BMNH703458a:0.22983459269245612444):0.00000328449424529074,BMNH703458b:0.29776257618061197086):0.09881729077887969892):0.02257522897558370684,BMNH833928:0.21599133163597591945):0.02365043128986757739,BMNH724053:0.16069861523756587274):0.0;"

def tokenize(str):
  for m in re.finditer(r"\(|\)|[\w.:]+", str):
    yield m.group()

def make_nested_list(tok, L=None):
  if L is None: L = []
  while True:
    try: t = tok.next()
    except StopIteration: break
    if   t == "(": L.append(make_nested_list(tok))
    elif t == ")": break
    else:
      i = t.find(":"); assert i != -1
      if i == 0: L.append(float(t[1:]))
      else:      L.append([t[:i], float(t[i+1:])])
  return L

L = make_nested_list(tokenize(a))
pprint.pprint(L)
2

好的,这里有一个正则表达式,用来提取只有数字(可能带小数点)的内容:

\b[0-9]+(?:\.[0-9]+)?\b

这里的\b是用来确保数字前后没有其他数字、字母或下划线的,这叫做“词边界”。

[0-9]+是用来匹配多个数字的。

(?:\.[0-9]+)?是一个可选的部分,意思是它可以匹配也可以不匹配。如果在第一个[0-9]+后面有一个小数点和数字,那么它就会匹配这些。如果没有,就不会匹配。这个部分本身是匹配一个小数点,后面至少跟着一个数字。

你可以用re.findall来把所有匹配到的结果放到一个列表里:

import re
NewickTree = ['(BMNH833953:0.16529463651919140688,(((BMNH833883:0.22945757727367316336,(BMNH724182a:0.18028180766761139897,(BMNH724182b:0.21469677818346077913,BMNH724082:0.54350916483644962085):0.00654573856803835914):0.04530853441176059537):0.02416511342888815264,(((BMNH794142:0.21236619242575086042,(BMNH743008:0.13421900772403019819,BMNH724591:0.14957653992840658219):0.02592135486124686958):0.02477670174791116522,BMNH703458a:0.22983459269245612444):0.00000328449424529074,BMNH703458b:0.29776257618061197086):0.09881729077887969892):0.02257522897558370684,BMNH833928:0.21599133163597591945):0.02365043128986757739,BMNH724053:0.16069861523756587274):0.0;']

pattern = re.compile(r"\b[0-9]+(?:\.[0-9]+)?\b")

for tree in NewickTree:
    branch_lengths = pattern.findall(tree)
    # Do stuff to the list branch_lengths
    print(branch_lengths)

对于这个列表,你会得到这样的输出:

['0.16529463651919140688', '0.22945757727367316336', '0.18028180766761139897',
 '0.21469677818346077913', '0.54350916483644962085', '0.00654573856803835914', 
 '0.04530853441176059537', '0.02416511342888815264', '0.21236619242575086042',
 '0.13421900772403019819', '0.14957653992840658219', '0.02592135486124686958', 
 '0.02477670174791116522', '0.22983459269245612444', '0.00000328449424529074',
 '0.29776257618061197086', '0.09881729077887969892', '0.02257522897558370684',
 '0.21599133163597591945', '0.02365043128986757739', '0.16069861523756587274',
 '0.0']

撰写回答