こんにちは!
Pythonを使ったデータ分析やアプリケーション開発において、CSVファイルを扱う機会はよくあります。
CSVの読み書き、整形をPythonで行うなら、Pandasを使うのがとても簡単です。
コード量も少なく可読性もいいので、メンテナンス性も確保できます。
今回は、PythonでPandasを使ってCSVをサクッと整形する方法をご紹介します。
基本構造
PythonでPandasを使ってCSVをサクッと整形する処理の基本構造は、次のとおりです。
import pandas as pd def main(): df = pd.read_csv( # 読み込み "./input.csv", # 入力ファイル dtype=str # 値が化けないように ) df = edit(df) # データ加工 df.to_csv( # 書き出し "./output.csv", # 出力ファイル index=False # read_csv()で付加されたindexを除去 ) def edit(df): # データ加工 # df を加工する処理を記述する return df if __name__ == "__main__": main()
Pandasは本当に便利ですね。
なお、Pandasによって「値が変わってしまう事象」が発生しないように、dtype=strをしていして、すべての値を文字列として読み込みます。
dtypeを指定しないと、Pandasがうまいこと型を推測しようとしてくれます。(型推測、とよばれます。)
しかし、数値が一律floatになって小数点が付いてしまうといった「値が変わってしまう事象」が発生します。
「値が変わってしまう事象」が発生すると、何が問題なのでしょうか?
例えば、DBから取得したCSVを加工してから、同じテーブルに入れようとすると、エラーが発生する可能性があります。
整数型のカラム値「1」について、CSV出力した時点では「1」のままですが、Pandasによりfloatで読み込まれて「1.0」に変換され、そのままCSVに「1.0」で出力されたとき、それをDBにINSERTしようとすると「1.0」は整数ではないのでDBへのINSERTでエラーとなります。
なので、Pandasで読み込む前のCSVと書き出したあとのCSVの型を同じにする工夫が必要となります。
この工夫として、文字列として扱うのが手っ取り早いです。
いくつかの加工処理の例
加工処理について、いくつかの例を挙げます。
最終列を除去する
最終列を除去するには、次のようにします。
import pandas as pd def main(): df = pd.read_csv( # 読み込み "./input.csv", # 入力ファイル dtype=str # 値が化けないように ) df = edit(df) # データ加工 df.to_csv( # 書き出し "./output.csv", # 出力ファイル index=False # read_csv()で付加されたindexを除去 ) def edit(df): # データ加工 df = df.iloc[:, :-1] # 最終列を除去 return df if __name__ == "__main__": main()
ヘッダを除去する
ヘッダを除去するには、次のようにします。
import pandas as pd def main(): df = pd.read_csv( # 読み込み "./input.csv", # 入力ファイル dtype=str # 値が化けないように ) df = edit(df) # データ加工 df.to_csv( # 書き出し "./output.csv", # 出力ファイル index=False, # read_csv()で付加されたindexを除去 header=False # ヘッダを出力しない ) def edit(df): # データ加工 # df を加工する処理を記述する return df if __name__ == "__main__": main()
文字コードを変換する
文字コードを変換するには、次のようにします。
import pandas as pd def main(): df = pd.read_csv( # 読み込み "./input.csv", # 入力ファイル dtype=str, # 値が化けないように encoding = "shift-jis" # SJISで読み込み。CP932相当。 ) df = edit(df) # データ加工 df.to_csv( # 書き出し "./output.csv", # 出力ファイル index=False, # read_csv()で付加されたindexを除去 encoding = "utf-8" # UTF-8で書き出し。 ) def edit(df): # データ加工 # df を加工する処理を記述する return df if __name__ == "__main__": main()
なお、
となります。
カラムの前後の空白を除去する
カラムの前後の空白を除去するには、次のようにします。
import pandas as pd def main(): df = pd.read_csv( # 読み込み "./input.csv", # 入力ファイル dtype=str # 値が化けないように ) df = edit(df) # データ加工 df.to_csv( # 書き出し "./output.csv", # 出力ファイル index=False # read_csv()で付加されたindexを除去 ) def edit(df): # データ加工 # df を加工する処理を記述する df.iloc[:, 0] = df.iloc[:, 0].str.strip() return df if __name__ == "__main__": main()
補足
巨大なCSVファイルの場合
巨大なファイルの場合はPandasのread_csv()のchunksizeオプションを使えば読み込める可能性があります。
本記事では本論でないため、申し訳ありませんが割愛します。
文字コードだけなら標準関数でOK
単に文字コードを変換するだけなら、Python組み込みの標準関数open()、write()で十分です。
例えば、Shift_JISからUTF-8に変換するには、下記のようにします。
with open('./input.csv', encoding="cp932") as fin: with open('./output.csv', 'w', encoding="utf8") as fout: fout.write(fin.read())
ただし、こちらも巨大なファイルの場合は、一度に読み込むとメモリエラーとなる可能性があります。
これを防ぐため、例えば、BufferedReader、BufferedWriterなどのioパッケージのストリームを使って少しずつ読み込むのもいいと思います。
なお、encoding="cp932"のところが encoding="shift jis"でないのは、cp932のほうがカバーできる文字コードが多いためです。
encoding="cp932"にしておくのが無難です。