pandasが提供するread_csv関数を使って、CSVファイルなどからデータを読み込む方法を紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
* 本稿は2021年9月7日に公開された記事をPython 3.12.0で動作確認したものです(確認日:2023年12月1日)。
import pandas as pd
from pathlib import Path
filepath = 'test0.csv'
print(Path(filepath).read_text())
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
df = pd.read_csv(filepath)
print(df)
# 0.0 1.1 2.2
#0 3.3 4.4 5.5
#1 6.6 7.7 8.8
# ヘッダー行がないことを指定
df = pd.read_csv(filepath, header=None)
print(df)
# 0 1 2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
# 列名を指定
names = ['col0', 'col1', 'col2']
df = pd.read_csv(filepath, names=names)
print(df)
# col0 col1 col2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
# ヘッダー行がある場合
filepath = 'test1.csv'
print(Path(filepath).read_text())
#col0,col1,col2
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
df = pd.read_csv(filepath)
print(df)
# col0 col1 col2 # ヘッダー行から列名を推測してくれる
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
# 列名をヘッダー行から推測せずに、明示的に指定する
names = ['foo', 'bar', 'baz']
df = pd.read_csv(filepath, names=names, header=0)
print(df)
# foo bar baz
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
# 区切り文字の指定
filepath = 'test2.csv'
print(Path(filepath).read_text())
#col0 col1 col2 # 空白文字で区切っている
#0.0 1.1 2.2
#3.3 4.4 5.5
#6.6 7.7 8.8
df = pd.read_csv(filepath, sep=' ')
print(df)
# col0 col1 col2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
# 読み込む列の指定
filepath = 'test1.csv'
df = pd.read_csv(filepath, usecols=[0, 2])
print(df)
# col0 col2
#0 0.0 2.2
#1 3.3 5.5
#2 6.6 8.8
df = pd.read_csv(filepath, usecols=['col0', 'col2'])
print(df) # 出力は省略
df = pd.read_csv(filepath, usecols=lambda x: x in ['col0', 'col2'])
print(df) # 出力は省略
# 行ラベル(インデックス)となる列の指定
filepath = 'test3.csv'
print(Path(filepath).read_text())
#IDX,col1,col2,col3
#row0,0.0,1.1,2.2
#row1,3.3,4.4,5.5
#row2,6.6,7.7,8.8
df = pd.read_csv(filepath, index_col=0)
print(df)
# col1 col2 col3
#IDX
#row0 0.0 1.1 2.2
#row1 3.3 4.4 5.5
#row2 6.6 7.7 8.8
df = pd.read_csv(filepath, index_col='IDX')
print(df) # 出力は省略
# データ型の指定
filepath = 'test4.csv'
print(Path(filepath).read_text())
#area,tel,value
#tokyo,0312345678,1.0
#kanagawa,045678901,2.0
#chiba,043210987,3.0
df = pd.read_csv(filepath)
print(df)
# area tel value # 電話番号が整数値になっている
#0 tokyo 312345678 1.0
#1 kanagawa 45678901 2.0
#2 chiba 43210987 3.0
df = pd.read_csv(filepath, dtype=str) # 全てのデータの型をstrに
print(df)
# area tel value
#0 tokyo 0312345678 1.0
#1 kanagawa 045678901 2.0
#2 chiba 043210987 3.0
df = pd.read_csv(filepath, dtype={0: str, 1: str, 2: float})
print(df) # 出力は省略
# 日付のパース
filepath = 'test5.csv'
print(Path(filepath).read_text())
#date,value0,value1
#2021/09/07,1.0,2.0
#2021/09/08,3.0,4.0
#2021/09/09,5.0,5.0
df = pd.read_csv(filepath, parse_dates=True, index_col=0)
print(df)
# value0 value1
#date
#2021-09-07 1.0 2.0
#2021-09-08 3.0 4.0
#2021-09-09 5.0 5.0
df = pd.read_csv(filepath, parse_dates=[0])
print(df)
# date value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 4.0
#2 2021-09-09 5.0 5.0
df = pd.read_csv(filepath, parse_dates=['date'])
print(df) # 出力は省略
filepath = 'test6.csv'
print(Path(filepath).read_text())
#year,month,day,value0,value1
#2021,9,7,1.0,2.0
#2021,9,8,3.0,5.0
#2021,9,9,4.0,6.0
dates = [['year', 'month', 'day']]
df = pd.read_csv(filepath, parse_dates=dates)
print(df)
# year_month_day value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 5.0
#2 2021-09-09 4.0 6.0
dates = {'date': ['year', 'month', 'day']}
df = pd.read_csv(filepath, parse_dates=dates)
print(df)
# date value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 5.0
#2 2021-09-09 4.0 6.0
# 欠損値の扱い
filepath = 'test7.csv'
print(Path(filepath).read_text())
# col0,col1,col2
# ,nan,1.0
# 2.0,N/A,null
# NaN,3.0,--
df = pd.read_csv(filepath)
print(df)
# col0 col1 col2
#0 NaN NaN 1.0
#1 2.0 NaN NaN
#2 NaN 3.0 -- # 「--」という文字列は欠損値としては扱われていない
df = pd.read_csv(filepath, na_values=['--'])
print(df)
# col0 col1 col2 # デフォルトの欠損値とna_valuesに指定した値が欠損値
#0 NaN NaN 1.0
#1 2.0 NaN NaN
#2 NaN 3.0 NaN
df = pd.read_csv(filepath, keep_default_na=False, na_values=['--'])
print(df)
# col0 col1 col2 # na_valuesに指定した値のみが欠損値として扱われる
#0 nan 1.0
#1 2.0 N/A null
#2 NaN 3.0 NaN
pandasのread_csv関数を使うと、CSVファイルの内容をpandas.DataFrameオブジェクトに読み込める。読み込んだ内容はpandasが提供するさまざまな機能を使って、参照したり加工したりできる。
read_csv関数の構文を以下に示す。なお、記述しているパラメーターは本稿で取り上げるものだけだ。詳細についてはpandasのドキュメント「read_csv関数」を参照されたい
pandas.read_csv(filepath, sep, header, names, index_col, usecols, dtype,
keep_default_na, na_values, parse_date)
これらのパラメーターについて簡単にまとめる。
最も基本的な使い方を以下に示す。なお、以下では読み込むCSVファイルの内容を示した後に、read_csv関数を呼び出して、読み込んだ値を表示することにする。
import pandas as pd
from pathlib import Path
filepath = 'test0.csv'
print(Path(filepath).read_text())
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
df = pd.read_csv(filepath)
print(df)
# 0.0 1.1 2.2
#0 3.3 4.4 5.5
#1 6.6 7.7 8.8
この例では、read_csv関数を使って3行3列のCSVファイルから読み込みを行っている。ただし、先頭行(0.0,1.1,2.2)がヘッダー行と見なされてしまっている(その内容から列名が「0.0」「1.1」「2.2」と自動的に推測されてもいる)。ヘッダー行がないことを知らせるには、次のようにする。
df = pd.read_csv(filepath, header=None)
print(df)
# 0 1 2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
header=Noneを指定したことで、CSVファイルの先頭行がヘッダー行として扱われなくなり、ヘッダー行から推測されていた列名の代わりに「0」「1」「2」という列名が付けられている。
列名を明示的に指定するには、namesパラメーターにそれらを指定する。以下に例を示す。
names = ['col0', 'col1', 'col2']
df = pd.read_csv(filepath, names=names)
print(df)
# col0 col1 col2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
一方、CSVファイルにヘッダー行があるときには、既に見たように自動的にヘッダー行が認識され、列名が推測される。
filepath = 'test1.csv'
print(Path(filepath).read_text())
#col0,col1,col2
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
df = pd.read_csv(filepath)
print(df)
# col0 col1 col2 # ヘッダー行から列名を推測してくれる
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
列名をヘッダー行の内容と異なるものにするのであれば、以下のようにヘッダー行を明示的に指定するとともに、namesパラメーターに列名を与える。
names = ['foo', 'bar', 'baz']
df = pd.read_csv(filepath, names=names, header=0)
print(df)
# foo bar baz
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
この例では、「header=0」としてヘッダー行が先頭行であることを指定し、同時にnamesパラメーターに['foo', 'bar', 'baz']というリストを与えているので、列名がヘッダー行とは異なるものになっている。
ヘッダー行が複数ある場合には、header=[0, 1]などと指定することも可能だ(この場合は、先頭行とその次の行の値を要素とするタプルが列にアクセスするためのインデックスとなる)。
区切り文字を指定するには、sepパラメーターまたはdelimiterパラメーターにそれを指定する。以下は、空白文字を区切り文字とするファイルから読み込みを行う例だ。
filepath = 'test2.csv'
print(Path(filepath).read_text())
#col0 col1 col2 # 空白文字で区切っている
#0.0 1.1 2.2
#3.3 4.4 5.5
#6.6 7.7 8.8
df = pd.read_csv(filepath, sep=' ')
print(df)
# col0 col1 col2
#0 0.0 1.1 2.2
#1 3.3 4.4 5.5
#2 6.6 7.7 8.8
「sep=None」「engine='python'」を指定することで、区切り文字の自動推測が可能になる。
df = pd.read_csv(filepath, sep=None, engine='python')
元のCSVファイルから特定の列の内容だけを読み込みたいときには、usecolsパラメーターに読み込みたい列を指定する。以下に例を示す。
filepath = 'test1.csv'
df = pd.read_csv(filepath, usecols=[0, 2])
print(df)
# col0 col2
#0 0.0 2.2
#1 3.3 5.5
#2 6.6 8.8
この例では、第0列と第2列の内容だけを読み込むように指定している。列名をusecolsパラメーターに指定してもよい。
df = pd.read_csv(filepath, usecols=['col0', 'col2'])
print(df) # 出力は省略
usecolsパラメーターにラムダ式を渡すこともできる。ラムダ式を渡した場合、そのラムダ式には列名が渡され、それを使ったラムダ式の評価結果がTrueとなる列のみが読み込まれる。
df = pd.read_csv(filepath, usecols=lambda x: x in ['col0', 'col2'])
print(df) # 出力は省略
この例では、列名は'col0'、'col1'、'col2'の3つであり、それらがラムダ式に渡される。ラムダ式では「列名 in ['col0', 'col2']」が評価されるので、第1列('col1')以外の2列が読み込まれる。
行ラベル(行にアクセスするためのインデックス)として使用する列を指定するには、index_colパラメーターにその値(整数、列名)を指定する。以下に例を示す。
filepath = 'test3.csv'
print(Path(filepath).read_text())
#IDX,col1,col2,col3
#row0,0.0,1.1,2.2
#row1,3.3,4.4,5.5
#row2,6.6,7.7,8.8
df = pd.read_csv(filepath, index_col=0)
print(df)
# col1 col2 col3
#IDX
#row0 0.0 1.1 2.2
#row1 3.3 4.4 5.5
#row2 6.6 7.7 8.8
あるいは列名を指定してもよい。
df = pd.read_csv(filepath, index_col='IDX')
print(df) # 出力は省略
CSVファイルに格納されているデータ全体、または特定の列のデータ型を指定するには、dtypeパラメーターを使用する。
例えば、以下のようなCSVファイルがあったとする。
filepath = 'test4.csv'
print(Path(filepath).read_text())
#area,tel,value
#tokyo,0312345678,1.0
#kanagawa,045678901,2.0
#chiba,043210987,3.0
第0列は地域、第1列は電話番号、第2列は何らかの値を示すデータとなっている。これを、何も指定せずにread_csv関数で読み込むと次のようになる。
df = pd.read_csv(filepath)
print(df)
# area tel value
#0 tokyo 312345678 1.0
#1 kanagawa 45678901 2.0
#2 chiba 43210987 3.0
df.info()
#<class 'pandas.core.frame.DataFrame'>
#RangeIndex: 3 entries, 0 to 2
#Data columns (total 3 columns):
# # Column Non-Null Count Dtype
#--- ------ -------------- -----
# 0 area 3 non-null object
# 1 tel 3 non-null int64 # 電話番号のはずが整数として扱われている
# 2 value 3 non-null float64
ご覧の通り、電話番号としていたはずが整数値として扱われている(先頭の「0」がなくなっていることからも分かるはずだ)。これを回避するには、幾つかの方法がある。例えば、全ての値を文字列としてしまうことが考えられる。
df = pd.read_csv(filepath, dtype=str) # 全てのデータの型をstrに
print(df)
# area tel value
#0 tokyo 0312345678 1.0
#1 kanagawa 045678901 2.0
#2 chiba 043210987 3.0
「dtype=str」とすることで、全てのデータを文字列化したので、先頭の「0」が削除されなくなった。とはいえ、列ごとにデータ型を指定できた方がよいだろう。その場合は、以下のように、各列のデータ型を指定できる。
df = pd.read_csv(filepath, dtype={0: str, 1: str, 2: float})
print(df) # 出力は省略
ここでは、全ての列のデータ型を指定しているが、特定の列だけを指定してもよい。
CSVファイルに日付データが含まれている場合には、parse_datesパラメーターを指定することで、日付型のデータへとパースできる。
例えば、以下のようなデータがあったとする。
filepath = 'test5.csv'
print(Path(filepath).read_text())
#date,value0,value1
#2021/09/07,1.0,2.0
#2021/09/08,3.0,4.0
#2021/09/09,5.0,5.0
parse_datesパラメーターにはブール値、整数リスト、列名を要素とするリスト、辞書を渡せる。最初にこのパラメーターにTrueを渡す例を示す。
df = pd.read_csv(filepath, parse_dates=True, index_col=0)
print(df)
# value0 value1
#date
#2021-09-07 1.0 2.0
#2021-09-08 3.0 4.0
#2021-09-09 5.0 5.0
「parse_dates=True」を指定すると、行インデックスとなる列の値が日付型に変換される(そのため、ここでは「index_col=0」を指定している)。
あるいは、変換したい列を表す整数値、または文字列(列名)を要素とするリストを渡してもよい。以下に例を示す(変換したい列が1つだけでもリストで渡す必要がある)。
df = pd.read_csv(filepath, parse_dates=[0])
print(df)
# date value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 4.0
#2 2021-09-09 5.0 5.0
df = pd.read_csv(filepath, parse_dates=['date'])
print(df) # 出力は省略
1つ目の例では「parse_dates=[0]」として、2つ目の例では「parse_dates=['date']」として日付を含んだ列を指定している。
日付を表すデータが複数の列に分かれていることもあるかもしれない。その場合は、parse_datesパラメーターに一つの日付を表す列をリストにまとめ、それをさらにリストの要素として渡すとよい。
例えば、以下のようにyear列、month列、date列に分けて、日付のデータが格納されていたとする。
filepath = 'test6.csv'
print(Path(filepath).read_text())
#year,month,day,value0,value1
#2021,9,7,1.0,2.0
#2021,9,8,3.0,5.0
#2021,9,9,4.0,6.0
このときに、それらをまとめて日付型に変換するには以下のようにする。
dates = [['year', 'month', 'day']]
df = pd.read_csv(filepath, parse_dates=dates)
print(df)
# year_month_day value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 5.0
#2 2021-09-09 4.0 6.0
列名が「year_month_day」と3つの列をつなげたものになっている点に注意しよう。辞書を渡すと、次のように列名を指定できる。
dates = {'date': ['year', 'month', 'day']}
df = pd.read_csv(filepath, parse_dates=dates)
print(df)
# date value0 value1
#0 2021-09-07 1.0 2.0
#1 2021-09-08 3.0 5.0
#2 2021-09-09 4.0 6.0
この例では日付は1つだけなので、辞書を使う意味が分からないかもしれないが、開始日と終了日など複数の日付が含まれている場合に、「dates = {'start_date': [……], 'end_date': [……]}」などと書けることは覚えておこう。
CSVファイルには空文字列や「nan」「NA」などとして値がないことを示すデータが含まれているかもしれない。read_csv関数はデフォルトで、そうした値をNaNとして扱ってくれる(NaN値については「CSVファイルから読み込みを行うには(NumPy編)」のコラムを参照)。
例えば、次のようなCSVファイルがあったとする。
filepath = 'test7.csv'
print(Path(filepath).read_text())
# col0,col1,col2
# ,nan,1.0
# 2.0,N/A,null
# NaN,3.0,--
CSVファイルには「nan」「N/A」などが欠損値があるフィールドとして記述されている。これをread_csv関数で読み込むと次のようになる。
df = pd.read_csv(filepath)
print(df)
# col0 col1 col2
#0 NaN NaN 1.0
#1 2.0 NaN NaN
#2 NaN 3.0 -- # 「--」という文字列は欠損値としては扱われていない
多くの値はNaNに変換されたが、CSVファイルにあった「--」というフィールドはそうはなっていない点に注意。これも欠損値として扱いたければ、na_valuesパラメーターにそれを指定すればよい。
df = pd.read_csv(filepath, na_values=['--'])
print(df)
# col0 col1 col2 # デフォルトの欠損値とna_valuesに指定した値が欠損値
#0 NaN NaN 1.0
#1 2.0 NaN NaN
#2 NaN 3.0 NaN
pandasがデフォルトで欠損値として扱っている文字列を無効化するには、keep_default_naパラメーターにFalseを指定する(以下の例では左下にも「NaN」があるが、これは文字列である)。
df = pd.read_csv(filepath, keep_default_na=False, na_values=['--'])
print(df)
# col0 col1 col2 # na_valuesに指定した値のみが欠損値として扱われる
#0 nan 1.0
#1 2.0 N/A null
#2 NaN 3.0 NaN
Copyright© Digital Advantage Corp. All Rights Reserved.