Linux 总结 a table 并对每个给定事件的所有列求和

Linux summary a table and sum all columns from each given occurrence

完成分析后,我得到一个包含很多列和行的 table。此外,随着新的 table 的写入,lines/cols 的数量可能会有所不同,因此我无法预测每个会有多少。每行在第一列中都有一个索引,但这些索引可以在 table 中重复。所以我想要的是一种 grep/awk/bash 方法来检索具有相同索引的所有这些行并对所有列求和以仅获得具有求和值的一行。 举个例子:

table

index,sampleA,sampleB,sampleC
nana,22,12,4
baba,47,4,5
nana,1,5,9
nana,7,5,8

解析后

index,sampleA,sampleB,sampleC
nana,30,22,21
baba,47,4,5

如果你能帮助我,我将不胜感激。 非常感谢。

有点啰嗦,但像这样就可以了:

awk -F"," 'BEGIN{OFS=FS} NR==1{print [=10=]; next} NR>1{sampleA[]+=; sampleB[]+=; sampleC[]+=}END{for (sample in sampleA){print sample, sampleA[sample], sampleB[sample], sampleC[sample]}}' yourfile

解释:

  1. 用逗号分隔每一行-F","
  2. 在处理文件之前确保输出字段分隔符与字段分隔符(逗号)相匹配BEGIN{OFS=FS}
  3. 如果这是文件的第一行然后打印出来(它是header)然后继续下一行NR==1{print [=13=]; next}
  4. 如果这不是 header 行,则创建三个数组来存储第 2、3、4 列的累积值。数组的 "key" 是第 1 列中的值 NR>1{sampleA[]+=; sampleB[]+=; sampleC[]+=}
  5. 最后,循环遍历三个数组中的第一个(它们的长度都相同,所以我们只需要循环一个)。然后打印出存储在每个键中的值:END{for (sample in sampleA){print sample, sampleA[sample], sampleB[sample], sampleC[sample]}}

您没有指定 shell,但如果您喜欢使用 ksh (93l+),那也应该可以解决问题。

虽然这个脚本做了一些假设:

  1. 可以有任意数量的列,但每行的列数相同
  2. 索引名称中没有space
  3. 每列总有一个值

    #!/bin/ksh
    
    # CSV to input as first argument, CSV to ouput as second argument
    InputCSV=
    OutputCSV=
    
    typeset -A Index
    
    while read line; do
        lineArray=(${line//,/ })
        # Assume that the first column is always named "index", but you can modify this
        if [[ ${lineArray[0]} == "index" ]]; then
            titleArray=(${line//,/ })
            continue
        fi
        for ((i=1;i<${#lineArray[*]};i++)); do
            if [[ -z ${Index[${lineArray[0]}][${titleArray[$i]}]} ]]; then
                    Index[${lineArray[0]}]+=( [${titleArray[$i]}]=${lineArray[$i]} )
            else
                    Index[${lineArray[0]}][${titleArray[$i]}]=$(( ${Index[${lineArray[0]}][${titleArray[$i]}]} + ${lineArray[$i]} ))
            fi
        done
    done < $InputCSV
    
    exec 3>$OutputCSV
    
    titleBar=${titleArray[0]}
    for ((i=1;i<${#titleArray[*]};i++)); do
        titleBar+=",${titleArray[$i]}"
    done
    
    print $titleBar >&3
    
    for j in ${!Index[@]}; do
        outLine=$j
        for ((i=1;i<${#titleArray[*]};i++)); do
                outLine+=",${Index[$j][${titleArray[$i]}]}"
        done
        print $outLine >&3
    done
    
    exec 3>&-