文本操作 - 根据值将列转置为行

文本操作 - 根据值将列转置为行

我想编写一段代码来操作 CSV 文件上的文本,其中包含以下内容:

71w - Rus,51200
71w - Phi,307200
71w - Ukr,307200
71w - Ukr,51200
71w - Mic,102400
71w - Mic,51200
71w - Jul,256000
71w - Jul,51200
71w - Pro,256000
71w - Uni,51200
71w - Ind,50176
71w - Ind,40960
71w - Sin,358400
71w - May,20480
71w - Tha,512000
71w - Tha,972800
71w - Bar,1280000
71w - Bar,102400
71w - Bar,2048000
71w - Upg,358400
71w - Leg,20480
71w - Res,153600

我想收集具有相同值的列并将其放在相应的行上,如下所示:

71w - Rus,51200
71w - Phi,307200
71w - Ukr,307200,51200
71w - Mic,102400,51200
71w - Jul,256000,51200
71w - Pro,256000
71w - Uni,51200
71w - Ind,50176,40960
71w - Sin,358400
71w - May,20480
71w - Tha,512000,972800
71w - Bar,1280000,102400,2048000
71w - Upg,358400
71w - Leg,20480
71w - Res,153600

谢谢。

答案1

做到这一点的一个好方法是使用关联数组或散列。每个哈希的键是第一个字段(我将它们称为“ids”,因为需要一个更好的术语),为每个键存储的值将是一个字符串,其中包含该值的逗号分隔列表id,或包含相同内容的数组。

awk:

这个 awk 版本使用逗号分隔的字符串,因为(在 awk 中)它比处理包含数组的关联数组更容易。

#!/usr/bin/awk -f

BEGIN {
  FS=" *, *";
  OFS="";
}

{
  key=$1; $1=""; $0=$0;

  if (length(ids[key]) > 0) {
    ids[key]=ids[key]","$0;
   } else {
    ids[key] = $0
   };
}

END {
  for (k in ids) {
    print k "," ids[k]
  }
}

在 Perl 中,处理数组哈希(或“HoA”)并不比连接字符串更难(而且更有用/更灵活):

#!/usr/bin/perl -w

use strict;

my %ids = ();

while(<>) {
  chomp;
  my @F = split /\s*,\s*/;
  push @{ $ids{$F[0]} }, $F[1];
};


END {
  foreach my $key (keys %ids) {
    print $key . ',' .  join(",",@{ $ids{$key} }), "\n";
  }
}

awk 和 perl 版本的输出是相同的:

71w - Ukr,307200,51200
71w - Bar,1280000,102400,2048000
71w - Res,153600
71w - Upg,358400
71w - Sin,358400
71w - Mic,102400,51200
71w - May,20480
71w - Tha,512000,972800
71w - Jul,256000,51200
71w - Uni,51200
71w - Ind,50176,40960
71w - Pro,256000
71w - Rus,51200
71w - Leg,20480
71w - Phi,307200

注意:awk 和 perl 版本的输出没有任何特定的顺序,并且每次运行时可能会以不同的顺序出现。这是因为 awk“关联数组”和 perl“哈希”(同一事物的两个名称)本质上是无序的。

sort如果需要,您可以通过管道输出。或者,在 Perl 中您可以使用:

      foreach my $key (sort keys %ids) {

代替:

      foreach my $key (keys %ids) {

另外 - 因为我们将每个 id 的单独值存储在一个数组中,所以在 Perl 中也很容易对这些值进行排序。例如,将ENDperl 版本中的整个块替换为:

END {
  foreach my $key (sort keys %ids) {
    print $key . ',' .  join(",",sort @{ $ids{$key} }), "\n";
  }
}

输出将是:

71w - Bar,102400,1280000,2048000
71w - Ind,40960,50176
71w - Jul,256000,51200
71w - Leg,20480
71w - May,20480
71w - Mic,102400,51200
71w - Phi,307200
71w - Pro,256000
71w - Res,153600
71w - Rus,51200
71w - Sin,358400
71w - Tha,512000,972800
71w - Ukr,307200,51200
71w - Uni,51200
71w - Upg,358400

答案2

使用 GNU datamash

$ datamash -t, -g 1 collapse 2 < file.csv
71w - Rus,51200
71w - Phi,307200
71w - Ukr,307200,51200
71w - Mic,102400,51200
71w - Jul,256000,51200
71w - Pro,256000
71w - Uni,51200
71w - Ind,50176,40960
71w - Sin,358400
71w - May,20480
71w - Tha,512000,972800
71w - Bar,1280000,102400,2048000
71w - Upg,358400
71w - Leg,20480
71w - Res,153600

使用排序输出-s

$ datamash -s -t, -g 1 collapse 2 < file.csv
71w - Bar,1280000,102400,2048000
71w - Ind,50176,40960
71w - Jul,256000,51200
71w - Leg,20480
71w - May,20480
71w - Mic,102400,51200
71w - Phi,307200
71w - Pro,256000
71w - Res,153600
71w - Rus,51200
71w - Sin,358400
71w - Tha,512000,972800
71w - Ukr,307200,51200
71w - Uni,51200
71w - Upg,358400

选项解释:

  • -s分组前对输入进行排序
  • -t,使用逗号作为字段分隔符
  • -g 1第一个字段上的组
  • collapse 2在第二个字段上崩溃

看看“崩溃”的例子手动的

答案3

假设您的输入是或可以通过排序等方式按输入中所示的 $1 值进行分组:

$ cat tst.awk
BEGIN { FS=OFS="," }
$1 != prev { if (NR>1) print rec; rec=prev=$1 }
{ rec = rec OFS $2 }
END { print rec }

$ awk -f tst.awk file
71w - Rus,51200
71w - Phi,307200
71w - Ukr,307200,51200
71w - Mic,102400,51200
71w - Jul,256000,51200
71w - Pro,256000
71w - Uni,51200
71w - Ind,50176,40960
71w - Sin,358400
71w - May,20480
71w - Tha,512000,972800
71w - Bar,1280000,102400,2048000
71w - Upg,358400
71w - Leg,20480
71w - Res,153600

或者如果您更喜欢简洁而不是清晰:

$ awk -F, '$1!=p{if(NR>1)print r;r=p=$1}{r=r FS$2}END{print r}' file
71w - Rus,51200
71w - Phi,307200
71w - Ukr,307200,51200
71w - Mic,102400,51200
71w - Jul,256000,51200
71w - Pro,256000
71w - Uni,51200
71w - Ind,50176,40960
71w - Sin,358400
71w - May,20480
71w - Tha,512000,972800
71w - Bar,1280000,102400,2048000
71w - Upg,358400
71w - Leg,20480
71w - Res,153600

相关内容