如何从具有子组的CSV文件创建数据透视表,并使用Shell脚本获取最后一个值的计数?

时间:2019-02-12 16:39:56

标签: shell awk

我想对列进行分组,然后组成后续组以获取最后列值的计数。 例如,后续组中的主组A,子组D,J,P和P的计数以及最后一列的总数。 我可以分组,但分组似乎有点困难。感谢您提供任何帮助,例如如何获得此帮助。

输入:

A,D,J,P
A,D,J,Q
A,D,K,P
A,D,K,P
A,E,J,Q
A,E,K,Q
A,E,J,Q
B,F,L,R
B,F,L,R
B,F,M,S
C,H,N,T
C,H,O,U
C,H,N,T
C,H,O,U

输出:

A D J P 1
Q 1
K P 2
A E J Q 2
K Q 1
B F L R 2
M S 1
C H N T 2
O U 2
总计14

5 个答案:

答案 0 :(得分:1)

这是另一种方法,一种使用sqlite来计算组数的shell脚本(需要3.25或更高版本,因为它使用窗口函数):

#!/bin/sh
file="$1"
sqlite3 -batch -noheader <<EOF
CREATE TABLE data(c1 TEXT, c2 TEXT, c3 TEXT, c4 TEXT);
.mode csv
.import "$file" data
.mode list
.separator " "
SELECT (CASE c1 WHEN lag(c1, 1) OVER (PARTITION BY c1 ORDER BY c1) THEN ' ' ELSE c1 END)
     , (CASE c2 WHEN lag(c2, 1) OVER (PARTITION BY c1,c2 ORDER BY c1,c2) THEN ' ' ELSE c2 END)
     , (CASE c3 WHEN lag(c3, 1) OVER (PARTITION BY c1,c2,c3 ORDER BY c1,c2,c3) THEN ' ' ELSE c3 END)
     , c4
     , count(*)
FROM data
GROUP BY c1, c2, c3, c4
ORDER BY c1, c2, c3, c4;
SELECT 'Total ' || count(*) FROM data;
EOF

运行此操作可获得:

$ ./group.sh example.csv
A D J P 1
      Q 1
    K P 2
  E J Q 2
    K Q 1
B F L R 2
    M S 1
C H N T 2
    O U 2
Total 14

使用datamash的单行代码,尽管它不包含奇特的输出格式:

$ datamash -st, groupby 1,2,3,4 count 4 < example.csv | tr , ' '
A D J P 1
A D J Q 1
A D K P 2
A E J Q 2
A E K Q 1
B F L R 2
B F M S 1
C H N T 2
C H O U 2

答案 1 :(得分:1)

使用Perl

脚本

perl -0777 -lne ' 
s/^(.+?)$/$x++;$kv{$1}++/mge; 
foreach my $k (sort keys %kv) 
    { $q=$c=$k; 
        while(length($p) > 0)
        {
        last if $c=~/^$p/g; 
        $q=substr($c,length($p)-1);
        $p=~s/(.$)//;
        }
    printf( "%9s\n", "$q $kv{$k}") ;
    $p=$k;
} 
print "Total $x";
' anurag.txt 

输出:

A,D,J,P 1
      Q 1
    K,P 2
  E,J,Q 2
    K,Q 1
B,F,L,R 2
    M,S 1
C,H,N,T 2
    O,U 2
Total 14

答案 2 :(得分:0)

我还没有精确地产生您的示例输出的答案,但是我足够敢于发布答案

现在我有一个答案可以准确地产生 您的示例输出... :-)

$ cat ABCD
A,D,J,P
A,D,J,Q
A,D,K,P
A,D,K,P
A,E,J,Q
A,E,K,Q
A,E,J,Q
B,F,L,R
B,F,L,R
B,F,M,S
C,H,N,T
C,H,O,U
C,H,N,T
C,H,O,U
$ awk '{a[$0]+=1}END{for(i in a) print i","a[i];print "Total",NR}' ABCD |\
  sort | \
  awk -F, '
    /Total/{print;next}
    {print a1==$1?" ":$1,a2==$2?" ":$2,a3==$3?" ":$3,a4==$4?" ":$4,$5
     a1=$1;a2=$2;a3=$3;a4=$4}'
A D J P 1
      Q 1
    K P 2
  E J Q 2
    K   1
B F L R 2
    M S 1
C H N T 2
    O U 2
Total 14
$ 

第一个awk脚本在每一行上进行迭代,并且在每一行中,我们将由整个行值索引的数组a元素的值递增,然后在末尾({{1 }}目标),我们在END的索引上循环以打印索引和相关值,即在数据中包含该行的次数的计数-最终我们还输出了已处理的行总数,会在变量a中自动更新,该变量为 r 个坐标的 n 个数字。

第二个NR脚本要么打印总行并跳过任何进一步的处理,要么将每个字段(用逗号分隔)与上一行的相应字段进行比较,并相应地输出新字段或空格。 / p>

答案 3 :(得分:0)

$ cat tst.awk
BEGIN { FS="," }
!($0 in cnt) { recs[++numRecs] = $0 }
{ cnt[$0]++ }
END {
    for (recNr=1; recNr<=numRecs; recNr++) {
        rec = recs[recNr]
        split(rec,f)
        newVal = 0
        for (i=1; i<=NF; i++) {
            if (f[i] != p[i]) {
                newVal = 1
            }
            printf "%s%s", (newVal ? f[i] : " "), OFS
            p[i] = f[i]
        }
        print cnt[rec]
        tot += cnt[rec]
    }
    print "Total", tot+0
}


$ awk -f tst.awk file
A D J P 1
      Q 1
    K P 2
  E J Q 2
    K Q 1
B F L R 2
    M S 1
C H N T 2
    O U 2
Total 14

答案 4 :(得分:0)

我将本着unix工具集的精神提出一个多阶段解决方案。

创建排序,计数,定界的数据格式

$ sort file | uniq -c | awk '{print $2,$1}' | tr ',' ' ' 

A D J P 1
A D J Q 1
A D K P 2
A E J Q 2
A E K Q 1
B F L R 2
B F M S 1
C H N T 2
C H O U 2

现在,任务是从连续的行中删除最长的左公共子串

... | awk 'NR==1 {p=$0} 
           NR>1  {k=0; 
                  while(p~t=substr($0,1,++k)); 
                  gsub(/./," ",t); sub(/^ /,"",t); 
                  p=$0; $0=t substr(p,k)}1'


A D J P 1
      Q 1
    K P 2
  E J Q 2
    K Q 1
B F L R 2
    M S 1
C H N T 2
    O U 2

是否比一个脚本更容易理解。