【问题标题】:Find the unique values in a column and replace the unique values with numbers查找列中的唯一值并将唯一值替换为数字
【发布时间】:2014-06-27 07:07:29
【问题描述】:

我有一个标签限制数据,显示为

1 0 0 1 1 Black Swan
0 0 1 0 0 Golden Duck
1 0 0 1 0 Brown Eagle
0 0 1 0 1 Golden Duck
1 0 0 1 0 Black Swan
1 0 1 0 0 Golden Duck
1 0 0 1 1 Sparrow

最后一列是由空格分隔的一个或多个单词的组合。我想计算最后一列中唯一值的数量,并将其替换为该组唯一的数字。我知道我可以计算并列出数字使用

awk -F '\t' '{print $NF}'  infile | sort | uniq | wc -l

但是如何用数字替换? 例如,将所有 Black Swan 替换为 1,将所有 Golden Duck 替换为 2 等等。我希望结果是:

1 0 0 1 1 1
0 0 1 0 0 2
1 0 0 1 0 3
0 0 1 0 1 2
1 0 0 1 0 1
1 0 1 0 0 2
1 0 0 1 1 4

我还想生成赋予特定值的数字列表,例如

Black Swan 1
Golden Duck 2
Brown Eagle 3
Sparrow 4

【问题讨论】:

    标签: perl unix replace awk


    【解决方案1】:

    您可以使用关联数组为每个不同的名称增加一个计数器:

    awk '
        BEGIN { 
            FS = OFS = "\t" 
            i = 0
        }
        {
            if (! names[$NF]) {
                names[$NF] = ++i
            }
            $NF = names[$NF]
            print $0
        }
        END {
            for (name in names) {
                printf "%s %d\n", name, names[name]
            }
        }
    ' infile
    

    它产生:

    1       0       0       1       1       1
    0       0       1       0       0       2
    1       0       0       1       0       3
    0       0       1       0       1       2
    1       0       0       1       0       1
    1       0       1       0       0       2
    1       0       0       1       1       4
    Golden Duck 2
    Brown Eagle 3
    Sparrow 4
    Black Swan 1
    

    【讨论】:

    • 同意。显然不需要初始化i,最后的printf可能只是打印但nbd。
    【解决方案2】:

    我开始写这个,所以我会完成:

    awk '
    BEGIN {FS = OFS = "\t"}
    {
        last[$NF] = (last[$NF] ? last[$NF] : ++cnt)
        $NF = last[$NF]
        line[NR] = $0
    }
    END {
        for (nr=1; nr<=NR; nr++) 
            print line[nr]
        for (name in last) 
            print name, last[name]
    }' file
    1       0       0       1       1       1
    0       0       1       0       0       2
    1       0       0       1       0       3
    0       0       1       0       1       2
    1       0       0       1       0       1
    1       0       1       0       0       2
    1       0       0       1       1       4
    Brown Eagle     3
    Black Swan      1
    Sparrow         4
    Golden Duck     2
    

    更新:

    这里是perl 备用:

    perl -F'\t' -lane '
        $h{$F[-1]} = ++$c unless exists $h{$F[-1]}; 
        $F[-1] = $h{$F[-1]}; 
        print join "\t", @F }{ print "$_  $h{$_}" for keys %h
    ' file
    1       0       0       1       1       1
    0       0       1       0       0       2
    1       0       0       1       0       3
    0       0       1       0       1       2
    1       0       0       1       0       1
    1       0       1       0       0       2
    1       0       0       1       1       4
    Golden Duck  2
    Brown Eagle  3
    Black Swan  1
    Sparrow  4
    

    这是基于mpapec's优秀评论的另一个更新:

    perl -F'\t' -lane '
        $F[-1] = $h{$F[-1]} ||= ++$c; 
        print join "\t", @F }{ print "$_  $h{$_}" for keys %h
    ' file 
    

    【讨论】:

    • +1,只是$h{$F[-1]} = $h{$F[-1]} ? $h{$F[-1]} : ++$c;可以写成$h{$F[-1]} = $h{$F[-1]} || ++$c;或简称$h{$F[-1]} ||= ++$c;splice @F, -1, 1, $h{$F[-1]};写成$F[-1] = $h{$F[-1]}。仅用于高尔夫目的,可以进一步缩短$F[-1] = $h{$F[-1]} ||= ++$c;
    • 谢谢@mpapec,看起来真的很棒。将更新答案。
    【解决方案3】:

    您要做的是创建一组独特的数据。 set 是具有所有唯一元素的字典或哈希表。创建集合后,您可以搜索它并将字符串替换为适当的值。

    这里是另一个可以帮助您的套装链接:

    http://world.std.com/~swmcd/steven/perl/pm/set.html

    【讨论】:

      猜你喜欢
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多