我想对列进行分组,然后形成后续组,获取最后一列值的计数。例如,后续组中的主要组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 &nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbspQn 1 &nbsp&nbsp&nbsp&nbsp&nbspK P 2 A E J Q 2 &nbsp&nbsp&nbsp&nbsp&nbsp&nbspK Q 1 B F L R 2 &nbsp&nbsp&nbsp&nbsp&nbsp&nbspM S 1 C H N T 2 &nbsp&nbsp&nbsp&nbsp&nbsp&Ubsp U 2 &nbsp&nbsp&nbsp&nbspTotal 14
$ 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
这是一个不同的方法,一个使用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
使用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
我将以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
是否比一个脚本更容易理解。
我没有一个能够产生你的示例输出的答案,但我已经足够接近敢于发布答案了
现在我的答案恰好产生了你的示例输出...... :-)
$ 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
,元素,由整行值索引,接下来在最后(END
目标)我们循环在a
的索引上打印索引以及相关的值,即我们在数据中包含该行的次数 - 最终我们还输出处理的总行数,该值在变量NR
中自动更新,记录数。
第二个awk
脚本打印总行并跳过任何进一步的处理,或者将每个字段(用逗号分割)与前一行的相应字段进行比较,并相应地输出新字段或空格。