検索
連載

[解決!Python]CSVファイルから読み込みを行うには(pandas編)解決!Python

pandasが提供するread_csv関数を使って、CSVファイルなどからデータを読み込む方法を紹介する。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「解決!Python」のインデックス

連載目次

* 本稿は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)


 これらのパラメーターについて簡単にまとめる。

  • filepath:読み込むCSVファイルの名前。必須
  • sep:区切り文字の指定(delimiterパラメーターも使える。ただし、両者を同時に指定することはできない)。省略可。省略時はカンマ「,」が指定されたものとして扱われる
  • header:ヘッダー行の指定。省略可。省略時は読み込んだ内容からヘッダー行と列名を推測する
  • names:列名の指定。省略可
  • index_col:行ラベル(行にアクセスするためのインデックス)となる列の指定。省略可。省略時は各行には整数値でアクセスする
  • usecols:読み込む列の指定。省略可。省略時は全ての列の内容が読み込まれる
  • dtype:データ全体または特定列のデータのデータ型を指定。省略可。省略時はデータの内容に応じて自動的にデータ型が推測される
  • keep_default_na:na_valuesパラメーターに欠損値と見なされる値が指定された場合に、デフォルトで欠損値として見なしている値を保持するかどうかの指定。省略可。省略時はTrueが指定されたものとして見なされる(デフォルトの欠損値を保持する)
  • na_values:欠損値として見なされる値の追加指定。省略可
  • parse_dates:日付としてパースするかどうかの指定

 最も基本的な使い方を以下に示す。なお、以下では読み込む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


「解決!Python」のインデックス

解決!Python

Copyright© Digital Advantage Corp. All Rights Reserved.

[an error occurred while processing this directive]