【Python】CSVファイルの読み書きと処理のススメ

本記事では、PythonでのCSVファイルの扱い方を解説します。機械学習用データや公共機関が提供する統計データはCSVで提供されていることが多いので、扱いに慣れておきましょう。

推奨はpandasライブラリを使用する方法なので、迷ったらこの方法を参考にしてください。

なお、本記事で解説しているソースコードは、以下のGoogle Colabノートブックで実行して動作を確認することができます。ぜひご活用下さい。

CSVファイルの基本形式 | 初心者向け

CSVは、Comma Separated Valuesの略で、カンマで区切られた値という意味になります。記法は簡単で、名前の通りデータをカンマで区切って並べていく形式です。

例えば、以下の宿泊記録の表データをCSV形式で格納することを考えます。

宿泊日宿泊日数国籍性別年齢
2022/05/152日本男性28
2022/06/202日本男性51
2022/06/202イギリス女性32
2022/07/033イギリス男性35
2022/08/104日本女性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/152日本男性28
2022/06/202日本男性51
2022/06/202イギリス女性32
2022/07/033イギリス男性35
2022/08/104日本女性30

Pandasライブラリを使う方法|推奨

“Vanilla” Pythonでも手軽にCSVが扱えますが、データ分析を行ったり複雑な操作を行ったりする場合にはpandasライブラリの使用を推奨します。

DataFrameにCSVを格納

pandasでは、DataFrameという形式でCSVを扱います。試しに、機械学習入門者用のiris(アヤメ)データセットを読み込んで見ましょう。

irisデータセットは、3品種のアヤメの、がく片の長さ・幅、花びらの長さ・幅 の 4つの測定値をまとめたデータセット。機械学習を使用して分類問題を解く練習用に使用される。

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ファイルの扱いの説明を終わります。お疲れ様でした。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


error: Content is protected !!