如何比较两个文件之间的字段的特定部分

时间:2012-04-09 21:48:03

标签: regex unix awk

我正在努力完成以下任务:使用制表符分隔的字段比较两个文件之间的字段部分($ 3)。这些文件匹配其他字段$ 1-2 line for line但$ 3略有不同。我只对3美元的一部分感兴趣, AF 的数值。 $ 3中的所有子字段(?)都用分号分隔,但正如您所看到的, AF 值并不总是在位置(有时它是#2,有时是#3)。我想拉出第三个字段中 AF 的值在文件之间不同的行。

例如,这是示例file1:

dmel_mitochondrion_genome       18984   AB=0.743;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19066   AB=0.684;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19074   AB=0.321;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19212   AC=8;AF=1.00;AN=8;DP=382;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19285   AC=8;AF=1.00;AN=8;DP=342;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19384   AC=8;AF=1.00;AN=8;DP=400;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19395   AC=8;AF=1.00;AN=8;DP=398;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19461   AB=0.524;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19472   AB=0.527;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19475   AC=8;AF=1.00;AN=8;BaseQRankSum=0.936;DP=$

和示例文件2:

dmel_mitochondrion_genome       18984   AB=0.730;AC=4;**AF=1.00**;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19066   AB=0.742;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19074   AB=0.345;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19212   AC=8;AF=1.00;AN=8;BaseQRankSum=1.722;DP=$
dmel_mitochondrion_genome       19285   AC=8;AF=0.50;AN=8;BaseQRankSum=1.721;DP=$
dmel_mitochondrion_genome       19384   AC=8;AF=1.00;AN=8;BaseQRankSum=1.458;DP=$
dmel_mitochondrion_genome       19395   AC=8;AF=1.00;AN=8;DP=391;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19461   AB=0.510;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19472   AB=0.526;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19475   AC=8;AF=0.50;AN=8;BaseQRankSum=-1.732;DP$

我想得到的输出是来自file1的以下几行:

dmel_mitochondrion_genome       18984   AB=0.743;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19285   AC=8;AF=1.00;AN=8;DP=342;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19475   AC=8;AF=1.00;AN=8;BaseQRankSum=0.936;DP=$

甚至是这样的:

dmel_mitochondrion_genome       18984   AF=0.50  
dmel_mitochondrion_genome       19285   AF=1.00  
dmel_mitochondrion_genome       19475   AF=1.00

我尝试使用awk,但我无法弄清楚如何比较部分字段而不是整个字段。我终于想出如何使用正则表达式从一个文件中找到每行中AF的值,但不知道如何捕获该值以将其与另一个文件中的另一个值进行比较。非常感谢任何帮助!

6 个答案:

答案 0 :(得分:4)

以下命令应该为您提供所需格式的每个文件。然后你可以在他们身上做一个diif ......

awk '{s=$0; split(s, a, "AF="); split(a[1], a1); split(a[2], a2, ";"); print a1[1] " " a1[2] " AF=" a2[1]}'

答案 1 :(得分:2)

您可以使用AWK数组作为哈希表来存储 AF 值的第一个匹配项,然后将其与下一个匹配项进行比较:

BEGIN { store[0] = 0 }
{
    key = $1 "-" $2
    match($3, /AF=[^;*]+/)
    val = substr($3, RSTART+3, RLENGTH-3)
    if ((key in store) && (store[key] != val))
        print $1,$2,"AF=" store[key]
    else
        store[key] = val
}

然而,filter-then-diff解决方案似乎更优雅,因为这个解决方案有可能消耗大量内存。

答案 2 :(得分:2)

略有不同的方法

awk '{subList=$3; 
 sub(/.*AF=/, "AF=", subList); sub(/;.*$/, "", subList)
 print $1 "\t" $2 "\t" subList}'  awkTest_20120409_1.txt > awkTest_20120409_1_cln.txt

awk '{subList=$3; 
 sub(/.*AF=/, "AF=", subList); sub(/;.*$/, "", subList)
 print $1 "\t" $2 "\t" subList}'  awkTest_20120409_2.txt > awkTest_20120409_2_cln.txt

diff awkTest_20120409_1_cln.txt awkTest_20120409_2_cln.txt | grep '^<' | sed 's/< //'

**输出**

dmel_mitochondrion_genome       18984   AF=0.50
dmel_mitochondrion_genome       19285   AF=1.00
dmel_mitochondrion_genome       19475   AF=1.00

当然,您需要将您的fileNames替换为输入和输出,以及diff的目标。

我希望这会有所帮助。

答案 3 :(得分:1)

TXR:为使用crummy文本文件的分子生物学家创建。还有其他人。

因为文件可能很大,我们避免在内存中保留从一对线到下一行的任何内容(由collect子句中的:vars ()确保)。

这里使用了一些技巧来使两个文件看起来像一个单独的流,具有交错的行。然后,我们可以对该流进行模式匹配,就像它是一个文件一样。

变量(第3列材料)被解析为Lisp关联列表,因此我们可以使用assoc来查找感兴趣的字段的值。它按字符串比较;如果需要,可以转换为数值,以便0.5和0.50被认为是相同的。

@(next :args)
@(cases)
@file1
@file2
@(or)
@  (throw error "two file names needed")
@(end)
@;
@; functional programming trick: make a bottomless lazy list which returns
@; strings, which are the lines from files f1 and f2, alternating.
@;
@(do (defun make-interleaved-lazy-stream (f1 f2)
       (let ((streams '#(,(open-file f1 "r") ,(open-file f2 "r"))))
         (let ((toggle 0) line)
           (gen (prog1
                  (set line (get-line [streams toggle]))
                  (set toggle (- 1 toggle)))
                line)))))
@(define parse-line (gen id alist))
@gen @id @(coll)@{var /[A-Z]+/}=@val;@(end)
@  (bind alist @[mapcar cons var val])
@(end)
@(next :list @(make-interleaved-lazy-stream file1 file2))
@(collect :vars ())
@  (cases)
@    (parse-line gen id alist1)
@    (parse-line gen id alist2)
@  (or)
@    (throw error `assumption violated: mismatching lines between @file1 and @file2`)
@  (end)
@  (do (let ((AF1 (cdr (assoc "AF" alist1)))
             (AF2 (cdr (assoc "AF" alist2))))
         (if (not (equal AF1 AF2))
           (put-string `@{gen 30} @{id -6}   AF1=@AF1\n`))))
@(end)

执行命令

$ txr gendiff.txr file1 file2
dmel_mitochondrion_genome       18984   AF1=0.50
dmel_mitochondrion_genome       19285   AF1=1.00
dmel_mitochondrion_genome       19475   AF1=1.00

答案 4 :(得分:0)

这可能会有所帮助 -

awk -F'[; =]' '
NR==FNR{ for (i=1;i<=NF;i++) if ($i=="AF") b[++x]=$(i+1); c[x]=$0; next } 
{for (j=1;j<=NF;j++) if ($j=="AF") d[++y]=$(j+1)} 
END {for (z=1;z<=length(b);z++) if (b[z]!=d[z]) print c[z]}' f1 f2

文件1:

[jaypal:~/Temp] cat f1
dmel_mitochondrion_genome       18984   AB=0.743;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19066   AB=0.684;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19074   AB=0.321;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19212   AC=8;AF=1.00;AN=8;DP=382;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19285   AC=8;AF=1.00;AN=8;DP=342;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19384   AC=8;AF=1.00;AN=8;DP=400;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19395   AC=8;AF=1.00;AN=8;DP=398;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19461   AB=0.524;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19472   AB=0.527;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19475   AC=8;AF=1.00;AN=8;BaseQRankSum=0.936;DP=$

文件2:

[jaypal:~/Temp] cat f2
dmel_mitochondrion_genome       18984   AB=0.730;AC=4;AF=1.00;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19066   AB=0.742;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19074   AB=0.345;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19212   AC=8;AF=1.00;AN=8;BaseQRankSum=1.722;DP=$
dmel_mitochondrion_genome       19285   AC=8;AF=0.50;AN=8;BaseQRankSum=1.721;DP=$
dmel_mitochondrion_genome       19384   AC=8;AF=1.00;AN=8;BaseQRankSum=1.458;DP=$
dmel_mitochondrion_genome       19395   AC=8;AF=1.00;AN=8;DP=391;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19461   AB=0.510;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19472   AB=0.526;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19475   AC=8;AF=0.50;AN=8;BaseQRankSum=-1.732;DP$

测试:

[jaypal:~/Temp] awk -F'[; =]' '
NR==FNR{ for (i=1;i<=NF;i++) if ($i=="AF") b[++x]=$(i+1); c[x]=$0; next } 
{for (j=1;j<=NF;j++) if ($j=="AF") d[++y]=$(j+1)} 
END {for (z=1;z<=length(b);z++) if (b[z]!=d[z]) print c[z]}' f1 f2
dmel_mitochondrion_genome       18984   AB=0.743;AC=4;AF=0.50;AN=8;BaseQRankSum=$
dmel_mitochondrion_genome       19285   AC=8;AF=1.00;AN=8;DP=342;DS;Dels=0.00;FS$
dmel_mitochondrion_genome       19475   AC=8;AF=1.00;AN=8;BaseQRankSum=0.936;DP=$

答案 5 :(得分:0)

这可能对您有用:

awk -vfile2=file2 -vOFS='\t' '{sub(/.*AF=[^0-9.-]*/,"",$3);sub(/[^0-9.-]+.*/,"",$3);getline line <file2;sub(/.*AF=[^0-9.-]*/,"",line);sub(/[^0-9.-]+.*/,"",line)};$3!=line{$3="AF="$3;print}' file1

由于file1file2匹配,但AF值除外:

  • 阅读一行file1
  • $3降低为AF
  • 将一行file2读入变量line
  • line降低为AF
  • $3line进行比较并从$0输出file1(在$3前加AF=之后),如果它们不匹配。