加快迭代长字符串中的字符
Speed up iterating over characters in long strings
我有一个采用 DNA 字符串的代码,其中仅找到 4 个字符:A、C、T 和 G,例如“ATACAAG”,并且对于每个字符,如果找到其他 3 个可能的字符。该代码包括一个字符串循环和另一个可能字符列表循环。问题是字符串很长:多达数十万个字符,所以速度不快并且计算机变热(风扇开始快速运转)。
我正在寻找一种更快的方法。我尝试了列表理解,但它仍然很慢。我还尝试将代码作为 pandas lambda 的函数调用,但每个字符串仍然需要大约一分钟的时间。这是我能得到的最好的吗?
对于每个字符,代码在文件的不同行中记录 3 个备选方案。
代码:
bases = set(list('ACGT'))
alts = {base: list(bases.difference(base)) for base in bases}
def get_variants(data, output_path): # pb: position base, b: base
[open(output_path + f'/{data.symbol}_variants.txt', 'a').writelines(
[f'{data.chromosome}\t{data.end + index}\t{data.end + index}\t{pb}/{b}\t{data.strand}\n' for b in alts[pb]])
for index, pb in enumerate(data.sequence)]
正在为“ATACAAG”调用函数:
get_variants(pandas.Series({'symbol': 'XYZ', 'sequence': 'ATACAAG', 'chromosome': 12, 'start': 9067664, 'end': 9067671, 'strand': '-'}),
'write_an_existing_output_directory_path_here')
输出按以下列排列在文件中:
chromosome number, start position, end position, original character/alternative character, strand (can + or -)
它在文件 XYZ_variants.txt 中产生以下行:
12 9067664 9067664 A/T -
12 9067664 9067664 A/G -
12 9067664 9067664 A/C -
12 9067665 9067665 T/A -
12 9067665 9067665 T/G -
12 9067665 9067665 T/C -
12 9067666 9067666 A/T -
12 9067666 9067666 A/G -
12 9067666 9067666 A/C -
12 9067667 9067667 C/T -
12 9067667 9067667 C/A -
12 9067667 9067667 C/G -
12 9067668 9067668 A/T -
12 9067668 9067668 A/G -
12 9067668 9067668 A/C -
12 9067669 9067669 A/T -
12 9067669 9067669 A/G -
12 9067669 9067669 A/C -
12 9067670 9067670 G/T -
12 9067670 9067670 G/A -
12 9067670 9067670 G/C -
谢谢。
这是我的做法。
从数据帧开始:
symbol sequence chromosome start end strand
0 XYZ ATACAAG 12 9067664 9067671 -
我会 explode
序列 reindex
具有 A/C/G/T 的所有组合,并且只保留初始碱基不同的那个
import numpy as np
df2 = df.assign(base=df['sequence'].apply(list)).explode('base').reset_index()
df2 = (df2.reindex(df2.index.repeat(4))
.assign(variant=np.tile(list('ACGT'), len(df2)))
.loc[lambda d: d['base'].ne(d['variant'])]
.assign(var=lambda d:d['base']+'/'+d['variant'])
)
中间输出:
>>> df2.head()
index symbol sequence chromosome start end strand base variant var
0 0 XYZ ATACAAG 12 9067664 9067671 - A C A/C
0 0 XYZ ATACAAG 12 9067664 9067671 - A G A/G
0 0 XYZ ATACAAG 12 9067664 9067671 - A T A/T
1 0 XYZ ATACAAG 12 9067664 9067671 - T A T/A
1 0 XYZ ATACAAG 12 9067664 9067671 - T C T/C
然后导出:
df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='\t', index=False, header=None)
示例输出(第一行):
9067664 9067671 A/C -
9067664 9067671 A/G -
9067664 9067671 A/T -
9067664 9067671 T/A -
9067664 9067671 T/C -
9067664 9067671 T/G -
9067664 9067671 A/C -
9067664 9067671 A/G -
9067664 9067671 A/T -
9067664 9067671 C/A -
优化
现在我们删除不需要的所有内容以保持最小大小:
df2 = (df.drop(columns=['symbol', 'chromosome'])
.assign(sequence=df['sequence'].apply(list))
.explode('sequence').reset_index(drop=True)
)
df2 = (df2.reindex(df2.index.repeat(4))
.assign(var=np.tile(list('ACGT'), len(df2)))
.loc[lambda d: d['sequence'].ne(d['var'])]
.assign(var=lambda d:d['sequence']+'/'+d['var'])
)
df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='\t', index=False, header=None)
我有一个采用 DNA 字符串的代码,其中仅找到 4 个字符:A、C、T 和 G,例如“ATACAAG”,并且对于每个字符,如果找到其他 3 个可能的字符。该代码包括一个字符串循环和另一个可能字符列表循环。问题是字符串很长:多达数十万个字符,所以速度不快并且计算机变热(风扇开始快速运转)。
我正在寻找一种更快的方法。我尝试了列表理解,但它仍然很慢。我还尝试将代码作为 pandas lambda 的函数调用,但每个字符串仍然需要大约一分钟的时间。这是我能得到的最好的吗?
对于每个字符,代码在文件的不同行中记录 3 个备选方案。
代码:
bases = set(list('ACGT'))
alts = {base: list(bases.difference(base)) for base in bases}
def get_variants(data, output_path): # pb: position base, b: base
[open(output_path + f'/{data.symbol}_variants.txt', 'a').writelines(
[f'{data.chromosome}\t{data.end + index}\t{data.end + index}\t{pb}/{b}\t{data.strand}\n' for b in alts[pb]])
for index, pb in enumerate(data.sequence)]
正在为“ATACAAG”调用函数:
get_variants(pandas.Series({'symbol': 'XYZ', 'sequence': 'ATACAAG', 'chromosome': 12, 'start': 9067664, 'end': 9067671, 'strand': '-'}),
'write_an_existing_output_directory_path_here')
输出按以下列排列在文件中:
chromosome number, start position, end position, original character/alternative character, strand (can + or -)
它在文件 XYZ_variants.txt 中产生以下行:
12 9067664 9067664 A/T -
12 9067664 9067664 A/G -
12 9067664 9067664 A/C -
12 9067665 9067665 T/A -
12 9067665 9067665 T/G -
12 9067665 9067665 T/C -
12 9067666 9067666 A/T -
12 9067666 9067666 A/G -
12 9067666 9067666 A/C -
12 9067667 9067667 C/T -
12 9067667 9067667 C/A -
12 9067667 9067667 C/G -
12 9067668 9067668 A/T -
12 9067668 9067668 A/G -
12 9067668 9067668 A/C -
12 9067669 9067669 A/T -
12 9067669 9067669 A/G -
12 9067669 9067669 A/C -
12 9067670 9067670 G/T -
12 9067670 9067670 G/A -
12 9067670 9067670 G/C -
谢谢。
这是我的做法。
从数据帧开始:
symbol sequence chromosome start end strand
0 XYZ ATACAAG 12 9067664 9067671 -
我会 explode
序列 reindex
具有 A/C/G/T 的所有组合,并且只保留初始碱基不同的那个
import numpy as np
df2 = df.assign(base=df['sequence'].apply(list)).explode('base').reset_index()
df2 = (df2.reindex(df2.index.repeat(4))
.assign(variant=np.tile(list('ACGT'), len(df2)))
.loc[lambda d: d['base'].ne(d['variant'])]
.assign(var=lambda d:d['base']+'/'+d['variant'])
)
中间输出:
>>> df2.head()
index symbol sequence chromosome start end strand base variant var
0 0 XYZ ATACAAG 12 9067664 9067671 - A C A/C
0 0 XYZ ATACAAG 12 9067664 9067671 - A G A/G
0 0 XYZ ATACAAG 12 9067664 9067671 - A T A/T
1 0 XYZ ATACAAG 12 9067664 9067671 - T A T/A
1 0 XYZ ATACAAG 12 9067664 9067671 - T C T/C
然后导出:
df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='\t', index=False, header=None)
示例输出(第一行):
9067664 9067671 A/C -
9067664 9067671 A/G -
9067664 9067671 A/T -
9067664 9067671 T/A -
9067664 9067671 T/C -
9067664 9067671 T/G -
9067664 9067671 A/C -
9067664 9067671 A/G -
9067664 9067671 A/T -
9067664 9067671 C/A -
优化
现在我们删除不需要的所有内容以保持最小大小:
df2 = (df.drop(columns=['symbol', 'chromosome'])
.assign(sequence=df['sequence'].apply(list))
.explode('sequence').reset_index(drop=True)
)
df2 = (df2.reindex(df2.index.repeat(4))
.assign(var=np.tile(list('ACGT'), len(df2)))
.loc[lambda d: d['sequence'].ne(d['var'])]
.assign(var=lambda d:d['sequence']+'/'+d['var'])
)
df2[['start', 'end', 'var', 'strand']].to_csv('variants.txt', sep='\t', index=False, header=None)