coo*_*ter 6 awk text-processing
我有 2 个文件,文件 1 的第 1 列必须替换为文件 2 的第 2 列,在文件 1 的第 2、3、4-5 或 5-4(交叉匹配)列与第 1、4、5 列匹配之后文件 2 的 -6 或 6-5。
文件 1
SNP Chr Pos EA NEA EAF Beta SE Pvalue Neff
1:79137 1 79137 A T 0.25 -0.026 0.0073 4.0e-04 231420
1:79033 1 79033 A G 0.0047 -0.038 0.056 4.9e-01 225429
1:118630 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311
1:533179 1 533179 A G 1 -0.098 0.19 6.1e-01 185906
Run Code Online (Sandbox Code Playgroud)
档案 2
1 1:79033_A_G 0 79033 A G
1 1:79137_A_T 0 79137 T A
1 1:118630_C_T 0 118630 T C
1 1:533179_A_G 0 533179 G A
Run Code Online (Sandbox Code Playgroud)
我需要输出看起来像这样:
SNP Chr Pos EA NEA EAF Beta SE Pvalue Neff
1:79137_A_T 1 79137 A T 0.25 -0.026 0.0073 4.0e-04 231420
1:79033_A_G 1 79033 A G 0.0047 -0.038 0.056 4.9e-01 225429
1:118630_C_T 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311
1:533179_A_G 1 533179 A G 1 -0.098 0.19 6.1e-01 185906
Run Code Online (Sandbox Code Playgroud)
这些文件没有确切的行数,并且文件不是制表符分隔的。我试过下面的代码,但它不起作用,你能更正我的代码吗?
awk 'NR==FNR{chr[$1]=$1;snp[$2]=$2;pos[$4]=$4;a1[$5]=$5;a2[$6]=$6;next} ($1 in chr)&&($4 in pos)&& ((($5 in a1) && ($6 in a2)) || (($6 in a1) && ($5 in a2))) {$2==snp[$2]}' file 2 file1
Run Code Online (Sandbox Code Playgroud)
编辑1:
下面的 perl 代码犯了一些错误,并在大约 20 000 行中产生了重复的行,一个例子是,
文件 1
SNP Chr Pos EA NEA EAF Beta SE Pvalue Neff
7:10100610 7 10100610 A G 0.0002 0.13 0.58 8.2e-01 120658
7:10100610 7 10100610 C G 0.0013 0.1 0.13 4.4e-01 139170
10:1006107 10 1006107 C G 1 -0.11 0.42 7.9e-01 152016
Run Code Online (Sandbox Code Playgroud)
档案 2
7 7:10100610_G_A 0 10100610 A G
7 7:10100610_G_C 0 10100610 C G
10 10:1006107_C_G 0 1006107 G C
Run Code Online (Sandbox Code Playgroud)
这些行的预期输出:
7:10100610_G_A 7 10100610 A G 0.0002 0.13 0.58 8.2e-01 120658
7:10100610_G_C 7 10100610 C G 0.0013 0.1 0.13 4.4e-01 139170
10:1006107_C_G 10 1006107 C G 1 -0.11 0.42 7.9e-01 152016
Run Code Online (Sandbox Code Playgroud)
但是 perl 代码给出的输出
7:10100610_G_A 7 10100610 A G 0.0002 0.13 0.58 8.2e-01 120658
10:1006107_C_G 7 10100610 C G 0.0013 0.1 0.13 4.4e-01 139170
10:1006107_C_G 10 1006107 C G 1 -0.11 0.42 7.9e-01 152016
Run Code Online (Sandbox Code Playgroud)
该join
命令将完成从多个文件中连接匹配行的工作。但是它对其输入文件有一些要求,因此您需要在此过程中制作一些临时文件,以及一些额外的字段。
awk '{printf $2" "$3" "$4" "$5"%"$1"%"; $1="";print $0 "%" NR }' < file1 | sort > 1.tmp
awk '{print $1" "$4" "$5" "$6"%"$2} $5 != $6 {print $1" "$4" "$6" "$5"%"$2}' < file2 | sort > 2.tmp
join -a 1 -t % -o 1.4 2.2 1.2 1.3 1.tmp 2.tmp | sort -t % -n | awk -F % '!$2{$2=$3}{print $2" "$4}'
Run Code Online (Sandbox Code Playgroud)
预处理第一个文件:
awk '{printf $2" "$3" "$4" "$5"%"$1"%"; $1="";print $0 "%" NR }''
Run Code Online (Sandbox Code Playgroud)
示例输出:
1 118630 C T%1:118630% 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311%4
Run Code Online (Sandbox Code Playgroud)
由 分隔的这 4 个字段%
是:
sort
)此输出通过管道传送sort
到临时文件中,因为join
需要对其输入进行排序。
对于第二个文件:
awk '{print $1" "$4" "$5" "$6"%"$2} $5 != $6 {print $1" "$4" "$6" "$5"%"$2}'
Run Code Online (Sandbox Code Playgroud)
示例输出:
1 118630 C T%1:118630_C_T
1 118630 T C%1:118630_C_T
Run Code Online (Sandbox Code Playgroud)
当您指定字段 5 和 6 应以任一方式匹配时,将打印第二行并交换它们(假设它们不相同)。%
这里的-separated 字段是
同样,输出通过管道传输sort
到另一个临时文件中。
然后是主要的“加入”步骤:
join -a 1 -t % -o 1.4 2.2 1.2 1.3 1.tmp 2.tmp
Run Code Online (Sandbox Code Playgroud)
当第二组没有匹配时,-a 1
指示join
保留第一组中的行。 -t %
将分隔符设置为%
(而不是空格)。该-o
参数产生以下四个输出字段:
file2
(当没有匹配项时,这将为空)file1
file1
示例输出行:
4%1:118630_C_T%1:118630% 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311
Run Code Online (Sandbox Code Playgroud)
然后sort
可以恢复原来的文件顺序(按数字排序,字段分隔符%
)
sort -t % -n
Run Code Online (Sandbox Code Playgroud)
最后awk
检查“替换”字段是否为空(因为未找到匹配项),如果是,则使用原始 column1 代替。它还丢弃行号和所有那些%
s。
awk -F % '!$2{$2=$3}{print $2" "$4}'
Run Code Online (Sandbox Code Playgroud)
最终输出线:
1:118630_C_T 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311
Run Code Online (Sandbox Code Playgroud)
我会在 Perl 中这样做,因为它有一个sort
功能可以让我们轻松地将A T
和T A
视为同一件事。例如(显示所有示例文件组合的输出):
$ perl -lane 'if(!$k){$name{join("","chr".$F[0],$F[3],sort($F[4],$F[5]))}=$F[1]; }else{$var=join("", "chr".$F[1],$F[2],sort($F[3],$F[4])); $F[0]=$name{$var} if $name{$var};print join "\t", @F; } $k++ if eof' file2 file1
SNP Chr Pos EA NEA EAF Beta SE Pvalue Neff
7:10100610_G_A 7 10100610 A G 0.0002 0.13 0.58 8.2e-01 120658
7:10100610_G_C 7 10100610 C G 0.0013 0.1 0.13 4.4e-01 139170
10:1006107_C_G 10 1006107 C G 1 -0.11 0.42 7.9e-01 152016
1:79137_A_T 1 79137 A T 0.25 -0.026 0.0073 4.0e-04 231420
1:79033_A_G 1 79033 A G 0.0047 -0.038 0.056 4.9e-01 225429
1:118630_C_T 1 118630 C T 0.99 -0.033 0.055 5.5e-01 226311
1:533179_A_G 1 533179 A G 1 -0.098 0.19 6.1e-01 185906
Run Code Online (Sandbox Code Playgroud)
或者,稍微更清晰一点:
$ perl -lane 'if(!$k){
$name{join("","chr".$F[0],$F[3],sort($F[4],$F[5]))}=$F[1];
}
else{
$var=join("", "chr".$F[1],$F[2],sort($F[3],$F[4]));
$F[0]=$name{$var} if $name{$var};
print join "\t", @F;
}
$k++ if eof' file2 file1
SNP Chr Pos EA NEA EAF Beta SE Pvalue Neff
7:10100610_G_A 7 10100610 A G 0.0002 0.13 0.58 8.2e-01 120658
7:10100610_G_C 7 10100610 C G 0.0013 0.1 0.13 4.4e-01 139170
10:1006107_C_G 10 1006107 C G 1 -0.11 0.42 7.9e-01 152016
1:79137_A_T 1 79137 A T 0.25 -0.026 0.0073 4.0e-04 231420
1:79033_A_G 1 79033 A G 0.0047 -0.038 0.056 4.9e-01 225429
Run Code Online (Sandbox Code Playgroud)
perl -lane
:-a
使 perl 像 awk 一样,自动将其输入拆分到@F
空白数组中。由于 perl 数组从 开始0
,$F[0]
将是第一个字段,$F[1]
将是第二个字段,以此类推。字段 N 是$F[N-1]
。该-n
令Perl读它的参数为文本文件和应用给出的剧本-e
给他们的每一行。的-l
只是删除后从每个输入行换行,并增加了一个新行给每个print
呼叫。
$k++ if eof
:$k
如果我们已经到达文件的末尾 ( eof
),这会将变量加 1 。然后我们可以使用if(!$k)
(如果 $k 没有定义)作为NR==FNR
awk的等价物。
if(!$k){$name{join("","chr".$F[0],$F[3],sort($F[4],$F[5]))}=$F [1];} : if this is the first file,
file2 , join fields 1, 4, and the sorted fields 5 and 6, into a string and use that string as the key in the hash (associative array)
name . Then, save the variant's name from file2 as the value associated with that key. The sorting lets us treat
A T and
T A as equivalent. I use
"chr".$F[0] to deal with cases like
1 123 and
11 23`,其中染色体和位置的连接给出相同的数字,即使染色体实际上不同。
else{
: 如果我们现在正在读取第二个文件,file1
.
$var=join("", $F[1],$F[2],sort($F[3],$F[4]));
: 生成密钥。这次使用字段 2、3 和排序 4 和 5。$F[0]=$name{$var} if $name{$var};
: 将第一个字段设置为存储在name
哈希中的值,如果此键有值。将if
需要确保我们不改变页眉或任何其他变体可能存在的file1
,但不是file2
。print join "\t", @F;
:打印字段,包括上面刚刚所做的更改。