私は見つけた「列1の固有値発生回数を増分して印刷する方法」、これは私の質問に似ていますが、答えは私の目的には十分ではありません。
まず私を見てみましょう言う私がしたいこと:
# Example input
apple abc jkl
apple xyz jkl
apple abc xyz
apple qrs xyz
apple abc jkl
banana abc lmno
banana lmnop xyz
banana lmnopq jkl
banana abc jkl
banana lmnop pqrs
banana abcdefg tuv
cucumber abc lmno
cucumber abc jkl
cucumber abc xyz
cucumber abcd jkl
cucumber abc jkl
cucumber abc lmno
# Desired output
apple 3 2
banana 4 5
cucumber 2 3
したがって、フィールド1の個々の値ごとに、そのフィールドとフィールド2とフィールド3の固有の関連値の数が印刷されます。
入力は最初のフィールドでソートされますが、他のフィールドでソートすることはできません(2番目と3番目のフィールドの両方を処理する必要があるため役に立ちません)。
私はそれを好みますawk
。おそらくPerlははるかに簡単になり、何をするかを学びたいのですが、awkスクリプトを作業していて、全体を書き直したくありません。
私は方法を見つけました。働くしかし、かなり長く、私には陳腐に見えます。私はこれを答えとして投稿します(事務室に戻ると)。しかし実際の内容を見たいです。いいね近い。 (私は私が「良い」とは思わない。)
答え1
そしてawk
:
awk 'function p(){print l,c,d; delete a; delete b; c=d=0}
NR!=1&&l!=$1{p()} ++a[$2]==1{c++} ++b[$3]==1{d++} {l=$1} END{p()}' file
説明する:
function p()
p()
:値を印刷し、使用された変数と配列を削除する関数を定義します。NR!=1&&l!=$1
最初の行ではなく、変数lが最初のフィールドと同じ場合、$1
関数p()
は実行されます。++a[$2]==1{c++}
a
インデックス配列の要素値の増加が$2
その値と等しい場合、1
その値が最初に表示されるため、c
変数が増加します。新しい値は要素の前に返されるため、++
それと比較する前に増加が発生します1
。++b[$3]==1{d++}
上記と同じですが、3番目のフィールドとd
変数があります。{l=$1}
l
最初のフィールドに(次の繰り返しのために..上)END{p()}
最後の行を処理した後、awk
最後のブロックの値を印刷する必要があります。
与えられた入力に基づいて、出力は次のようになります。
apple 3 2
banana 4 5
cucumber 2 3
答え2
私は空白と説明的な変数名が好きです。また何を言うのですか?とても久しぶりに文を書いてシャバンawk
も忘れてしまいましたね。-f
しかし、こうすると本当に禪状態にあるような感じがします。 俳句コード。
私は最小限のコーディングロジックがあるので、このソリューションが大好きです。配列インデックスを繰り返すforループは2つだけです。 3つの部分で構成されるステップfor
ループ、if
ステートメント、および明示的な値の比較はありません。これらすべては、ソフトウェアの欠陥(バグ)と統計的に相関があります。興味深いことに、明示的な割り当てはなく、1つの数学的操作と数の増加だけがあります。これはすべて言語機能の最大限の活用を示すものだと思います。
何か抜けたような気がしますが、まだ抜け穴が見つかりませんでした。
ご意見をお知らせください。コメントと建設的な批判を求めてください。このスクリプトのパフォーマンスに関する考慮事項について聞きたいです。
#!/usr/bin/awk -f
function count(seen, unique_count) {
for (ij in seen) {
split(ij, fields, SUBSEP)
++unique_count[fields[1]]
}
}
{
seen2[$1,$2]
seen3[$1,$3]
}
END {
count(seen2, count2)
count(seen3, count3)
for (i in count3) {
print i, count2[i], count3[i]
}
}
コメント
このスクリプトのユニークな特徴は、配列にデータがなくインデックスのみが含まれていることseen2
です。seen3
これは固有の値のみを計算するため、重要なのはこれらの値が表示されただけであり、何回表示されるかは関係ありません。
#!/usr/bin/awk -f
この関数は、入力レコード(フィールド1と2、またはフィールド1と3)で見つかった2つのフィールド値でインデックス付き配列を取得し、そのcount
レコードを含む最初のフィールドでインデックス付けされた内部呼び出し配列を返します。一意のフィールド値の数。 2番目のインデックス累積列:seen
unique_count
function count(seen, unique_count) {
このcount
関数は配列のインデックスを繰り返しますseen
。
for (ij in seen) {
インデックスを2つの元の値(フィールド1とフィールド2またはフィールド3)に分割します。
split(ij, fields, SUBSEP)
フィールド1で索引付けされた要素の数を増やします。
++unique_count[fields[1]]
}
}
各入力ラインで、最初のフィールドと2番目または3番目のフィールドにインデックス付きの空の配列要素(まだ存在しない場合)を作成します。seen2
計算する各フィールド番号に対して別々の配列(合計)を維持します。seen3
与えられた列(2または3)の各固有値には1つの配列要素しかありません。
{
seen2[$1,$2]
seen3[$1,$3]
}
データの最後に、各列に表示される固有フィールドの数を数えます。
END {
入力から蓄積された配列をcount
関数に渡し、一意のフィールド数を受け取るか入力しますcount2
。count3
count(seen2, count2)
count(seen3, count3)
count2
または、配列を段階的に実行しcount3
(両方とも各行の最初のフィールドを持っているため、どちらも問題ありません)、フィールド1を印刷してから、フィールド1を含む各行に見つかった一意の値の数を印刷します。
for (i in count3) {
print i, count2[i], count3[i]
}
}
一行バージョン
awk 'function c(s,u){for(x in s){split(x,f,SUBSEP); ++u[f[1]];}}
{a[$1,$2]; b[$1,$3];} END {c(a,d); c(b,e); for(i in d){print i,d[i],e[i];}}'
答え3
perl -lane 'undef $h{ $F[0] }[ $_ - 1 ]{ $F[$_] } for 1,2
}{
for my $k (keys %h) {
print join " ", $k, map scalar keys $_, @{ $h{$k} }
}' < input
デフォルトでは、次のハッシュを生成します。
'apple' => [
{
'abc' => undef,
'xyz' => undef,
'qrs' => undef
},
{
'jkl' => undef,
'xyz' => undef
}
],
'banana' => [
{
'abcdefg' => undef,
'lmnop' => undef,
'lmnopq' => undef,
'abc' => undef
},
{
'lmno' => undef,
'pqrs' => undef,
'tuv' => undef,
'jkl' => undef,
'xyz' => undef
}
],
'cucumber' => [
{
'abcd' => undef,
'abc' => undef
},
{
'lmno' => undef,
'jkl' => undef,
'xyz' => undef
}
]
次に、各内部ハッシュのキーを計算します。
答え4
約束通り、私のアプローチは次のとおりです。したこの質問を書く前に調べてください。それは動作しますおそらくこれは素晴らしいアプローチですが、単純に見えるこの作業に比べて複雑すぎます。今はあまり悪くないようです。 :)
function printcounts() {
printf "%s", currentf1
for (i = 2; i <= 3; i++ ) {
printf "%s", FS countuniq [ i ]
}
printf "\n"
}
function resetvars() {
delete already_seen_value
for ( i = 2; i <= 3; i++ ) {
countuniq [ i ] = 0
}
}
$1 != currentf1 {
if ( NR != 1 ) {
printcounts()
}
currentf1 = $1
resetvars()
}
{
for ( i = 2; i <= 3; i++ ) {
if ( ! already_seen_value [ i ":" $i ] ) {
already_seen_value [ i ":" $i ] ++
countuniq [ i ] ++
}
}
}
END {
printcounts()
}
そして修正に基づいてカオスに対する答え:
function printcounts() {
printf "%s", currentf1
for (i = 2; i <= 3; i++ ) {
printf "%s", FS countuniq [ i ] + 0
}
printf "\n"
# Reset vars
delete seenthis
delete countuniq
}
NR != 1 && currentf1 != $1 {
printcounts()
}
{
for ( i = 2; i <= 3; i++ ) {
if ( ++ seenthis [ i ":" $i ] == 1 ) {
countuniq [ i ] ++
}
}
currentf1 = $1
}
END {
printcounts()
}
(+ 0
printcounts関数の重要な点は、数値が常に印刷されるようにすることです。実際のユースケースには、カンマフィールドの区切り文字と空のフィールドを無視することが含まれているため、実際にゼロカウントを達成できるためです。)