【问题标题】:Filter lines based on multiple columns基于多列过滤行
【发布时间】:2013-10-23 09:02:18
【问题描述】:

我想根据多列中的匹配项过滤行。 我有(各种)4 列制表符分隔的文件。 我需要检查第 1 列中的所有重复项,检查相应的第 4 列,如果值不同(即使仅出现一次)打印整行(4 列)。

这是一个输入示例:

function-n  such_as-handheld-n  6.4623  A
function-n  such_as-hash-n  6.5328  A
party-n such_as-head-n  2.5586  A
function-n  such_as-headphone-n 8.0794  B
function-n  such_as-health-n    3.1938  A
party-n such_as-heartbeat-n 6.5902  B
party-n such_as-heat-n  3.9708  B
zebra-n at-1-aquatic-n  10.0476 B
zebra-n become-pelican-n    12.4166 B
zebra-n behind-idea-of-concept-n    16.0319 B
zebra-n move-lion-n 12.2017 B
zebra-n such_as-1-pole-n    8.9519  B
zebra-n try-reasoning-n 12.9504 B
zooplankton-n   than-1-mangrove-n   12.0638 B

因此结果如下:

function-n  such_as-handheld-n  6.4623  A
function-n  such_as-hash-n  6.5328  A
party-n such_as-head-n  2.5586  A
function-n  such_as-headphone-n 8.0794  B
function-n  such_as-health-n    3.1938  A
party-n such_as-heartbeat-n 6.5902  B
party-n such_as-heat-n  3.9708  B

因为“function-n”和“party-n”是 Column1 中唯一在 Column1 中具有不同值的值。

我已经看到这篇关于丢弃基于多列 here 的行的帖子,使用 awk。 代码(由@Steve 提出)如下:

FNR==NR {
    array[$0]++
    next
}

{
    counter = 0
    for (i in array) {
        split(i, holder, FS)
        if (holder[1] == $1) {
            counter++
        }
    }
    if (counter >= 2) {
        print
    }
}



$ awk -f script.awk file.txt{,}

此代码完全符合我的需要,除了 2 列数据。 我试图修改脚本的一部分来比较第 4 列,如下所示:

{
    counter = 0
    for (i in array) {
        split(i, holder, FS)
        if (holder[1] == $4) {
            counter++
        }

然而,它不起作用。谁能提供有关如何修改此脚本的见解,以便我可以达到预期的结果?

或者也许有人有更有效/优化的方法来处理问题? 谢谢。

【问题讨论】:

    标签: awk terminal compare multiple-columns


    【解决方案1】:

    我认为您必须非常努力地使用 awk。在阅读完每一行之前,您无法开始打印,而且我认为您需要的数据结构在某种程度上超出了 awk 提供的范围。您可以使用更高级的语言:想到 Python、Perl、Ruby。

    这是 ruby​​ 1.9.3:

    ruby -F"\t" -ane '
        BEGIN { 
            f4 = Hash.new {|h,k| h[k] = Hash.new} 
            lines = Hash.new {|h,k| h[k] = Array.new} 
        }
        f4[$F[0]][$F[-1]] = 1
        lines[$F[0]] << [$., $_]
        END {
            output = []
            f4.each_pair do |key, subhash|
                if subhash.length > 1
                    lines[key].each {|pair| output[pair[0]] = pair[1]}
                end
            end
            puts output
        }
    '
    

    Perl:

    perl -F"\t" -ane '
        $f4{$F[0]}{$F[-1]} = 1;
        push @{$lines{$F[0]}}, [$., $_];
        END {
            @output=();
            while (($key, $subhash) = each %f4) {
                if (keys(%$subhash) > 1) {
                    $output[$_->[0]] = $_->[1] for @{$lines{$key}};
                }
            }
            print @output;
        }
    '
    

    【讨论】:

    • 谢谢:我试过你的代码,但它给了我以下错误: ruby​​:1: syntax error, unexpected tSTRING_BEG, expecting kDO or '{' or '(' ruby​​ -F"\t " -ane ' ^ ruby​​:1: 语法错误,意外的 tSTRING_BEG,期望 kDO 或 '{' 或 '('
    • 你有什么版本的 ruby​​? ruby --version
    • 我使用的是 Ruby 1.8.6
    • 我用 1.9.3 测试了这个。翻译成 Perl 会有帮助吗?
    • 是的,如果能比较一下不同的方法来解决问题,那就太好了。
    【解决方案2】:

    一个可能的解决方案(使用 awk)如下:

    $ awk 'NR==FNR { if(A[$1]!=$NF && A[$1]){B[$1]++} A[$1]=$NF; next }\
      { if(B[$1]){ print } }' input input > output
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-12
      • 2022-07-21
      • 1970-01-01
      • 2018-06-21
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多