SQL (超巨大) 集計データから、行列形式に変換して出力する。
概要
- 巨大な集計データがあり、それを行列の形に変換して出力したい。
- 集計データを行列化すると、ほとんどの値がゼロの『スパースな』行列。
- 始めは Python listに一度入れる形で実装したが、メモリが足りなくなった orz..
- dictionary にすることで解決!!
入力データ
- PostgreSQL に格納されているデータ群を CSV 化したもの。
- 「行ID、列ID、重み」の順になっていて、重みがゼロの行は省かれている。
- 行ID、列ID はそれぞれ 1 始まり。
- データ一部抜粋:
1,7,1 1,12,1 1,13,1 … 2,31,1 2,35,1 2,45,1 2,50,1 …
Python で集計 ( list 型版 ) → 失敗 (ノД`)
- Python で集計スクリプト作成。
- list で集計し、それを書き出す形。
- 始めの初期化時にメモリ不足で強制終了 orz...
- 以下、(失敗版) ソースコード抜粋:
import csv # ( 関数呼び出し部省略 ) def create_matrix( in_filename, out_filename, row_size, col_size ) : # 配列初期化 print "Create matrix memory..." matrix = [ [ x for x in range( col_size + 1 ) ] ] + [ [y+1] + [0] * col_size for y in range( row_size ) ] # データ設定 print "Read matrix data file..." f_in = open( in_filename ) reader = csv.reader( f_in, lineterminator = '\n' ) for row in reader : if row < 3 : print "Warning: Row {0} is too few columns ({1}). skip...".format( row_index + 1, len( row ) ) continue object_id = int( row[0] ) infon_id = int( row[1] ) weight = int( row[2] ) matrix[object_id][infon_id] = weight f_in.close() # データ書き込み print "Write matrix to file..." f_out = open( out_filename, 'w' ) writer = csv.writer( f_out, lineterminator = '\n' ) for row in matrix : writer.writerow( row ) f_out.close()
Python で集計 ( dictionary 型版 ) → 成功 \(^ ^)/
- list の初期化を止め、matrix 変数を dictionary 型に変更。
- ゼロの値の部分を持たなくなったので、大幅にメモリ節約。
- ただし、書き込みのスピードは…。
- 以下、(成功版) ソースコード抜粋:
import csv # ( 関数呼び出し部省略 ) def create_matrix( in_filename, out_filename, row_size, col_size ) : # データ設定 print "Read matrix data file..." f_in = open( in_filename ) reader = csv.reader( f_in, lineterminator = '\n' ) matrix = {} for row in reader : if row < 3 : print "Warning: Row {0} is too few columns ({1}). skip...".format( row_index + 1, len( row ) ) continue object_id = int( row[0] ) infon_id = int( row[1] ) weight = int( row[2] ) if not matrix.has_key( object_id ) : matrix[object_id] = {} matrix[object_id][infon_id] = weight f_in.close() # データ書き込み print "Write matrix to file..." f_out = open( out_filename, 'w' ) writer = csv.writer( f_out, lineterminator = '\n' ) header = [ x for x in xrange( col_size + 1 ) ] writer.writerow( header ) for object_id in xrange( 1, row_size + 1 ) : row = [ object_id ] for infon_id in xrange( 1, col_size + 1 ) : if matrix.has_key( object_id ) and matrix[object_id].has_key( infon_id ) : row.append( matrix[object_id][infon_id] ) else : row.append( 0 ) writer.writerow( row ) f_out.close()
出力結果
- 1行目が header で列番号
- 2行目以降、1列目が行番号、2列目以降データ。ほとんどゼロ。
- データ一部抜粋
0,1,2,3,4,5,6,… 1,0,0,0,0,0,0,… 2,0,0,1,0,0,0,… …
まとめ
- 「Python で巨大なデータを扱うときにはリストを使ってはいけない」ですね。