重新排列csv文件(Rearranging a csv file)

2019-10-23 09:01发布

我有类似以下内容的文件

Boy,Football
Boy,Football
Boy,Football
Boy,Squash
Boy,Tennis
Boy,Football
Girl,Tennis
Girl,Squash
Girl,Tennis
Girl,Tennis
Boy,Football

我如何使用“AWK”或类似的重新安排这下面:

     Football Tennis Squash
Boy  5        1      1
Girl 0        3      1

我甚至不知道这是可能的,但任何帮助将是巨大的。

Answer 1:

我只想循环正常:

awk -F, -v OFS="\t" '
          {names[$1]; sport[$2]; count[$1,$2]++}
          END{printf "%s", OFS;
              for (i in sport) 
                   printf "%s%s", i, OFS;
              print "";
              for (n in names) {
                   printf "%s%s", n, OFS
                   for (s in sport) 
                        printf "%s%s", count[n,s]?count[n,s]:0, OFS; print ""
                   }
               }' file

此跟踪三个阵列: names[]为第一列, sport[]对于第二列和count[name,sport]计数每一个组合的出现。

然后,它是通过循环的结果并以奇特的方式印制和确保的事情0 ,如果是打印count[a,b]不存在。

测试

$ awk -F, -v OFS="\t" '{names[$1]; sport[$2]; count[$1,$2]++} END{printf "%s", OFS; for (i in sport) printf "%s%s", i, OFS; print ""; for (n in names) {printf "%s%s", n, OFS; for (s in sport) printf "%s%s", count[n,s]?count[n,s]:0, OFS; print ""}}' a
    Squash  Tennis  Football    
Boy 1   1   5   
Girl    1   3   0   

格式是有点难看,也有一些尾随OFS。

为了摆脱尾随OFS的:

awk -F, -v OFS="\t" '{names[$1]; sport[$2]; count[$1,$2]++} END{printf "%s", OFS; for (i in sport) {cn++; printf "%s%s", i, (cn<length(sport)?OFS:ORS)} for (n in names) {cs=0; printf "%s%s", n, OFS; for (s in sport) {cs++; printf "%s%s", count[n,s]?count[n,s]:0, (cs<length(sport)?OFS:ORS)}}}' a

您可以随时管道column -t一个不错的输出。



Answer 2:

$ cat tst.awk
BEGIN{ FS=","; OFS="\t" }
{
    genders[$1]
    sports[$2]
    count[$1,$2]++
}
END {
    printf ""
    for (sport in sports) {
        printf "%s%s", OFS, sport
    }
    print ""
    for (gender in genders) {
        printf "%s", gender
        for (sport in sports) {
            printf "%s%s", OFS, count[gender,sport]+0
        }
        print ""
    }
}

$ awk -f tst.awk file
        Squash  Tennis  Football
Boy     1       1       5
Girl    1       3       0

一般来说,当你知道你把OFS或ORS每个字段后循环的终点:

for (i=1; i<=n; i++) {
    printf "%s%s", $i, (i<n?OFS:ORS)
}

但如果你没有,那么你把OFS第二和随后的场之前​​并打印循环之后的ORS:

for (x in array) {
    printf "%s%s", (++i>1?OFS:""), array[x]
}
print ""

我喜欢的:

n = length(array)
for (x in array) {
    printf "%s%s", array[x], (++i<n?OFS:ORS)
}

想法得到循环结束了,但是length(array)是呆子特定的。

另一种方法来考虑:

$ cat tst.awk
BEGIN{ FS=","; OFS="\t" }
{
    for (i=1; i<=NF; i++) {
        if (!seen[i,$i]++) {
            map[i,++num[i]] = $i
        }
    }
    count[$1,$2]++
}
END {
    for (i=0; i<=num[2]; i++) {
        printf "%s%s", map[2,i], (i<num[2]?OFS:ORS)
    }
    for (i=1; i<=num[1]; i++) {
        printf "%s%s", map[1,i], OFS
        for (j=1; j<=num[2]; j++) {
            printf "%s%s", count[map[1,i],map[2,j]]+0, (j<num[2]?OFS:ORS)
        }
    }
}

$ awk -f tst.awk file
        Football        Squash  Tennis
Boy     5       1       1
Girl    0       1       3

这最后将打印在他们阅读的顺序的行和列。 不是很明显它是如何工作的,虽然:-)。



文章来源: Rearranging a csv file