250,000行と10列のファイルがあります。たとえば、次のようになります。
img1 aa bb cc ...
img2 aa yy dd ...
img3 uu bb ee ...
img4 NA bb tt ...
このファイルを次に変換するスクリプトが必要です。
img1 1 1 1 ...
img2 1 2 2 ...
img3 2 1 3 ...
img4 0 1 4 ...
最初の列以降、各列の一意の値は、ゼロから始まる一意の識別子で置き換える必要があります。ここで、0 は文字列 "NA" 用に予約されています。
また、各列に対してマッピングを含むファイルを生成したいと思います。たとえば、ファイルの2番目の列は次のようになります。
NA 0
aa 1
uu 2
誰でもこれに対する優雅な解決策を提案できますか?どんな助けでも大変感謝します。
答え1
これは非常に簡単な方法です。これはgawk 3.1.7ではうまくいきます。
#!/usr/bin/awk -f
{
for(x=2;x<=NF;x++) {
if(x$x in a) {
$x=a[x$x]
} else {
if($x=="NA") {
print $x,0 > "column"x
a[x$x]=0
$x="0"
} else {
m[x]++
print $x,m[x] > "column"x
a[x$x]=m[x]
$x=m[x]
}
}
}
print $0 > "results"
}
答え2
$ awk 'BEGIN { id["NA"] = ++n } { for (i=2; i<=NF; ++i) { id[$i] || id[$i] = ++n; $i = id[$i] - 1 } } { print } END { for (i in id) { print i, id[i] - 1 >"map" } }' file
img1 1 2 3
img2 1 4 5
img3 6 2 7
img4 0 2 8
img
これは、最初の列(-column)を除くすべての列の各値に一意のIDを割り当てます。私はIDを列ごとに一意にするのではなく、グローバルに一意にすることを選択しました。これは、生成する必要がある必要なマッピングファイルの数を減らすためです。
公開スクリプト:
BEGIN { id["NA"] = ++n }
{
for (i=2; i<=NF; ++i) {
id[$i] || id[$i] = ++n;
$i = id[$i] - 1
}
}
{ print }
END {
for (i in id) {
print i, id[i] - 1 >"map"
}
}
まず、文字列NA
ID 1を割り当てて(IDは出力する前に常に1ずつ減らす)、カウンタをn
1に更新します。 n
常に前の文字列に割り当てられたIDになります。
各入力行に対してフィールドを繰り返します。現在のフィールドの文字列にIDが割り当てられていない場合は、IDを割り当ててそのフィールドを変更します。
次に、行と変更されたフィールドを印刷します。
最後に、すべての文字列とそのIDはmap
。
与えられた入力に対して、ファイルは次のようになります。
bb 2
ee 7
cc 3
NA 0
tt 8
dd 5
yy 4
aa 1
uu 6
答え3
GNUawk
ソリューション(2Dアレイサポートが必要)
awk '{
printf "%s ", $1;
for(i = 2; i <= NF; i++) {
filename = "column_" i - 1 "_mapping"
if(NR == 1) {
arr[i]["NA"] = 0;
print "NA 0" > filename;
}
if(! ($i in arr[i]) ) {
cnt[i]++;
arr[i][$i] = cnt[i];
print $i, cnt[i] > filename;
}
printf "%d ", arr[i][$i];
}
print "";
}' input.txt
入力する
img1 aa bb cc
img2 aa yy dd
img3 uu bb ee
img4 NA bb tt
出力
img1 1 1 1
img2 1 2 2
img3 2 1 3
img4 0 1 4
マッピングファイルの内容
tail -n +1 -- *_mapping
==> column_1_mapping <==
NA 0
aa 1
uu 2
==> column_2_mapping <==
NA 0
bb 1
yy 2
==> column_3_mapping <==
NA 0
cc 1
dd 2
ee 3
tt 4