Django:DataFrameを配列(リスト)にしてDBに格納する時の注意事項

Django:DataFrameを配列(リスト)にしてDBに格納する時の注意事項
CSVデータをDBに取り込む時どうしてますか。
CSVの構造をそのまま(計算処理と化しないで)DBに持っていくのであれば、DataFrameに格納して【to_sql】で投げてしまうのが一番簡単です。が、今回はDataFrame化した値をリストで引き取って計算して統制してDBにSaveするという面倒な段取りを踏んだ時の注意事項です。
問題はDataFrameに格納したときに発生している
DataFrameに格納すると下記2点の問題が発生します。
- フィールド型が勝手に割り振られる ※int にしたいのに float になったりなど
- 空白にnanが割り当てられる
フィールド型が勝手に割り振られる
例えばJANコードの列が存在していた場合、この列は高確率でfloat が割り当てられます。
CSVデータの中でダブルクォーテーションで囲っている場合はこの限りではないのですが、13桁の数字のみであった場合、私の実験では100% floatとなりました。JANのお尻に【.0】が付いていると言う不思議な構成です。
これを解決するには、pd.read_csv 実行の際にdtypeを指定してあげます。
1 2 3 4 5 6 7 8 9 10 11 |
import pandas as pd file = "hoge.csv" encode = "" head = 0 dtype = {'日付':'object','総売上':'int','通常購入':'int','アクセス人数':'int','客単価':'float','転換率':'float'} # CSV読込み df = pd.read_csv(file, encoding=encode, header=head, dtype=dtype) print df |
dtypeは辞書型で作成して配置します。
要素名と要素数が状況によって変わる場合は ifで状況分類をする方法以外にこんな手段もあります。
1 2 3 4 5 6 7 8 9 10 |
import pandas as pd file = "hoge.csv" encode = "" head = 0 # CSV読込み df = pd.read_csv(file, encoding=encode, header=head, dtype='object') print df |
辞書型を配置せずにobjectとだけ記載しました。
こうする事で、すべての値を一度文字列として所得し格納すます。
その後 .astype(float) などで適切な型に変更すれば、要素数のコントロール不要となり関数を外に配置することも可能です。
空白にnanが割り当てられる
DataFrameは何もしなければデータ欠損をnanで埋めてくれます。
でも、DataFrameをリストに変換するとnanという値が入ったカラムが出来上がります。
テキスト上では2つの回避方法(欠損値の穴埋め方法)があります。
- na_values の設定
- df.fillna()の利用
na_values の設定
1 2 |
# CSV読込み df = pd.read_csv(file, encoding=encode, header=head, dtype=dtype, na_values='--') |
pandas.read.csv の実行時に na_values の設定をするのですが、私の環境ではなぜか動作しませんでした。
df.fillna()の利用
1 2 3 4 |
# CSV読込み df = pd.read_csv(file, encoding=encode, header=head, dtype=dtype) data = df.fillna('--') |
fillnaコマンドは nan の値に指示した値を置換してくれます。
df = df.fillna(‘–‘) と書いてしまうと大本の変数df を置換できず【 nan 】が表示され続けますので注意しましょう。
エラー:Must specify a fill ‘value’ or ‘method’.
今回の記事を記載しようと思った切っ掛けです。
チョット考えれば分かるのですが、DataFrameではNullもNoneも欠損値の記号ではありません。
DataFrameの欠損値はあくまでもnanが記号となります。
つまりこんなコードを書いてしまうと上のエラー(Must specify a fill ‘value’ or ‘method’.)が発生します。
1 2 3 4 |
# CSV読込み df = pd.read_csv(file, encoding=encode, header=head, dtype=dtype) data = df.fillna(None) |
そのためDataFrameに格納した値をリスト化した場合、高確率で欠損値には【nan】が入っていることになります。
指定カラムにnanが入っている場合Noneに置き換えてDBに渡せばDB側でNULLを登録できる
場合分けなのでIF文を使います。
1 2 3 4 5 6 7 |
# nan が入るカラム番号をリストに記載 list = [3,4,5,6] #nanをNoneに変換 for li in list: if str(data[li]) == 'nan': data[li] = None |
注目ポイントは6行目【 str(data[li]) == ‘nan’ 】
ついつい【data[li] == ‘nan’ 】としてしまいますが、DataFrame上で該当の値は 『nan』となっており『’nan’』ではありません。そのため【 .tolist() 】でリスト化した値も『nan』となっています。
つまり【data[li] == ‘nan’ 】では一致が発生しません。
一致を取得するためにはdata[li] にstr()を掛けて文字列にしたうえで == ‘nan’ でifに掛ける必要があります。
チョットした事なんですけど、忘れがちなんですよね。
まとめ
今回の例のようなNanをMySQLの数値カラムに渡してしまった場合、100%エラーです。
(文字列の様に登録はしてくれません)
バックエンド弄っていて面倒なのは大体【欠損処理】【NULLの処理】【日付処理】です。
これを知っていればバックエンドは怖くない。
-
前の記事
Django:MySQLのdatetime型を日付一致で取り出す方法(QuerySetで) 2021.04.15
-
次の記事
システム案件の探し方:お手伝いしているプログラミングスクールで頂いた質問について 2021.04.21
コメントを残す