【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とします。

"宿泊日","宿泊日数","国籍","性別","年齢"
 "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"

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()で読み込んだオブジェクトを指定します。

import csv
with open('guests.csv', 'r') as f:
    reader = csv.reader(f)

    # 1行ずつ出力
    for row in reader:
        print(row)

# ['宿泊日','宿泊日数','国籍','性別','年齢']
# ['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形式のデータは、1行ずつ区切られ、Pythonのリストとして格納されます。デフォルトでは、データは全て文字列として認識されるので注意してください。

CSVファイルへの書き込み | csv.writer

CSVファイルへの書き込みは、csv.writer()で行います。また、書き込み時には、open() に書き込みモード’w”を指定します。

書き込みモードでは、CSVファイルを上書きしてしまうので注意してください。

import csv

with open('guests.csv', 'w') as f:
    writer = csv.writer(f)
    # CSVファイルに書き込み
    writer.writerow(['2022/12/10', '3', 'アメリカ', '女性', '25'])
    writer.writerow(['2022/12/10', '3', 'アメリカ', '男性', '30'])

# 書き込み後のCSVファイルの内容を確認
with open('guests.csv', 'r') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# ['2022/12/10', '3', 'アメリカ', '女性', '25']
# ['2022/12/10', '3', 'アメリカ', '男性', '30']

CSVファイルへの追記 | appendモード

元のCSVファイルを上書きせず、ファイルの後ろに追記したい時は、appendモードを使用します。open() の引数にappend(追記)を表す’a’を指定します。

# appendモード ' a '
with open('guests.csv', 'a') as f:
    writer = csv.writer(f)

    # 1行追記
    write.writerow(['2022/12/20', '2', '日本', '男性', '50'])

# 書き込み後のCSVファイルの内容を確認
with open('guests.csv', 'r') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# ['宿泊日','宿泊日数','国籍','性別','年齢']
# ['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']
# ['2022/12/20', '2', '日本', '男性', '50']

2次元リストで操作

CSVファイル全体を、1つの2次元リストにまとめてしまうことも可能です。こうすると、表の行・列を指定する感覚で範囲を指定出来るようになるので便利です。

import csv

with open('guests.csv', 'r') as f:
    reader = csv.reader(f)
    
    # CSV全体を2次元リストとして読み込み
    data = [row for row in reader]

    print(data)

# [['宿泊日','宿泊日数','国籍','性別','年齢'],['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']

スライス表記で、行を指定して切り出すことができます。表の行番号・列番号を指定する感覚です。

import csv

with open('guests.csv', 'r') as f:
    reader = csv.reader(f)
    data = [row for row in reader]
    
    # 1行目から3行名を表示
    print(data[0:3])
 
# [['宿泊日', '宿泊日数', '国籍', '性別', '年齢'], ['2022/05/15', '2', '日本', '男性', '28'], ['2022/06/20', '2', '日本', '男性', '51']]

zip() 関数を使用すれば、行と列をひっくり返す操作(転置を取る)ができます。特定の列だけを取り出すために利用できます。

下の例は、guest.csvの中の、宿泊者の国籍だけを抽出した例です。

import csv

with open('guests.csv', 'r') as f:
    reader = csv.reader(f)
    data = [row for row in reader]
    
    # zip関数で2次元リストの行と列を入れ替える
    data_t = list(zip(*data))

    # 元データの3列目(国籍)を出力
    print(data_t[2])

# ('国籍', '日本', '日本', 'イギリス', 'イギリス', '日本')
宿泊日宿泊日数国籍性別年齢
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を指定すると、オンラインから直接データを取得可能です。

import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')

# データの先頭をいくつか表示
print(df.head())

# sepal_length  sepal_width  petal_length  petal_width species
# 0           5.1          3.5           1.4          0.2  setosa
# 1           4.9          3.0           1.4          0.2  setosa
# 2           4.7          3.2           1.3          0.2  setosa
# 3           4.6          3.1           1.5          0.2  setosa
# 4           5.0          3.6           1.4          0.2  setosa

DataFrameは、以下のような構造で、各行のIDを保存するindex、表のカラム名(項目名)を保存するcolumns、データの中身を持つvaluesの3つで構成されます。

デフォルトの設定では、CSVファイルの1行目がデータのカラム名(項目名)として保存されます。

# カラム名を表示
print(df.columns)

# Index(['sepal_length', 'sepal_width', 'petal_length', 'petal_width','species'],dtype='object')

範囲を指定して抽出

df.values[]ではスライス表記で、特定の範囲を指定してデータを抽出することが出来ます。範囲指定の記法は、後述する他のメソッドでも共通なので覚えておきましょう。

df.values[行の範囲指定, 列の範囲指定

行範囲、列範囲をそれぞれスライス表記で指定する。

# 最初から3行分を出力
print(df.values[:3,:])

# [[5.1 3.5 1.4 0.2 'setosa']
# [4.9 3.0 1.4 0.2 'setosa']
# [4.7 3.2 1.3 0.2 'setosa']]

# 最初から3行分、2列目から4列目を出力
print(df.values[:3, 1:4])

# [[3.5 1.4 0.2]
# [3.0 1.4 0.2]
# [3.2 1.3 0.2]]

同様の操作は、DataFrameのiloc[]メソッドでも可能です。df.values[]で抽出したデータはnumpy配列というデータ型に、iloc[]メソッドの場合はDataFrameのままになります。

# 最初から3行分、2列目から4列目を出力
print(df.iloc[:3, 1:4])

# sepal_width  petal_length  petal_width
# 0          3.5           1.4          0.2
# 1          3.0           1.4          0.2
# 2          3.2           1.3          0.2

# データ型の表示
print(type(df.values[:3, 1:4]))

# class 'numpy.ndarray'
# print(type(df.iloc[:3, 1:4]))
# class 'pandas.core.frame.DataFrame'

カラム名(項目名)を指定して抽出 | locメソッド

dataFrameのloc[]メソッドを使用すると、範囲指定にカラム名(項目名)を使用できるため、コードが分かりやすくなる場合があります。

# 最初から3行分の、がく片長さとがく片の幅を出力
print(df.loc[:3, "sepal_length":"sepal_width"])

# sepal_length  sepal_width
# 0           5.1          3.5
# 1           4.9          3.0
# 2           4.7          3.2
# 3           4.6          3.1

#全てのデータの花びらの長さだけを抽出
print(df.loc[:, "petal_length":"petal_length"])

# petal_length
# 0             1.4
# 1             1.4
# 2             1.3
# 3             1.5
# 4             1.4
# …            …
# 145           5.2
# 146           5.0
# 147           5.2
# 148           5.4
# 149           5.1

DataFrameで使用できるデータの抽出方法について、さらにテクニックを知りたい方は、以下の公式ドキュメントを参考にして下さい。

データの統計量を求める

pandasを推奨する大きな理由の1つとして、平均値・分散などの統計量を簡単に出せることがあります。

前述のirisデータセットを用いて、各品種それぞれの(それぞれ50個体分)花びらの長さの平均値を求めてみましょう。

import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')

# 1品種目(setosa)の花びらの長さの平均値
m1 = df.loc[0:50, "petal_length"].mean()
print(m1)

# 1.5254901960784315

# 2品種目(versicolor)の花びらの長さの平均値
m2 = df.loc[50:100, "petal_length"].mean()
print(m2)

# 4.294117647058823

# 3品種目(virginica)の花びらの長さの平均値
m3 = df.loc[100:150, "petal_length"].mean()
print(m3)

# 5.5520000000000005

品種ごとに花びらの長さに違いが見られました。

基本的な統計量に対応するメソッドを下に示します。

統計量メソッド
平均値.mean()
分散.var()
標準偏差.std()
最大値.max()
最小値.min()
中央値.median()
最頻値.mode()
データの個数.count()

実践的な統計量の求め方などについて、適宜下記の公式ドキュメントを参照して下さい。

以上で、PythonでのCSVファイルの扱いの説明を終わります。お疲れ様でした。

コメントを残す

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

CAPTCHA


error: Content is protected !!