计算唯一值并将结果值添加为新列

计算唯一值并将结果值添加为新列

我有一个很大的 csv 文件,想要建立简单的排名:

$ cat file.csv
2022-12-01     RED     1     
2022-12-01     RED     1     
2022-12-01     RED     2     
2022-12-01     RED     2     
2022-12-01     RED     2     
2022-12-01     YELLOW     1     
2022-12-01     YELLOW     1     
2022-12-01     YELLOW     2     
2022-12-01     YELLOW     2     
2022-12-01     YELLOW     2     
$ sort file.csv | uniq -c | sort -nr > file_sort.csv
$ cat file_sort.csv 
3 2022-12-01     RED     2     
3 2022-12-01     YELLOW     2     
2 2022-12-01     RED     1     
2 2022-12-01     YELLOW     1     

我希望将结果添加为新列,但将其添加到现有的 $1 中,如下所示:

$ cut -f1 file_sort.csv
3 2022-12-01
3 2022-12-01
2 2022-12-01
2 2022-12-01

有什么方法可以将 uniq 行的值保存为新列吗?像这样:

$ cut -f1 file_sort.csv
3
3
2
2

答案1

听起来您想要做的只是将数字从第一个字段移动到最后一个字段。您没有指定这一点,但考虑到cut您显示的命令,这应该意味着您的文件是制表符分隔的。如果是这样,鉴于uniq -c输出将用空格填充该行(您也没有显示),并且不会在出现次数后添加制表符,您可以执行以下简单操作:

$ sort file.csv | uniq -c | sort -nr | perl -pe 's/^\s+(\d+) (.*)/$2\t$1/' 
2022-12-01  YELLOW  2   3
2022-12-01  RED 2   3
2022-12-01  YELLOW  1   2
2022-12-01  RED 1   2

或者,在sed支持-E

$ sort file.csv | uniq -c | sort -nr | sed -E 's/^ *([0-9]*) (.*)/\2\t\1/' 
2022-12-01  YELLOW  2   3
2022-12-01  RED 2   3
2022-12-01  YELLOW  1   2
2022-12-01  RED 1   2

并且,在任何 sed 中:

$ sort file.csv | uniq -c | sort -nr | sed 's/^ *\([0-9]*\) \(.*\)/\2\t\1/' 
2022-12-01  YELLOW  2   3
2022-12-01  RED 2   3
2022-12-01  YELLOW  1   2
2022-12-01  RED 1   2

或者,您可以进行计数awk,将字段添加到您想要的位置,并在第四个字段上进行排序:

$ awk -F'\t' -v OFS='\t' '{ cnt[$0]++ } 
                          END{
                            for(line in cnt){
                              print line,cnt[line]
                            }
                          }' file.csv | sort -nrk4,4 
2022-12-01  YELLOW  2   3
2022-12-01  RED 2   3
2022-12-01  YELLOW  1   2
2022-12-01  RED 1   2

答案2

使用(以前称为 Perl_6)

您想要做的是Bag元素,在本例中元素是lines

~$ raku -e '.say for lines.Bag;'  file

输入示例(制表符分隔):

2022-12-01     RED     1     
2022-12-01     RED     1     
2022-12-01     RED     2     
2022-12-01     RED     2     
2022-12-01     RED     2     
2022-12-01     YELLOW     1     
2022-12-01     YELLOW     1     
2022-12-01     YELLOW     2     
2022-12-01     YELLOW     2     
2022-12-01     YELLOW     2 

示例输出:

2022-12-01    RED    2        3
2022-12-01    RED    1        2
2022-12-01    YELLOW    2        3
2022-12-01    YELLOW    1        2

注意:这里真正的问题可能是尾随空格。您可以添加一个trim-trailing调用来消除每行右端的空格:

~$ raku -e '.put for lines.map(*.trim-trailing).Bag;' 

#OR

~$ raku -e '.put for lines>>.trim-trailing.Bag;'  

https://docs.raku.org/type/Bag
https://raku.org

答案3

使用count-distinctMiller ( ) 的子命令mlr来计算前三个字段中具有不同值的记录数,假设输入是制表符分隔且没有列标题:

$ mlr --tsv -N count-distinct -f 1,2,3 file
2022-12-01      RED     1       2
2022-12-01      RED     2       3
2022-12-01      YELLOW  1       2
2022-12-01      YELLOW  2       3

计数将作为新字段添加到字段列表的末尾。输入不需要排序。

如果您希望计数作为第一个字段,请使用reorder子命令。请注意,即使标题未显示在输出中,我们也可以引用操作count创建的命名字段:count-distinct

$ mlr --tsv -N count-distinct -f 1,2,3 then reorder -f count file
2       2022-12-01      RED     1
3       2022-12-01      RED     2
2       2022-12-01      YELLOW  1
3       2022-12-01      YELLOW  2

答案4

使用 awk 命令的关联数组思想可以轻松完成对唯一行的计数:

$ awk  '{a[$0]++} END {for (i in a) print a[i]"--->",i}' infile

2---> 2022-12-01     YELLOW     2     
3---> 2022-12-01     RED     2     
2---> 2022-12-01     YELLOW     1     
1---> 2022-12-01     YELLOW     2
2---> 2022-12-01     RED     1

相关内容