#はじめに
前回の記事で紹介したDaskによって、だいたいのデータは分散処理できるようになったのですが、一部それでも解決できない問題がありました。
それは「ダミー変数化」の処理です。
##ダミー変数化とは?
ダミー変数化とは、数値でないデータを0と1のデータに変換する手法のことです。
具体的には、例えばユーザーの職種が入っているデータがあるとします。
UserID | 職種 |
---|---|
1 | エンジニア |
2 | 経理 |
3 | 営業 |
4 | 企画 |
5 | エンジニア |
これをこんな風にカラムを横に広げて0と1で表せるようにするのがダミー変数化です。
UserID | 職種__エンジニア | 職種__経理 | 職種__営業 | 職種_企画 |
---|---|---|---|---|
1 | 1 | 0 | 0 | 0 |
2 | 0 | 1 | 0 | 0 |
3 | 0 | 0 | 1 | 0 |
4 | 0 | 0 | 0 | 1 |
5 | 1 | 0 | 0 | 0 |
##なぜ分散処理するとダミー変数化できないことがあったのか
例えば上の例で使った「職種」というカラムがあったとしますよね。
この「職種」というカラムにどういうデータが入ってくるわかっていればあらかじめカラムを追加しておくこともできますが、事前にわからない場合は、データを読み込むまでカラムをどれだけ広げればよいのかわかりません。
もし、10万行ある場合は、10万行全て読まないと正しくダミー変数化できないことになります。
Daskでは単純に「この列だけ読み込む」という指定ができなかったため、ダミー変数化をしようとするとデータをまず列分割をして「対象の列だけのデータ」を別で作る必要がありました。私自身Daskに慣れていないので、その手順を整理するのが面倒で諦めてしまいました。
そこで、DaskやPandas、Numpyを使わずに素のPythonで処理する方向で処理を作成しました。
#「素のPython」での処理作成方法
素のPythonでダミー変数化するためには、2つポイントがあります。
**「1行ずつ読み込むこと」****「データを2回読み込むこと」**の2点です。
##データを1行ずつ読み込む
データ処理用のライブラリを使わずにデータを読み込むのですが、
ライブラリが無いため分散処理はできません。
どうするかというと**「1行読んで処理する」を行数分ひたすら繰り返す**ことになります。
Pythonのreadline()
を使うと1行ずつファイルオブジェクトからデータを取り出すことができますが、もっと単純にファイルオブジェクトをそのままfor分に突っ込むことでも実現できます。
with open('C:/Python/データ.csv') as lines:
for line in lines:
#行ごとの処理
##データを2回読む
1行ずつ読み込む処理をかけば、ライブラリを使わなくともメモリをパンクさせずデータを読んでいくことができます。
ただし、先にも書いたようにダミー変数化するためにはユニークなデータの数だけカラムを追加する必要があり、そのためには全データを把握する必要があります。
そこで、**「カラムを作るために1回」と「データを0、1に置き換えるために1回」**の計2回データを読むことでこの問題を解決します。
##具体的な処理の実装
###カラム作成
まず下の例2がカラムを読み込むための1回目の読み込み処理の例です。
with open('C:/Python/データ.csv') as lines:
title = lines.readline() #csvにカラム名が無い場合は不要
for line in lines:
pieces = line.split(',')
if pieces['カラムの位置'] not in key_list:
key_list.append(str(pieces['カラムの位置']))
title_list = []
for key in key_list:
title_list.append('職種_' + str(key))
title = ','.join(title_list)
for文で1行ずつ読んでいき、not in
を使うことで今までにないデータが出てきたら、それをリストに追加していきます。これでデータ全体の種類を把握できます。
そのあと、作ったデータのリストに対して更にfor文を回しタイトルを作成しています。
(ライブラリが無いとどうしてもfor文祭になってしまいますね...)
「カラムの位置」は単純にcsvの何番目のデータかということです。
上の職種データの例だと2番目なので「1」が入ります。
###0、1データへの置換
with open('C:/Python/練習/提供データ_0401_0403.csv') as lines:
##csvにカラム名が無い場合は不要
lines.readline()
for line in lines:
pieces = line.split(',')
line_list = ['0'] * line_size
for i, key in enumerate(key_list):
if pieces['カラムの位置'] == key:
line_list[i] = '1'
データの置き換えの処理でも同じようにfor文で1行ずつ読み込んでいきます。
ただし、今度は各行ごとに先ほど「カラム作成」で作ったデータのリスト(key_list)と使ってどのデータに当てはまるかを探していきます。
line_list = ['0'] * line_size
ここの1文で全て0の行を作って、データがリストにあてはまったカラムのみ1に置き換えるようにしています。
enumerate
は自分で変数を作らなくてもfor文を回した回数をカウントしてくれる奴です。
i
にfor文を回した回数が入るのでline_list[i]
とすればfor文を回すごとに1に置き換える場所をずらしてくれます。
これでライブラリを使わずに大規模データのダミー変数化ができるようになりました。
今回の処理はカラムごとにデータを指定しているため複数カラムをダミー変数化する場合は
複数処理を書く必要があるので、注意してください。
最後に
今回、私が業務でぶつかった「大規模データをダミー変数化できない」という問題が解決できました!!
他にも効率的な手法があれば、ぜひ教えてください!
また、以前の記事もよければご覧ください!じゃあの!
【初めての大規模データ①】Linuxでファイル分割
【初めての大規模データ②】Daskでの並列分散処理