複数のファイルから同じ行を消す

機械翻訳用のデータは言語毎にファイルは別になっていて、文同士の対応は行番号が同じことによって保たれている

例えばこんな感じ

# ja.txt
こんにちは
私はテニスが好きです
私はペンを持っています
# en.txt
hello
I like tennis
I have a pen

これをtoken化してモデルへの入力として使うのだが、あまりにもtoken数の多い文があるとメモリに乗り切らなくなったり処理の効率が落ちたりする。

それを避けるために一定数以上のtokenを持つ行はそれぞれのファイルから削除したい。

token化した後のデータは各言語とも以下のような数字がスペース区切りで書いてあるものとすると

1 723 84 935 3385 4 172 7435 6 213 183 575 155 26 9 54 2025 4 199 279 2404 6 5 2
1 1415 333 375 27 239 26 7572 4 27 1745 67 77 13 7 1891 2024 6 8 183 517 2258 46 26 10 4148 10 7 203 8 12 366 211 5 2
1 120 9 116 665 21 98 64 1435 21 26 72 1890 183 517 10 52 605 10 943 166 2025 5 2
...

以下のような処理で一定数以下のtokenを持つ文のみを残したファイルを作成できる

max_words=50
token_file1=...
token_file2=...

# 2つのファイルからmax_words以上を持つ行を消すためのsedコマンドを生成
cat $token_file1 | awk  -v mw=$max_words 'mw < NF { print NR "d" }' > a.sed.tmp
cat $token_file2 | awk  -v mw=$max_words 'mw < NF { print NR "d" }' >> a.sed.tmp
cat a.sed.tmp | sort | uniq > a.sed

# -fですべての命令を一括適用
sed -f a.sed $token_file1 > ${token_file1}.tmp
sed -f a.sed $token_file2 > ${token_file2}.tmp

# 元のファイルへ上書き
mv ${token_file1}.tmp $token_file1
mv ${token_file2}.tmp $token_file2

ポイントとしてはsed -fによって一気に適用してること。1コマンドずつ順番に適用すると削除しているために行数がずれてしまいうまくいかない。

最後にmvで上書きする形をとっているのは、GNUBSDsed -iの挙動が異なるため。どちらかでよいなら-iでやってしまって問題なし。

あとawkの処理のところで条件を足せば空行削除も簡単。

参考

シェルスクリプト - 指定した複数行を一度に削除したい|teratail