从字符串中间删除字符

时间:2019-05-01 14:42:21

标签: regex sed bioinformatics samtools

我有一个带有RX:的SAM文件,该字段包含12个碱基,中间用-RX:Z:CTGTGC-TCGTAA隔开

我想从此字段中删除连字符,但是我不能简单地从整个文件中删除所有连字符,因为读取的名称包含它们,例如1713704_EP0004-T

大多数情况下都尝试使用tr,,但这只是从文件中删除所有连字符。:

tr -d '"-' < sample.fq.unaln.umi.sam > sample.fq.unaln.umi.re.sam

输入是一个大于10,000,000行的大型SAM文件,如下所示:

1902336-103-016_C1D1_1E-T:34    99  chr1    131341  36  146M    =   131376  182 GGACAGGGAGTGTTGACCCTGGGCGGCCCCCTGGAGCCACCTGCCCTGAAAGCCCAGGGCCCGCAACCCCACACACTTTGGGGCTGGTGGAACCTGGTAAAAGCTCACCTCCCACCATGGAGGAGGAGCCCTGGGCCCCTCAGGGG  NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN  MC:Z:147M   MD:Z:83T62cD:i:4    cE:f:0  PG:Z:bwa    RG:Z:A  MI:Z:34 NM:i:1  cM:i:3  MQ:i:36 UQ:i:45 AS:i:141    XS:i:136    RX:Z:CTGTGC-TCGTAA

所需的输出(即最后一个字段)

1902336-103-016_C1D1_1E-T:34    99  chr1    131341  36  146M    =   131376  182 GGACAGGGAGTGTTGACCCTGGGCGGCCCCCTGGAGCCACCTGCCCTGAAAGCCCAGGGCCCGCAACCCCACACACTTTGGGGCTGGTGGAACCTGGTAAAAGCTCACCTCCCACCATGGAGGAGGAGCCCTGGGCCCCTCAGGGG  NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN  MC:Z:147M   MD:Z:83T62cD:i:4    cE:f:0  PG:Z:bwa    RG:Z:A  MI:Z:34 NM:i:1  cM:i:3  MQ:i:36 UQ:i:45 AS:i:141    XS:i:136    RX:Z:CTGTGCTCGTAA

如何解决此问题?

4 个答案:

答案 0 :(得分:5)

awk

awk '{sub(/-/,"",$NF)}1' file

是您所需要的。

说明

  • this中,您显然只关心最后一个字段。
  • NF是记录包含的字段总数,因此$ NF是最后一个字段。
  • sub(/-/,"",$NF)用空字符串替换最后一个字段中的-,使更改持久化。

GNU sed

出于this相同的原因,

sed -Ei 's/^(.*)-/\1/' file

将起作用。它还有一个额外的优势,就是可以执行就地编辑。

说明

  • -E选项启用扩展的正则表达式引擎。
  • (.*)是一种贪婪搜索,它将与任意字符(.)匹配任意次数(*)。对于贪婪的事实,它将匹配最后一个连字符。
  • ()使sed记住匹配的内容。
  • 在替换部分中,我们只放置了匹配的部分\11,因为我们只有一对括号,请注意,您可以根据需要任意设置),而无需连字符,因此有效地将其从应该出现的最后一个字段中删除。

注意: GNU awk支持-i inplace,但是我不确定从哪个版本开始。

答案 1 :(得分:2)

我已经使用pysam解决了这个问题,该方法更快,更安全且所需的磁盘空间更少,因为不需要sam文件。这不是完美的,我仍在学习python并且已经使用pysam半天了。

import pysam
import sys
from re import sub

# Provide a bam file
if len(sys.argv) == 2:
    assert sys.argv[1].endswith('.bam')

# Makes output filehandle
inbamfn = sys.argv[1]
outbamfn = sub('.bam$', '.fixRX.bam', inbamfn)

inbam = pysam.Samfile(inbamfn, 'rb')
outbam = pysam.Samfile(outbamfn, 'wb', template=inbam)

# Counters for reads processed and written
n = 0
w = 0

# .get_tag() retrieves RX tag from each read
for read in inbam.fetch(until_eof=True):
    n += 1
    umi = read.get_tag('RX')
    assert umi is not None
    umifix = umi[:6] + umi[7:]
    read.set_tag('RX', umifix, value_type='Z')
    if '-' in umifix:
        print('Hyphen found in UMI:', umifix, read)
        break
    else:
        w += 1
        outbam.write(read)

inbam.close()
outbam.close()

print ('Processed', n, 'reads:\n',
       w, 'UMIs written.\n',
       str(int((w / n) * 100)) + '% of UMIs fixed')

答案 2 :(得分:1)

最好的解决方案是使用BAM而不是SAM文件,并使用适当的BAM解析器/编写器库,例如htslib。

缺少该功能,您可以通过在可选标签(第12列及以上)中搜索正则表达式^RX:Z:来拼凑出一些东西。

在可能的情况下,使用sed很难处理列。相反,这是在awk中执行此操作的方法:

awk -F '[[:space:]]*' '{
    for (i = 12; i <= NF; i++) {
        if ($i ~ /^RX:Z:/) gsub("-", "", $i)
    }
}
1' file.sam

这是与Perl“单线”大致等效的解决方案:

perl -ape '
    for (@F[11..(scalar @F)]) {
        s/-//g if /^RX:Z:/;
    }
    $_ = join("\t", @F);
' file.sam

要在原始文件中执行替换,您可以将选项-i.bak传递给perl(这将创建备份file.sam.bak;如果您不想备份,请省略扩展名)。

答案 3 :(得分:0)

此模式在您要编辑的许多记录上,并且始终位于行尾吗?如果是这样-

sed -E 's/^(.*)(\s..:.:......)-(......\s*)$/\1\2\3/' < sample.fq.unaln.umi.sam > sample.fq.unaln.umi.re.sam