
2つの列を含むファイルがある場合:
Id ht
510 69
510 67
510 65
510 62
510 59
601 29
601 26
601 21
601 20
同じIDを持つすべての行を平均高さの行にマージする方法が必要です。この例では、(69 + 67 + 65 + 62 + 59)/ 5 = 64および(29 + 26 + 21 + 20)/ 4 = 24なので、出力は次のようになります。
Id Avg.ht
510 64
601 24
sed/awk/perlを使ってこれを行うにはどうすればよいですか?
答え1
awkを使う:
入力ファイル
$ cat FILE
Id ht
510 69
510 67
510 65
510 62
510 59
601 29
601 26
601 21
601 20
シェルからawk:
$ awk '
NR>1{
arr[$1] += $2
count[$1] += 1
}
END{
for (a in arr) {
print "id avg " a " = " arr[a] / count[a]
}
}
' FILE
または、シェルでPerlを使用してください。
$ perl -lane '
END {
foreach my $key (keys(%hash)) {
print "id avg $key = " . $hash{$key} / $count{$key};
}
}
if ($. > 1) {
$hash{$F[0]} += $F[1];
$count{$F[0]} += 1;
}
' FILE
出力は次のとおりです
id avg 601 = 24
id avg 510 = 64.4
最後のジョークはPerlの暗く難読化された一行のジョークです =)
perl -lane'END{for(keys(%h)){print"$_:".$h{$_}/$c{$_}}}($.>1)&&do{$h{$F[0]}+=$F[1];$c{$F[0]}++}' FILE
答え2
#!/usr/bin/perl
use strict;
use warnings;
my %sum_so_far;
my %count_so_far;
while ( <> ) {
# Skip lines that don't start with a digit
next if m/^[^\d]/;
# Accumulate the sum and the count
my @line = split();
$sum_so_far{$line[0]} += $line[1];
$count_so_far{$line[0]} += 1;
}
# Dump the output
print "Id Avg.ht\n";
foreach my $id ( keys %count_so_far ) {
my $avg = $sum_so_far{$id}/$count_so_far{$id};
print " $id $avg\n";
}
出力:
ire@localhost$ perl make_average.pl input.txt
Id Avg.ht
510 64.4
601 24
出力例が正しくありません。そのIDのすべての値が59以上の場合、平均52は得られません。
また、列の1つにl
数字で偽装された文字があります1
。
答え3
そしてgnu
datamash
:
datamash -H -s -g 1 mean 2 <file
GroupBy(ID) 平均() 510 64.4 601 24
s
ヘッダーを維持しながら、ソートおよびグループ化する最初のフィールドに基づいて2番目のフィールド値を計算します。フィールドが単一のタブ文字で区切られていると仮定します。複数のスペースで区切ったり、異なるフィールド区切り文字(スペース、カンマなど)を定義したりする場合に使用されます。入力をソートする必要があるため、出力はグループ化列に基づいてソートされます。g
1
2
mean
H
-W, --whitespace
-t, --field-separator=
datamash
答え4
ここで行われたことを確認してください。http://www.sugihartono.com/programming/group-by-count-and-sorting-using-perl-script/
最も難しいのは、「グループ別」の操作を実行することです。リンカスクリプトはこれを達成するためにハッシュを使用します。
そのリンクでは合計を計算しますが、平均を求めても大きな違いはありません。