从列中提取信息

从列中提取信息

我有一个如下所示的文件:

chr1    HAVANA  exon    12613   12721   .   +   .   gene_id "ENSG00000223972.5"; transcript_id "ENST00000456328.2"; gene_type "transcribed_unprocessed_pseudogene"; gene_name "DDX11L1"; transcript_type "processed_transcript"; transcript_name "DDX11L1-202"; exon_number 2; exon_id "ENSE00003582793.1"; level 2; transcript_support_level "1"; tag "basic"; havana_gene "OTTHUMG00000000961.2"; havana_transcript "OTTHUMT00000362751.1";
chr1    HAVANA  exon    13221   14409   .   +   .   gene_id "ENSG00000223972.5"; transcript_id "ENST00000456328.2"; gene_type "transcribed_unprocessed_pseudogene"; gene_name "DDX11L1"; transcript_type "processed_transcript"; transcript_name "DDX11L1-202"; exon_number 3; exon_id "ENSE00002312635.1"; level 2; transcript_support_level "1"; tag "basic"; havana_gene "OTTHUMG00000000961.2"; havana_transcript "OTTHUMT00000362751.1";

我想提取gene_id 和gene_name 值以及前8 列(文件以制表符分隔)。我已经用 perl 编写了一个脚本,可以做到这一点,但我正在 awk、sed 等中寻找一个可以做到这一点的单行程序。

附言。该文件以制表符分隔,有 9 列。第 9 列的值由空格分隔。

我的输出应该是这样的:

chr1    HAVANA  exon    12613   12721   .   +   .   ENSG00000223972.5   DDX11L1
chr1    HAVANA  exon    13221   14409   .   +   .   ENSG00000223972.5   DDX11L1

答案1

以下awk脚本假设第 9 列可以包含任意顺序的数据。

该代码将拆分该列,;后跟一个可选空格。然后它将迭代结果元素并将这些元素按空格分割成键值对。如果键(空格左侧的东西)是两个字符串gene_id或中的任何一个gene_name,则会记住该键的值。当我们找到两个字符串时,第 9 列的解析结束,之后该列被重写并打印修改后的行。

该代码还会丢弃任何输入不是包含gene_idgene_name

BEGIN {
    FS = OFS = "\t"
}

{
    n = split($9, a, "; ?")

    found = 0;
    for (i = 1; i <= n; ++i)
        if (split(a[i], b, " ") == 2) {
            if (b[1] == "gene_id") {
                gene_id = b[2]
                ++found
            } else if (b[1] == "gene_name") {
                gene_name = b[2]
                ++found
            }

            if (found == 2) break
        }

    if (found == 2) {
        $9 = gene_id " " gene_name
        print
    }
}

对提供的数据进行测试:

$ awk -f script.awk <file
chr1    HAVANA  exon    12613   12721   .       +       .       "ENSG00000223972.5" "DDX11L1"
chr1    HAVANA  exon    13221   14409   .       +       .       "ENSG00000223972.5" "DDX11L1"

要从值中删除双引号,请更改

if (found == 2) {
    $9 = gene_id " " gene_name
    print
}

进入

if (found == 2) {
    gsub("\"", "", gene_id)
    gsub("\"", "", gene_name)
    $9 = gene_id " " gene_name
    print
}

这会删除基因名称和 ID 中的所有双引号,或者,

if (found == 2) {
    gene_id = substr(gene_id, 2, length(gene_id) - 2)
    gene_name = substr(gene_name, 2, length(gene_name) - 2)
    $9 = gene_id " " gene_name
    print
}

它从两个值中删除第一个和最后一个字符。

答案2

Perl 一行代码。它可以打得短一点,但我认为这很清楚。

perl -F'\t' -lane '
    if (($id, $name) = / \b gene_id \s+ " ([^"]+) .+ \b gene_name \s+ " ([^"]+)/x) {
        print join "\t", @F[0..7], $id, $name;
    }
' file

更“聪明”一点:

perl -F'\t' -E '$,="\t"; say @F[0..7], $g{id}, $g{name} if %g = /\bgene_(id|name)\s+"([^"]+)/g' file

答案3

awk '{ print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t" $10 "\t" $16 ; } ' filename > output

不带引号和分号:

awk '{ print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t" $10 "\t" $16 ; }' filename | sed -e 's/;//g; s/\"//g;' > output

仅使用 awk 更准确:

awk '{ ORS=" "; print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t"; gsub(";", "", $10); gsub("\"", "", $10); print $10 "\t"; gsub(";", "", $16) ; gsub("\"", "", $16); print $16 ; ORS="\n" ; print " "; } ' filename > output

相关内容