【问题标题】:Linux Bash count and summarize by unique columnsLinux Bash 按唯一列计数和汇总
【发布时间】:2018-09-05 11:23:46
【问题描述】:

我有一个包含这样行的文本文件(在 Linux Bash 中):

A B C D
A B C J
E B C P
E F G N
E F G P
A B C Q
H F S L
G Y F Q
H F S L

我需要为前 3 列找到具有唯一值的行,打印它们的计数,然后为每个唯一行打印汇总的最后一列,所以结果是这样的:

3 A B C D,J,Q
1 E B C P
2 E F G N,P
1 G Y F Q
2 H F S L

我尝试过的:

cat FILE | sort -k1,3 | uniq -f3 -c | sort -k3,5nr

有什么建议吗?

提前致谢!

【问题讨论】:

  • 欢迎来到 SO。 Stack Overflow 是一个面向专业和狂热程序员的问答网站。目标是您将一些自己的代码添加到您的问题中,以至少显示您为解决这个问题所做的研究工作。
  • 唯一组是否总是由相邻的行组成?
  • 感谢您的提问 - 不一定,行不需要相邻,我现在已经编辑了原帖。
  • 我明白了前三列的计数,但最后的值没有分组:$ cat FILE |排序-k1,3 | uniq -f3 -c |排序 -k3,5nr
  • 在收到答案后修改您的要求是一种可疑的做法。也许您应该回滚您的最新编辑,接受其中一个答案,然后发布一个新问题,其中包含您的实际要求以及到目前为止的代码。

标签: bash awk uniq


【解决方案1】:

最简单的方法是:

awk '{key=$1 OFS $2 OFS $3; a[key]=a[key]","$4; c[key]++}
     END{for(key in a) { print c[key],key,substr(a[key],2) }}' <file>

如果你不想要任何重复,你可以这样做

awk '{ key=$1 OFS $2 OFS $3; c[key]++ }
     !gsub(","$4,","$4,a[key]) {a[key]=a[key]","$4; }
     END{for(key in a) { print c[key],key,substr(a[key],2) }} <file>

【讨论】:

  • 谢谢,这个答案很好!如果值相同,有什么方法可以避免在最后一列打印重复?我现在已经编辑了这个问题。例如,结果中的第四行理想情况下应该是“2 H F S L”,而您发送的脚本将是“2 H F S L,L”,因为最后一列的值是相同的。
  • 谢谢 - 看起来第二个脚本不会打印第四列(只有前三列)?
【解决方案2】:

您能否尝试关注并告诉我这是否对您有帮助。 这将以与 Input_file 的 $1$2$3 出现的相同顺序为您提供输出。

awk '
!a[$1,$2,$3]++{
  b[++count]=$1 FS $2 FS $3
}
{
  c[$1,$2,$3]=c[$1,$2,$3]?c[$1,$2,$3] "," $4:$0
  d[$1 FS $2 FS $3]++
}
END{
  for(i=1;i<=count;i++){
    print d[b[i]],c[b[i]]
  }
}
' SUBSEP=" "  Input_file

【讨论】:

  • 为了清楚起见,应该测试($1,$2,$3) in c,而不是(未加括号的!)三元组中的c[$1,$2,$3]。如果您在顶部添加 {key = $1 FS $2 FS $3} 然后在使用 $1 FS $2 FS $3$1,$2,$3 的任何地方使用 key 而不是一遍又一遍地重复相同的字段组合,您的代码会更简洁,您也可以摆脱SUBSEP=" " 如果你这样做。另外-当您拥有a[] 时,d[] 的意义何在?给你的数组和其他变量起有意义的名字,你就不太可能有多余的代码(在这种情况下,你已经创建了 2 个命名为 cnt[] 之类的数组)
【解决方案3】:

另一个使用 GNU awk 和 2d 数组删除 $4 中的重复项:

$ awk '{
    i=$1 OFS $2 OFS $3                        # key to hash
    a[i][$4]                                  # store each $4 to separate element
    c[i]++                                    # count key references
}
END {
    for(i in a) {                             
        k=1                                   # comma counter for output
        printf "%s %s ",c[i],i                # output count and key
        for(j in a[i])                        # each a[]i[j] element
            printf "%s%s",((k++)==1?"":","),j # output commas and elements
        print ""                              # line-ending
    }
}' file

以默认随机顺序输出:

2 E F G N,P
3 A B C Q,D,J
1 G Y F Q
1 E B C P
2 H F S L

由于我们使用的是 GNU awk,因此设置 PROCINFO["sorted_in"]="@ind_str_asc" 很容易影响输出顺序:

3 A B C D,J,Q
1 E B C P
2 E F G N,P
1 G Y F Q
2 H F S L

【讨论】:

    【解决方案4】:

    你可以使用GNU datamash:

    $ cat input
    A B C D
    A B C J
    E B C P
    E F G N
    E F G P
    A B C Q
    H F S L
    G Y F Q
    H F S L
    $ datamash -t' ' --sort groupby 1,2,3 unique 4 count 4 < input
    A B C D,J,Q 3
    E B C P 1
    E F G N,P 2
    G Y F Q 1
    H F S L 2
    

    不幸的是,这会将计数输出为最后一列。如果绝对有必要将其作为第一列,则必须重新格式化:

    $ datamash -t' ' --sort groupby 1,2,3 unique 4 count 4 < input | awk '{$0=$NF FS $0; NF--}1'
    3 A B C D,J,Q
    1 E B C P
    2 E F G N,P
    1 G Y F Q
    2 H F S L
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-01
      • 1970-01-01
      • 2019-02-16
      • 2021-04-29
      • 2021-11-14
      • 1970-01-01
      • 2021-09-22
      相关资源
      最近更新 更多