选择性转置大CSV

时间:2018-07-12 02:53:22

标签: bash perl csv awk sed

我正在tty Linux上处理一些巨大的CSV文件(> 500 MB)。我的数据采用以下格式:

A, XYZ
A, ZSY
A, TVT
B,  GHJ
B, XYZ
C,  XYZ
C, TVT

输出应如下。

A, XYZ, ZSY, TVT
B, GHJ, XYZ, nil
C, XYZ, TVT, nil

第一列用作键,并将所有其他对应的行转置为列。我只有标准的Unix工具(+ perl)。

是否有标准的Unix解决方案可以实际地解决此问题?

3 个答案:

答案 0 :(得分:0)

如果您不关心输出的顺序应该与Input_file相同,那么下面的内容可能会对您有所帮助。

awk 'BEGIN{FS=", ";OFS=","}{a[$1]=a[$1]?a[$1] OFS $NF:$NF} END{for(i in a){print i,a[i]}}' Input_file

OR

awk '
BEGIN{  FS=", ";OFS=","  }
{
  a[$1]=a[$1]?a[$1] OFS $NF:$NF
}
END{
  for(i in a){ print i,a[i] }
}' Input_file

第二种解决方法: :如果您希望输出顺序与Input_file的顺序相同,那么以下操作可能会对您有所帮助。

awk '
BEGIN   { FS=", ";OFS="," }
!b[$1]++{ c[++count]=$1   }
{
  a[$1]=a[$1]?a[$1] OFS $NF:$NF
}
END{
  for(i=1;i<=count;i++){  print c[i],a[c[i]]  }
}'  Input_file

答案 1 :(得分:0)

这是awk的另一种解决方案,取决于您的数据是否针对第一列进行了排序

排序:

awk -F',' -v OFS=',' '{if(a!=$1){printf (a!="")?"\n"$1:$1;a=$1} printf "%s%s",OFS,$2}END{print}'

简要说明,

  1. if(a!=$1){printf (a!="")?"\n"$1:$1;a=$1}:已定义a并在未定义的情况下打印出来
  2. printf "%s%s",OFS,$2:始终打印以下列。

未排序

sort -sk1,1 file | awk -F',' -v OFS=',' '{if(a!=$1){printf (a!="")?"\n"$1:$1;a=$1} printf "%s%s",OFS,$2}END{print}'

使用sort -sk1,1对第一列进行排序,然后执行与已排序的列相同的工作。

答案 2 :(得分:0)

这只会在找到每个唯一键时打印它们的值,因此您不必将整个巨大的文件存储在内存中:

$ cat tst.awk
BEGIN {
    FS  = "[[:space:]]*,[[:space:]]*"
    OFS = ", "
}
$1 != prev {
    if ( NR > 1 ) {
        prt()
    }
    prev = $1
}
{
    vals[++numVals] = $2
}
END {
    prt()
}
function prt(   numCols, colNr, val) {
    numCols = 3
    printf "%s", prev
    for (colNr=1; colNr<=numCols; colNr++) {
        val = (colNr in vals ? vals[colNr] : "nil")
        printf "%s%s", OFS, val
    }
    print ""
    delete vals
    numVals = 0
}

$ awk -f tst.awk file
A, XYZ, ZSY, TVT
B, GHJ, XYZ, nil
C, XYZ, TVT, nil