LoginSignup
2
1

More than 5 years have passed since last update.

awkでcsvを縦に斬る

Last updated at Posted at 2016-07-15

やりたいこと

サイズが大きいcsvは読みこむだけでも一苦労。だったら分割してやればいいと思ったわけですが、splitを使うと2ファイル目以降がヘッダ行が無くなって分からなくなってしまう。
そもそも、カラム数が多いcsvを頑張って開いて横スクロールすることに限界を感じたので、だったら横じゃなくて縦に斬ってやろう。

サンプル

  • large.csvは15カラム目までがユニークなデータカラム、それ以降のカラムは51x51の繰り返しという、いかにもデータですというCSVです。
  • 今回はこれをユニークなデータカラム、51x26のデータカラム51x25という具合に分割します(51x25も十分デカイですが...)
  • それではサンプルのスクリプトです。
awkでcsvを縦に斬る
  cat large.csv | awk -F"," 'BEGIN{OFS=","}{ret=""}{for(i=1;i<=NF;i++){if(i<16){if(ret==""){ret=$(i)}else{ret=ret","$(i)}}}}{print ret}' > small_1.csv
  cat large.csv | awk -F"," 'BEGIN{OFS=","}{ret=""}{for(i=1;i<=NF;i++){if(i>15 && i < 51*26+16){if(ret==""){ret=$(i)}else{ret=ret","$(i)}}}}{print ret}' > small_2.csv
  cat large.csv | awk -F"," 'BEGIN{OFS=","}{ret=""}{for(i=1;i<=NF;i++){if(i>51*26+15){if(ret==""){ret=$(i)}else{ret=ret","$(i)}}}}{print ret}' > small_03.csv

軽く説明

  • まずcatで該当のCSVを読み込みパイプでawkに流します
  • awk-F","でカンマを区切り文字にしています
  • BEGINの最初の{OFS=","}は意味をなしていませんが、CSVで出力したいという意思表示です。
  • for文で1~NF(awkの結果の取る値すべて)を回し、ifの条件の中で取り出したい範囲でを指定しretに格納します
  • retに格納する際はretが空文字であれば$(i)を、retが空文字でなければカンマ区切りで結合
  • 最後にprint retで出力して完了です。

課題

  • パフォーマンスの問題
    1. まとめてて思ったのですが、for文ですべて回して、ifで範囲指定は無駄ですね。for文の中で範囲絞ったほうが速い。
    2. 3つに分割するのに3回ファイルを読み込むのは勿体無い。1回のループで3つの変数に格納して1つのファイルに出力して、splitで3分割するほうが速いのかもしれない。

結論 

awkを使えばコマンドライン上でテキスト操作ができるので、使いこなせると便利。
そもそもこんなデカイCSVなんかつくらないでくれよ

追記

cutのほうが速いかもしれない、というコメントを頂きました。(ご指摘ありがとうございます。)
実際コマンドとしてはこんな感じですね

cutでCSVを縦に斬る
cut large.csv -f 1-15 -d ,
cut large.csv -f 16-1341 -d ,
cut large.csv -f 1342- -d ,

あとはこれをファイルに書きだしてやればOK、こちらのほうがコマンドがシンプルで使いやすいですね!!
あえてawkの利点をいうのであれば、csvのカラム数をNFで取得できるので、動的に分割するカラムを指定するスクリプトがかけるというところでしょうか。

2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1