如何从制表符分隔文件的列中的文本字符串中提取第一个整数?

如何从制表符分隔文件的列中的文本字符串中提取第一个整数?

我在医学遗传学领域工作,经常有分隔文本文件,其中一列(例如第 5 列)中有一个文本字符串,用我们的行话来说是“突变”:
c.2458C>Tc.45_46delAAc.749_754delinsTG

类似地,在另一个文件中可能会读取:
p.Glu34*p.Ala78_Arg80delp.L378Ffs*11

和应该存在,但可能会被省略c.p.可以有任意数量的非数字字符。这些数字始终为整数,长度通常为 1-14 左右。

我想在我的文件中的某处添加一个新列,其中有只有第一个整数,如第一个示例中的 2458 或 45 或 749。然后我想使用这个整数作为键值来在查找表中查找多个值。

我的一些文件有 70,000 行,因此无法手动编辑...

解决方案越基本越好。可以用 bash、sed 或 awk 来完成吗?

示例表如下(正确解释如下):

1       2       3       4       c.2458C>T
a   b   c   d   c.45_46delAA
a1  b2  c3  d4  p.Ala78_Arg80del

(注意:列以制表符分隔,而不是空格分隔)

该格式有一个规范人类基因组变异协会。没有程序使用这种格式(我希望!),但人们在出版物和医疗报告中使用它。较新的格式,例如变体调用格式已经被引入,它们更容易解析。

答案1

根据您的描述,假设我们有一个制表符分隔的文件作为输入,例如:

$ cat file
1       2       3       4       c.2458C>T       6
a       b       c       d       c.45_46delAA or f
a1      b2      c3      d4      p.Ala78_Arg80del        f6

使用 sed

要查找第五列中的第一个整数:

$ sed -r 's/([^\t]*\t){4}[^[:digit:]]*([[:digit:]]+).*/\2/' file
2458
45
78

以上是在 GNU 上测试的sed。对于 OSX 或其他 BSD 系统,请尝试:

sed -E 's/([^\t]*\t){4}[^[:digit:]]*([[:digit:]][[:digit:]]*).*/\2/' file

使用 awk

$ awk '{sub(/^[^[:digit:]]*/, "", $5); sub(/[^[:digit:]].*/, "", $5); print $5;}' file
2458
45
78

答案2

使用 @John1024 的示例文本,这是 GNU-awk 特定的

gawk -F '\t' -v OFS='\t' 'match($5, /[[:digit:]]+/, m) {$(++NF) = m[0]} 1' file

产生

1   2   3   4   c.2458C>T   6   2458
a   b   c   d   c.45_46delAA    or  f   45
a1  b2  c3  d4  p.Ala78_Arg80del    f6  78

或者perl

perl -F'\t' -lane 'print join "\t", @F, $F[4]=~/(\d+)/' file

答案3

Glenn jackman 的 GNU/AWK 答案很优雅,但更简单一点是

awk 'BEGIN {FS=OFS="\t"} match($5,/[0-9]+/,arr) {print $0,arr[0]}' file

答案4

sed可以通过出现来替换 - 所以你只需要第五个< \tab>- 分隔[1]字段以及其中的任何数字,通过排除其他可能的匹配:

sed 's/[^\t0-9]*\([0-9]*\)[^\t]*/\1/5' <infile

将其他示例复制到剪贴板后,我做了:

xsel -bo | unexpand -a | sed ...

...unexpand -a收费<选项卡>大小的空间序列转化为实际的<选项卡>。并且打印出来了...

1   2   3   4   2458    6
a   b   c   d   45
a1  b2  c3  d4  78  f6

...它只是隔离第五列中的第一个整数。不过,我不确定这是否是您想要的。如果您只想将第五列中的第一个整数单独放在一行上,那就容易多了(而且速度更快)

<infile \
 cut -f5 | tr -cs '0-9\n' \\t |
 expand -t1,2,4 | cut -d' ' -f-2

...第一个cut是第五个<选项卡>- 分隔[2]每行完整数据字段(以避免每个字段有多个整数可能引起的问题)然后tr翻译成一个<选项卡> 每个与 ewlines 集互补的-s压缩字符序列和-c\n0-9 标准数字 [3]

这意味着在输出中,第一个整数将位于第一个或第二个字段中 - 因为第一个字段现在为空(由 <tab> 引导)或您的数字序列,具体取决于它是否如您所记的那样带有前缀。所以我expand第一张和第二张CD<选项卡>-将一行上的位置停止为一个空格,第三个空格 - 这有效地将空格分隔的字段列表填充为具有空的第一个字段或空的第三个字段。从那里我可以直接cut输出前两个字段。

 2458
 45
 78

...是我使用的示例的结果,因为它们都是由[cp]。所以所有人都有领先<选项卡>但那些没有的人会摇摇欲坠地向左走。另外,要将所有结果压缩到一行,每个整数用一个空格分隔,您只需附加|xargs到命令并获取:

2458 45 78

笔记

  1. 请注意,\t转义不是所涉及的标准转义sed- 并且在字符类的上下文中,[bracket-expression]可以说它甚至明显违反标准,因为反斜杠\t字符应该各自代表自己。我在这里使用转义来更清楚地展示可读意图 - 但您可能应该使用文字<选项卡>在它的位置。

  2. cut定界于<选项卡>默认情况下是字符,因此在这种情况下通用-d [delim-char]选项是不必要的 - 但还添加了此注释来解释原因。

  3. 正如链接中所述,POSIX 标准要求[:digit:]字符类包括0123456789所有语言环境中的字符和排序顺序,并排在该类中的任何其他包含之前。非 C 语言环境还可能包括其他本地化数字集 - GNUtr可能无法正确处理这些数字集,因为它们可能由多个字节表示 - 但是仅有的无论如何,标准数字集在大多数情况下更有可能是最不令人惊讶的结果,因此使用[:digit:]除非你确实想要匹配标准阿拉伯数字集中的字符和一些其他依赖于语言环境的数字集可能是不可取的。

相关内容