本記事では、PythonでのCSVファイルの扱い方を解説します。機械学習用データや公共機関が提供する統計データはCSVで提供されていることが多いので、扱いに慣れておきましょう。
推奨はpandasライブラリを使用する方法なので、迷ったらこの方法を参考にしてください。
なお、本記事で解説しているソースコードは、以下のGoogle Colabノートブックで実行して動作を確認することができます。ぜひご活用下さい。
python_csv_basic.ipynb
PythonでのCSVファイルの扱い方
CSVファイルの基本形式 | 初心者向け
CSVは、Comma Separated Valuesの略で、カンマで区切られた値という意味になります。記法は簡単で、名前の通りデータをカンマで区切って並べていく形式です。
例えば、以下の宿泊記録の表データをCSV形式で格納することを考えます。
宿泊日 | 宿泊日数 | 国籍 | 性別 | 年齢 |
---|
2022/05/15 | 2 | 日本 | 男性 | 28 |
2022/06/20 | 2 | 日本 | 男性 | 51 |
2022/06/20 | 2 | イギリス | 女性 | 32 |
2022/07/03 | 3 | イギリス | 男性 | 35 |
2022/08/10 | 4 | 日本 | 女性 | 30 |
CSVでは、以下のように書くことが多いです。値をカンマで区切って並べ、表を一行ずつ区切ってCSV側でも同じように改行します。例として、ファイル名をguest.csvとします。
{"filename":"guests.csv","code":"\"\u5bbf\u6cca\u65e5\",\"\u5bbf\u6cca\u65e5\u6570\",\"\u56fd\u7c4d\",\"\u6027\u5225\",\"\u5e74\u9f62\"\n\"2022\/05\/15\",\"2\",\"\u65e5\u672c\",\"\u7537\u6027\",\"28\"\n\"2022\/06\/20\",\"2\",\"\u65e5\u672c\",\"\u7537\u6027\",\"51\"\n\"2022\/06\/20\",\"2\",\"\u30a4\u30ae\u30ea\u30b9\",\"\u5973\u6027\",\"32\"\n\"2022\/07\/03\",\"3\",\"\u30a4\u30ae\u30ea\u30b9\",\"\u7537\u6027\",\"35\"\n\"2022\/08\/10\",\"4\",\"\u65e5\u672c\",\"\u5973\u6027\",\"30\"","language":"csv","id":21}
1行目に項目名(宿泊日、宿泊日数、…)が、2行目以降に値が並びます。
CSVはシンプルで記法で、様々なプログラミング言語・データベースで直接操作できます。
実務的には、人が1つ1つ処理できない大量のデータを扱うことの方が多いので、データはCSVに格納し、プログラムやデータベースで直接操作する方が都合が良いのです。
“Vanilla” PythonでのCSVデータ操作
Pythonは、Python本体と数値計算ライブラリをパッケージ化したanaconda、anacondaの軽量バージョンであるminicondaなど、様々な形で提供されています。
ただ、2章ではとりあえず最も利用者が多いPython単体(”Vanilla” Pythonと呼ばれます)だけで出来るCSVの扱いを紹介します。
CSVファイルの読み込み | csv.reader
CSVファイルの読み書きのために、csvライブラリをインポートします。pythonでは、ファイルを開く時、open()
メソッドを使用します。open()
の引数にCSVファイルのパスを指定、csv.reader()
にopen()
で読み込んだオブジェクトを指定します。
{"code":"import csv\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n # 1\u884c\u305a\u3064\u51fa\u529b\n for row in reader:\n print(row)\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# ['\u5bbf\u6cca\u65e5','\u5bbf\u6cca\u65e5\u6570','\u56fd\u7c4d','\u6027\u5225','\u5e74\u9f62']\n# ['2022\/05\/15','2','\u65e5\u672c','\u7537\u6027','28']\n# ['2022\/06\/20','2','\u65e5\u672c','\u7537\u6027','51']\n# ['2022\/06\/20','2','\u30a4\u30ae\u30ea\u30b9','\u5973\u6027','32']\n# ['2022\/07\/03','3','\u30a4\u30ae\u30ea\u30b9','\u7537\u6027','35']\n# ['2022\/08\/10','4','\u65e5\u672c','\u5973\u6027','30']","filename":"","language":"python","id":0}
CSV形式のデータは、1行ずつ区切られ、Pythonのリストとして格納されます。デフォルトでは、データは全て文字列として認識されるので注意してください。
CSVファイルへの書き込み | csv.writer
CSVファイルへの書き込みは、csv.writer()
で行います。また、書き込み時には、open()
に書き込みモード’w”を指定します。
書き込みモードでは、CSVファイルを上書きしてしまうので注意してください。
{"code":"import csv\nwith open('guests.csv', 'w') as f:\n writer = csv.writer(f)\n # CSV\u30d5\u30a1\u30a4\u30eb\u306b\u66f8\u304d\u8fbc\u307f\n writer.writerow(['2022\/12\/10', '3', '\u30a2\u30e1\u30ea\u30ab', '\u5973\u6027', '25'])\n writer.writerow(['2022\/12\/10', '3', '\u30a2\u30e1\u30ea\u30ab', '\u7537\u6027', '30'])\n# \u66f8\u304d\u8fbc\u307f\u5f8c\u306eCSV\u30d5\u30a1\u30a4\u30eb\u306e\u5185\u5bb9\u3092\u78ba\u8a8d\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n for row in reader:\n print(row)\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# ['2022\/12\/10', '3', '\u30a2\u30e1\u30ea\u30ab', '\u5973\u6027', '25']\n# ['2022\/12\/10', '3', '\u30a2\u30e1\u30ea\u30ab', '\u7537\u6027', '30']","filename":"","language":"python","id":0}
CSVファイルへの追記 | appendモード
元のCSVファイルを上書きせず、ファイルの後ろに追記したい時は、appendモードを使用します。open()
の引数にappend(追記)を表す’a’を指定します。
{"code":"# append\u30e2\u30fc\u30c9 'a'\nwith open('guests.csv', 'a') as f:\n writer = csv.writer(f)\n # 1\u884c\u8ffd\u8a18\n write.writerow(['2022\/12\/20', '2', '\u65e5\u672c', '\u7537\u6027', '50'])\n# \u66f8\u304d\u8fbc\u307f\u5f8c\u306eCSV\u30d5\u30a1\u30a4\u30eb\u306e\u5185\u5bb9\u3092\u78ba\u8a8d\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n for row in reader:\n print(row)\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# ['\u5bbf\u6cca\u65e5','\u5bbf\u6cca\u65e5\u6570','\u56fd\u7c4d','\u6027\u5225','\u5e74\u9f62']\n# ['2022\/05\/15','2','\u65e5\u672c','\u7537\u6027','28']\n# ['2022\/06\/20','2','\u65e5\u672c','\u7537\u6027','51']\n# ['2022\/06\/20','2','\u30a4\u30ae\u30ea\u30b9','\u5973\u6027','32']\n# ['2022\/07\/03','3','\u30a4\u30ae\u30ea\u30b9','\u7537\u6027','35']\n# ['2022\/08\/10','4','\u65e5\u672c','\u5973\u6027','30']\n# ['2022\/12\/20', '2', '\u65e5\u672c', '\u7537\u6027', '50']","filename":"","language":"python","id":0}
2次元リストで操作
CSVファイル全体を、1つの2次元リストにまとめてしまうことも可能です。こうすると、表の行・列を指定する感覚で範囲を指定出来るようになるので便利です。
{"code":"import csv\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n \n # CSV\u5168\u4f53\u30922\u6b21\u5143\u30ea\u30b9\u30c8\u3068\u3057\u3066\u8aad\u307f\u8fbc\u307f\n data = [row for row in reader]\n print(data)\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# [['\u5bbf\u6cca\u65e5','\u5bbf\u6cca\u65e5\u6570','\u56fd\u7c4d','\u6027\u5225','\u5e74\u9f62'],['2022\/05\/15','2','\u65e5\u672c','\u7537\u6027','28'],['2022\/06\/20','2','\u65e5\u672c','\u7537\u6027','51'],['2022\/06\/20','2','\u30a4\u30ae\u30ea\u30b9','\u5973\u6027','32'],['2022\/07\/03','3','\u30a4\u30ae\u30ea\u30b9','\u7537\u6027','35'],['2022\/08\/10','4','\u65e5\u672c','\u5973\u6027','30']","filename":"","language":"python","id":0}
スライス表記で、行を指定して切り出すことができます。表の行番号・列番号を指定する感覚です。
{"code":"import csv\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n data = [row for row in reader]\n \n # 1\u884c\u76ee\u304b\u30893\u884c\u540d\u3092\u8868\u793a\n print(data[0:3])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# [['\u5bbf\u6cca\u65e5', '\u5bbf\u6cca\u65e5\u6570', '\u56fd\u7c4d', '\u6027\u5225', '\u5e74\u9f62'], ['2022\/05\/15', '2', '\u65e5\u672c', '\u7537\u6027', '28'], ['2022\/06\/20', '2', '\u65e5\u672c', '\u7537\u6027', '51']]","filename":"","language":"python","id":0}
zip()
関数を使用すれば、行と列をひっくり返す操作(転置を取る)ができます。特定の列だけを取り出すために利用できます。
下の例は、guest.csvの中の、宿泊者の国籍だけを抽出した例です。
{"code":"import csv\nwith open('guests.csv', 'r') as f:\n reader = csv.reader(f)\n data = [row for row in reader]\n \n # zip\u95a2\u6570\u30672\u6b21\u5143\u30ea\u30b9\u30c8\u306e\u884c\u3068\u5217\u3092\u5165\u308c\u66ff\u3048\u308b\n data_t = list(zip(*data))\n # \u5143\u30c7\u30fc\u30bf\u306e3\u5217\u76ee\uff08\u56fd\u7c4d\uff09\u3092\u51fa\u529b\n print(data_t[2])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# ('\u56fd\u7c4d', '\u65e5\u672c', '\u65e5\u672c', '\u30a4\u30ae\u30ea\u30b9', '\u30a4\u30ae\u30ea\u30b9', '\u65e5\u672c')","filename":"","language":"python","id":0}
宿泊日 | 宿泊日数 | 国籍 | 性別 | 年齢 |
---|
2022/05/15 | 2 | 日本 | 男性 | 28 |
2022/06/20 | 2 | 日本 | 男性 | 51 |
2022/06/20 | 2 | イギリス | 女性 | 32 |
2022/07/03 | 3 | イギリス | 男性 | 35 |
2022/08/10 | 4 | 日本 | 女性 | 30 |
Pandasライブラリを使う方法|推奨
“Vanilla” Pythonでも手軽にCSVが扱えますが、データ分析を行ったり複雑な操作を行ったりする場合にはpandasライブラリの使用を推奨します。
DataFrameにCSVを格納
pandasでは、DataFrameという形式でCSVを扱います。試しに、機械学習入門者用のiris(アヤメ)データセットを読み込んで見ましょう。
pandasライブラリのread_csv()
メソッドにURLを指定すると、オンラインから直接データを取得可能です。
{"code":"import pandas as pd\ndf = pd.read_csv('https:\/\/raw.githubusercontent.com\/mwaskom\/seaborn-data\/master\/iris.csv')\n# \u30c7\u30fc\u30bf\u306e\u5148\u982d\u3092\u3044\u304f\u3064\u304b\u8868\u793a\nprint(df.head())\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# sepal_length sepal_width petal_length petal_width species\n# 0 5.1 3.5 1.4 0.2 setosa\n# 1 4.9 3.0 1.4 0.2 setosa\n# 2 4.7 3.2 1.3 0.2 setosa\n# 3 4.6 3.1 1.5 0.2 setosa\n# 4 5.0 3.6 1.4 0.2 setosa","filename":"","language":"python","id":0}
DataFrameは、以下のような構造で、各行のIDを保存するindex、表のカラム名(項目名)を保存するcolumns、データの中身を持つvaluesの3つで構成されます。
デフォルトの設定では、CSVファイルの1行目がデータのカラム名(項目名)として保存されます。
{"code":"# \u30ab\u30e9\u30e0\u540d\u3092\u8868\u793a\nprint(df.columns)\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# Index(['sepal_length', 'sepal_width', 'petal_length', 'petal_width','species'],dtype='object')","filename":"","language":"python","id":0}
範囲を指定して抽出
df.values[]
ではスライス表記で、特定の範囲を指定してデータを抽出することが出来ます。範囲指定の記法は、後述する他のメソッドでも共通なので覚えておきましょう。
df.values[行の範囲指定, 列の範囲指定]
{"code":"# \u6700\u521d\u304b\u30893\u884c\u5206\u3092\u51fa\u529b\nprint(df.values[:3,:])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# [[5.1 3.5 1.4 0.2 'setosa']\n# [4.9 3.0 1.4 0.2 'setosa']\n# [4.7 3.2 1.3 0.2 'setosa']]\n\n# \u6700\u521d\u304b\u30893\u884c\u5206\u30012\u5217\u76ee\u304b\u30894\u5217\u76ee\u3092\u51fa\u529b\nprint(df.values[:3, 1:4])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# [[3.5 1.4 0.2]\n# [3.0 1.4 0.2]\n# [3.2 1.3 0.2]]","filename":"","language":"python","id":0}
同様の操作は、DataFrameのiloc[]
メソッドでも可能です。df.values[]
で抽出したデータはnumpy配列というデータ型に、iloc[]
メソッドの場合はDataFrameのままになります。
{"code":"# \u6700\u521d\u304b\u30893\u884c\u5206\u30012\u5217\u76ee\u304b\u30894\u5217\u76ee\u3092\u51fa\u529b\nprint(df.iloc[:3, 1:4])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# sepal_width petal_length petal_width\n# 0 3.5 1.4 0.2\n# 1 3.0 1.4 0.2\n# 2 3.2 1.3 0.2\n\n# \u30c7\u30fc\u30bf\u578b\u306e\u8868\u793a\nprint(type(df.values[:3, 1:4]))\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# class 'numpy.ndarray'\n# print(type(df.iloc[:3, 1:4]))\n# class 'pandas.core.frame.DataFrame'","filename":"","language":"python","id":0}
カラム名(項目名)を指定して抽出 | locメソッド
dataFrameのloc[]
メソッドを使用すると、範囲指定にカラム名(項目名)を使用できるため、コードが分かりやすくなる場合があります。
{"code":"# \u6700\u521d\u304b\u30893\u884c\u5206\u306e\u3001\u304c\u304f\u7247\u9577\u3055\u3068\u304c\u304f\u7247\u306e\u5e45\u3092\u51fa\u529b\nprint(df.loc[:3, \"sepal_length\":\"sepal_width\"])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# sepal_length sepal_width\n# 0 5.1 3.5\n# 1 4.9 3.0\n# 2 4.7 3.2\n# 3 4.6 3.1\n\n#\u5168\u3066\u306e\u30c7\u30fc\u30bf\u306e\u82b1\u3073\u3089\u306e\u9577\u3055\u3060\u3051\u3092\u62bd\u51fa\nprint(df.loc[:, \"petal_length\":\"petal_length\"])\n\n# \u30b3\u30f3\u30bd\u30fc\u30eb\u51fa\u529b\n# petal_length\n# 0 1.4\n# 1 1.4\n# 2 1.3\n# 3 1.5\n# 4 1.4\n# \u2026 \u2026\n# 145 5.2\n# 146 5.0\n# 147 5.2\n# 148 5.4\n# 149 5.1","filename":"","language":"python","id":0}
DataFrameで使用できるデータの抽出方法について、さらにテクニックを知りたい方は、以下の公式ドキュメントを参考にして下さい。
データの統計量を求める
pandasを推奨する大きな理由の1つとして、平均値・分散などの統計量を簡単に出せることがあります。
前述のirisデータセットを用いて、各品種それぞれの(それぞれ50個体分)花びらの長さの平均値を求めてみましょう。
{"code":"import pandas as pd\n\n#\u30ea\u30e2\u30fc\u30c8\u304b\u3089\u30b5\u30f3\u30d7\u30eb\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\ndf = pd.read_csv('https:\/\/raw.githubusercontent.com\/mwaskom\/seaborn-data\/master\/iris.csv')\n\n# 1\u54c1\u7a2e\u76ee(setosa)\u306e\u82b1\u3073\u3089\u306e\u9577\u3055\u306e\u5e73\u5747\u5024\nm1 = df.loc[0:50, \"petal_length\"].mean()\nprint(m1)\n# 1.5254901960784315\n\n# 2\u54c1\u7a2e\u76ee(versicolor)\u306e\u82b1\u3073\u3089\u306e\u9577\u3055\u306e\u5e73\u5747\u5024\nm2 = df.loc[50:100, \"petal_length\"].mean()\nprint(m2)\n# 4.294117647058823\n\n# 3\u54c1\u7a2e\u76ee(virginica)\u306e\u82b1\u3073\u3089\u306e\u9577\u3055\u306e\u5e73\u5747\u5024\nm3 = df.loc[100:150, \"petal_length\"].mean()\nprint(m3)\n# 5.5520000000000005","filename":"","language":"python","id":0}
品種ごとに花びらの長さに違いが見られました。
基本的な統計量に対応するメソッドを下に示します。
統計量 | メソッド |
---|
平均値 | .mean() |
分散 | .var() |
標準偏差 | .std() |
最大値 | .max() |
最小値 | .min() |
中央値 | .median() |
最頻値 | .mode() |
データの個数 | .count() |
実践的な統計量の求め方などについて、適宜下記の公式ドキュメントを参照して下さい。
以上で、PythonでのCSVファイルの扱いの説明を終わります。お疲れ様でした。