【问题标题】:inefficient code: comparing combining different columns from different files awk or perl?低效代码:比较来自不同文件 awk 或 perl 的不同列的组合?
【发布时间】:2013-10-22 07:58:53
【问题描述】:

我有两个文件,我想将 file1 的第 2 列与 file2 的 NF 列相匹配。如果它们匹配,我想从 file2 输出整行,此外还有 file1 中的第 5 列和文件 1 中的第 5 列,最后与文件 2 中的 NF-2 列相乘。这些文件有不同的长度。

我有以下两种文件类型:

文件1

xx  name1 1 we    freq1
xy  name2 2 wer   freq2
xz  name3 3 werf  freq3

文件2

..... value1 cv name1
..... value4 cvb name4
..... value3 cvb name3  
..... value1 vbn name5

想要的输出

..... value1 cv name1 freq1 (freq1*value1)
..... value3 cvb name3 freq3 (freq3*value3)

我尝试过使用 awk 来执行此操作。

awk 'FNR==NR { 两个[$0]++;下一个 } { for (i in two) { split(i, one);如果 (one[2] == $NF) print $0, one[5], $(NF-2)*one[5] } }' file1 file2 > 文件3

它可以工作,但效率极低,并且在一段时间后让我的 comp 崩溃。 这些文件每个大约有 100,000 行。也许我应该为此使用 perl 或 python?或者有没有办法读取文件2?谢谢!

【问题讨论】:

    标签: python perl bash awk


    【解决方案1】:

    听起来最好的方法是将file1中的所有相关列(2和5)读入哈希,然后逐行处理file2。 p>

    如果哈希使用file1第2列作为键,那么可以简单地用file2的最后一列的值进行索引,看看其他文件中是否有对应的值。

    然后,只需打印来自file2 记录的值,加上使用来自哈希元素的数据计算的值。

    这个程序演示。变量名称有点晦涩,因为您没有为问题中的字段赋予含义,所以我所能做的就是参考文件和列号。

    我不得不输出string*string 而不是实际的产品,因为您的数据没有数值。

    use strict;
    use warnings;
    use autodie;
    
    open my $fh1, '<', 'file1';
    my %file1;
    while (<$fh1>) {
      my ($f1_2, $f1_5) = (split)[1,4];
      $file1{$f1_2} = $f1_5;
    }
    
    open my $fh2, '<', 'file2';
    while (<$fh2>) {
      my @f2_rec = split;
      my ($f2_nm2, $f2_n) = @f2_rec[-3,-1];
      if (my $f1_5 = $file1{$f2_n}) {
        print join(' ', @f2_rec, $f1_5, "$f1_5*$f2_nm2"), "\n";
      }
    }
    

    输出

    ..... value1 cv name1 freq1 freq1*value1
    ..... value3 cvb name3 freq3 freq3*value3
    

    【讨论】:

      【解决方案2】:

      你只需要:

      awk 'NR==FNR{a[$2]=$NF;next} $NF in a{print $0, a[$NF], a[$NF] * $(NF-2)}' file1 file2
      

      例如:

      $ cat file1
      xx  name1 1 we    3
      xy  name2 2 wer   5
      xz  name3 3 werf  7
      
      $ cat file2
      ..... 2 cv name1
      ..... 4 cvb name4
      ..... 6 cvb name3
      ..... 8 vbn name5
      
      $ awk 'NR==FNR{a[$2]=$NF;next} $NF in a{print $0, a[$NF], a[$NF] * $(NF-2)}' file1 file2
      ..... 2 cv name1 3 6
      ..... 6 cvb name3 7 42
      

      【讨论】:

      • +1 与其他答案相比,awk 答案的简短和简洁真是令人惊讶
      【解决方案3】:
      perl -ane'
        BEGIN{ open($f,shift)||die$!; %h = (%h, (split)[1,4]) while <$f> }
        $fq = $h{$F[-1]} or next;
        s|$| sprintf(" %s (%s)", $fq, "$fq*$F[-3]") |e;
        print;
      ' file1 file2 > file3
      

      表示为独立脚本:

      #!/usr/bin/perl
      use strict; use warnings;
      
      my %frequencies;
      
      my $frequency_file = shift @ARGV;
      open my $fh, "<", $frequence_file or die "Can't open $frequency_file: $!";
      
      while (<$fh>) {
        my (undef, $name, undef, undef, $freq) = split;
        $frequencies{$name} = $freq;
      }
      
      while (<>) {
        my @fields = split;
        my ($value, $name) = @fields[-3, -1];
        next if not exists $frequencies{$name};
        my $freq = $frequencies{$name};
        print join(" ", @fields, $freq, "($freq * $value)"), "\n";
      }
      

      输出

      ..... value1 cv name1 freq1 (freq1*value1)
      ..... value3 cvb name3   freq3 (freq3*value3)
      

      删除"$fq*$F[-3]" 的双引号以进行实际乘法。

      【讨论】:

      • 感谢您的回答。这比我的 awk 程序运行得快得多,但 Borodin 的回答甚至更快。
      • @madieke 你能说出多少更快吗?
      猜你喜欢
      • 2017-09-02
      • 2021-01-16
      • 2021-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多