2017年02月22日

巨大なcsvファイルのヘッダだけ取り替えたい

巨大なcsvファイルのヘッダだけを取り替えたいということがしばしばあります。例えば、あるソフトウェアにデータを読み込ませたいのだけれど使用できない文字が入っているとか、ヘッダにだけ2バイト文字が入っていて、それらを英数になおしたいとか、ヘッダをつけ間違えたとか。でも、大きすぎてエディタで開いて修正することはできないし。

修正したいヘッダ付きのcsvファイルをA.csvとし、修正済みのヘッダだけを一行書いたファイルをH.csvとします。A.csvのヘッダをH.csvで取り替えて、B.csvに出力します。

コマンドラインから、

cat H.csv <(cat A.csv | tail -n +2) > B.csv

でOK。一つ前の記事でも使ったプロセス置換です。

この方法が本領発揮するのは、巨大なデータファイルがgzipされているとき。そもそも大きいファイルは圧縮されていることが多く、これらを展開するとやたらディスクを食うので、できれば圧縮したまま取り扱いたい。その場合には

cat H.csv <(zcat A.csv.gz | tail -n +2) | gzip > B.csv.gz

こうすれば、展開ファイルを作ることなくヘッダを取り替えられます。



posted by jinya at 23:29| Comment(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2017年02月06日

【bash】teeとプロセス置換を使ってパイプを分岐

bashにてteeとプロセス置換を使ってパイプを分岐する方法の備忘録です。

bashのパイプラインはテキスト処理に便利ですね。ビッグデータが普通に取り扱われるようになった昨今ですが、システムに組み込み済みの処理はともかく、アドホックなデータ分析業務の場合はまだまだテキストファイルでデータを受け渡しすることが多いです。そうすると、データが巨大な場合はそれをちょっと確認するだけでも一苦労もふた苦労もあったりして、そんなときにbashのテキスト処理系コマンドが威力を発揮します。

ここで、途中まで同じで、後ろが異なる処理を複数やりたい場合がありました。例えば、

zcat file0.gz | ./script0 | ./script1 > file1
zcat file0.gz | ./script0 | ./script2 > file2
zcat file0.gz | ./script0 | ./script3 > file3

のようなケースです。

ここで、file0.gzが小さくて、script0が一瞬で終わるようなものだったらどうってことないのですが、数ギガとか数十ギガという巨大なファイルを取り扱う場合には、このscript0を三回繰り返すのが勿体ない。かといって、script0を適用した結果を保存しておくにはディスクスペースが勿体ない。

そこで、teeを使ってパイプラインを分岐し、それぞれ結果を出力します。

zcat file0.gz | ./script0 | tee >(./script1 > file1) \
| tee >(./script2 | file2) | ./script3 > file3

こうすると、file0の展開ファイルを作る必要が無く、また、script0を適用する回数も一回で済み、やりたい三つの作業を実行することができます。繰り返しになりますが、file0.gzが小さかったり、script0の処理が軽かったりすればなにもこんな面倒なことをしなくて済むのですが、巨大で重い場合はこれだけでかなりの時間節約になります。

なお、各scriptがメモリを大きく食う場合はメモリ不足に要注意です。そもそもパイプラインは一行単位のテキスト処理であるケースが多いですから、その場合はあまり気にしなくても大丈夫だろうと思います。


ちなみにこれを何に使ったかというと、あるPOSデータを記した巨大なcsvファイルの整形です。このファイルには、商品情報と店舗情報がマスターになっておらず、一つのテーブルにまとめて書いてありましたので、


zcat pos.csv.gz | nkf -Sw | sed -e 's/,/\t/g' -e 's/\"//g' \
| tee >(cut -f 3,4,5,6 | sort -u > item.txt) \
| tee >(cut -f 7,8,9,10 | sort -u > shop.txt) \
| cut -f 1,2,3,7,11,12 | gzip > transaction.txt.gz


こうやって商品マスター、店舗マスター、トランザクションの三つに分割しています。このcsvはたまたま要素内に半角カンマがないことがわかっていたので、切り出しは単純にカンマ区切りでOKでしたが、ダブルクオーテーションの中にカンマや改行が入っていたらもっと厄介なことになっていました。カラム3と7がそれぞれ商品マスタと店舗マスタの固有IDです。pos.csv.gzはめちゃめちゃ巨大なので、展開したデータを置いておきたくないですし、さらに文字コードを変換したデータを置くのも厳しいので、展開と文字コード変換を一度だけ実施した上で三つの作業を同時にやりたかった、というわけです。

posted by jinya at 19:59| Comment(0) | 日記 | このブログの読者になる | 更新情報をチェックする
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。