![テキスト処理(2つのファイルの読み取りと計算)[awk、スクリプト]](https://linux33.com/image/96498/%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E5%87%A6%E7%90%86%EF%BC%882%E3%81%A4%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E8%AA%AD%E3%81%BF%E5%8F%96%E3%82%8A%E3%81%A8%E8%A8%88%E7%AE%97%EF%BC%89%5Bawk%E3%80%81%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%5D.png)
次のテキストがあります。 (単語、単語フィールド)
car transport
car machine
bank economy
bank politics
bank parks
God religion
...
多くの単語があり、いくつかの単語は異なるドメインを持ち、いくつかの単語はドメインが1つしかありません。次のように、各単語の単語とベクトルで構成される巨大な行列(1行あたり300次元)の別のファイルがあります。
bank 0.9 1.5 3.2 -0.2 0.1 ...
God 1.0 2.1 -0.5 0.7 ...
rose 0.2 -1.8 ...
... ...
最初のファイルから各単語の発生回数を読み取り、その値に基づいて2番目のファイルの各ベクトルで最も高い「n」数を選択し、その単語が属するフィールドを知りたいです。このような:
car 2
bank 3
God 1
この数字
bank 4 3.2
bank 3 1.5
bank 2 0.9
God 3 2.1
私が最初に考えた部分
gawk 'NR==FNR {a[$1]++;next;} dont know what here?' list matrix
少し複雑であることを知っていますが、助けてくれてありがとう。たぶん他の方法が簡単になりますか?
答え1
awk '
NR==FNR{ #operate matrix file first
A[$1] = 1 #array of words
for(i=2;i<=NF;i++)
B[$1 OFS i] = $i #array with indexes [word field_num]
next
}
$1 in A{ #if word in array A
max = $1 OFS 2
for(i in B)
if(i ~ "^" $1 && B[max] < B[i])
max = i #find maximum in B-array
print max, B[max] #output word + field_num + value
delete B[max] #exclude value from next search
}
}
' matrix list
awk バージョンが擬似多次元配列を受け入れると、スクリプトが簡素化される可能性があります。
awk '
NR==FNR{
for(i=2;i<=NF;i++)
A[$1][i] = $i
next
}
$1 in A{
max = 2
for(i in A[$1])
if(A[$1][max] < A[$1][i])
max = i
print $1, max, A[$1][max]
delete A[$1][max]
}
}
' matrix list
答え2
これは実際には非常に複雑です。awk
誰かが奇跡的な代謝を出さない限り、スクリプトを書くことをお勧めします。
ファイルからawk
:
NR==FNR {
a[$1]++
next
} #Your probably know what that does since it's your starting point
# If first field is a key in array a
$1 in a {
# Assign the number of occurences of this word in variable n
n=a[$1]
# Initialize this value to + INFINITY
k=-log(0)
# Loop on the number of occurences of the word
for (i=0; i<n; i++) {
# Initialize max value and its index at the first value of the vector
m=$2
i_m=2
# Loop on the number of fields in the matrix for that word
for (j=3; j<NF+1; j++) {
# Look for the largest value that stays below previous max (if none then k is INFINITY)
if ($j > m && $j < k) { m=$j; i_m=j }
}
# Print the word, the index of its max and its value
printf $1" "i_m" "m"\n"
# Store the max to be able to scan for the next biggest number at next iteration
k=m
}
}
実行してください:
$ awk -f myScript.awk list matrix
私のスクリプトは1つのケースを除いてうまくいくようです。つまり、単語が表示された場合の回数がlist
そのベクトルの値以上ですmatrix
。ベクトルが非常に大きいので、ここでは問題にならないようです。また、値を取得するためのk
atの初期化は少し以上ですが、自分で設定する方法がわかりません(明らかに機能しません)。おそらくいくつかのケースをさらに処理することができますが(たとえば、ベクトルに同じ値が複数回ある場合など)、開始点があるのでこれを任せます。-log(0)
inf
inf
=inf
答え3
(let ((h (hash :equal-based)))
(awk (:inputs "word-dom-pairs")
(t (inc [h [f 0] 0])))
(awk (:inputs "word-vectors")
(t (whenlet ((count [h [f 0]]))
(fconv - : r)
(let* ((n-fn-pairs (zip (rest f) (range 2)))
(n-fn-sorted [sort n-fn-pairs > first]))
(each ((p [n-fn-sorted 0..count]))
(prn [f 0] (second p) (first p))))))))
ランニング:
$ txr munge.tl
bank 4 3.2
bank 3 1.5
bank 2 0.9
God 3 2.1
データ:
$ cat word-dom-pairs
car transport
car machine
bank economy
bank politics
bank parks
God religion
$ cat word-vectors
bank 0.9 1.5 3.2 -0.2 0.1
God 1.0 2.1 -0.5 0.7
rose 0.2 -1.8
以下は、単一の式に結合されたプログラムのバージョンですawk
。
(awk (:inputs "word-dom-pairs" "word-vectors")
(:let (h (hash :equal-based)))
((= arg 1) (inc [h [f 0] 0]))
((= arg 2) (whenlet ((count [h [f 0]]))
(fconv - : r)
(let* ((n-fn-pairs (zip (rest f) (range 2)))
(n-fn-sorted [sort n-fn-pairs > first]))
(each ((p [n-fn-sorted 0..count]))
(prn [f 0] (second p) (first p)))))))
:inputs
以前に分離された2つのawk
-sが1つに結合されました。t
変数によって提供される入力に基づいて処理し、無条件の真のパターンをセレクタに置き換えますarg
。バインドされたハッシュテーブル変数の内容は、let
awkマクロ句に折り畳まれます:let
。
その句を削除すると、(:inputs ...)
コマンドライン引数のペアを使用してファイルを提供できます。
$ txr munge.tl file1 file2
TXR Lispは、変数を割り当てるか使用する前に定義する必要があるタイプの安全な動的言語です。存在しない変数とゴミ文字列は数字0でも文字列でもありません。バラより同様の数字はその数字ではありません。これが、私たちがハッシュテーブルの存在を明示的に定義し、fconv
2番目とそれ以降のフィールドを誤って明示的に変換する方法(r
)を使用する理由です。