質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

標準出力

標準出力(stdout)は、プログラムが標準的に用いるデータ出力元。標準出力に書き込み要求を発行しすることにより、ディスプレイ装置にデータを表示することができます。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

4回答

1401閲覧

大容量のcsvをグループ分けしたい

退会済みユーザー

退会済みユーザー

総合スコア0

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

標準出力

標準出力(stdout)は、プログラムが標準的に用いるデータ出力元。標準出力に書き込み要求を発行しすることにより、ディスプレイ装置にデータを表示することができます。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

0クリップ

投稿2020/06/15 04:50

csvファイル「sample.csv」に、以下のような内容が200万行以上書いてあります。容量にして50MB以上です。

csv

1id,name 2"A101","aaa" 3"A101","bbb" 4"B202","aaa" 5"B202","bbb" 6"B202","ccc" 7"C303","aaa" 8"C303","bbb" 9"C303","ccc" 10"C303","ddd"

この大容量のcsvを、項目「id」ごとにグループ分けして整形し、以下のように1行ずつ標準出力したいと考えています。

"id":"A101" "name":["aaa","bbb"] "id":"B202" "name":["aaa","bbb","ccc"] "id":"C303" "name":["aaa","bbb","ccc","ddd"]

出力結果がこのようになる処理をシェルスクリプトで記述したいのですが、どのように記述すると良いのでしょうか?

試したこと

シェルスクリプトに以下のように記述しました。
この記述方法ですと、数百KB程のcsvを読み取れることは確認できたのですが、
50MBになると何時間経っても処理が終わりませんでした。

shell

1data1=[$(awk -F, '(NR>1){r[$1]=r[$1] $2} 2 END{for(i in r) print "{\"id\": "i FS " " "\"name\": ["r[i]"]}"}' sample.csv)] 3 4data2=$(echo ${data1} | sed "s/\"\"/\", \"/g" | sed "s/} {/}, {/g") 5 6echo ${data2} | jq -c '.[]' | sed -E "s/,(.*[)/$(printf '\\012 ')\1/g" | sed "s/[\{\} ]//g]"

大容量のcsvでも処理できるようにするための方法をご教授いただけますと幸いです。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

こう云う処理はシェルスクリプトよりawkスクリプトで処理したほうが楽じゃないでしょうか。

# test.awk BEGIN{ FS="," } NR>1{ names[$1] = names[$1] "," $2; } END{ for(id in names){ name = substr(names[id],2) printf "\"id\":%s\n", id printf "\"name\":[%s]\n", name } }
awk -f test.awk sample.csv

投稿2020/06/15 07:22

KojiDoi

総合スコア13692

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/18 16:35

大変参考になりました! ありがとうございます!
guest

0

csv を DB に import を、 SQL で集計するとか、 ruby や python などで DB にアクセスして
集計して、所定の書式で出出力するという方針は取れないのですか?

id の種類が少なければ、 id で grep して、 id 毎に分割したファイルを作り、書くファイル毎に所定の書式に変換するとかの方法も考えられます。

id ソート (sort コマンドでこの程度サイズのファイルでも可能なはず) すれば、所定の書式に書き換えることは
小さなプログラムを書いて実現できるはず。

投稿2020/06/18 11:17

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/18 16:36

大変参考になりました! ありがとうございます!
guest

0

ソートされている前提で、

Bash

1#!/bin/bash 2cat sample.csv | 3sed '1d;$a,' | 4while IFS=, read key val 5do if [ "$key" = "$old" ] 6 then data="$data,$val" 7 else if [ "$old" ] 8 then echo '"id":'"$old" 9 echo '"name":['"${data#,}]" 10 fi 11 old="$key" 12 data="" 13 fi 14done

投稿2020/06/16 11:42

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/18 16:35

大変参考になりました! ありがとうございます!
guest

0

ベストアンサー

bash の連想配列を使う方法はどうでしょうか。
id の順番が変わってしまいますが。

bash

1declare -A names 2while IFS=, read id name ; do 3 [ "$id" = "id" ] && continue 4 if [ "${names[$id]}" = "" ] ; then 5 names[$id]="$name" 6 else 7 names[$id]="${names[$id]},$name" 8 fi 9done < CSVFILE 10 11for id in ${!names[*]} ; do 12 echo "\"id\":$id" 13 echo "\"name\":[${names[$id]}]" 14done

(2020/06/16 15:44) 追記
順序を保持するように改良。

bash

1declare -A names 2declare -a id_order 3while IFS=, read id name ; do 4 [ "$id" = "id" ] && continue 5 if [ "${names[$id]}" = "" ] ; then 6 names[$id]="$name" 7 id_order+=($id) 8 else 9 names[$id]="${names[$id]},$name" 10 fi 11done < CSVFILE 12 13for id in ${id_order[*]} ; do 14 echo "\"id\":$id" 15 echo "\"names\":[${names[$id]}]" 16done

投稿2020/06/15 06:52

編集2020/06/16 06:44
TaichiYanagiya

総合スコア12173

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/18 16:34

大変参考になりました! ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問