【Python】PandasでCSVファイルを読み込み/書き出しする実践テクニック集

この記事は、PythonのPandasモジュールを使用して、CSVファイルの読み書きをする実践テクニックをまとめたものになっています。

PythonでCSVファイルを処理する方法はPandas以外にもたくさんありますが、私はPandas一択派です。

  • Pandasはデータの加工・分析に特化したモジュール
  • 何千何万行に渡るデータ量でも高速に処理できる
  • 複数のCSVをまとめて処理できる
  • NumPyやSciPy、Matplotlibモジュールと親和性がある

CSVを読み込む

pandasモジュールのread_csvメソッドを使用すると、CSVファイルのデータをpandas.DataFrameとして変数に格納することができます。

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html

CSVファイルを読み込む
import pandas as pd
df = pd.read_csv('{{ 読み込むCSVファイルのパス }}')

pandas.read_csvでCSVファイルを読み込む実践テクニック集は、次の通りです。

  • 日本語が含まれる場合
  • 郵便番号や電話番号が含まれる場合
  • ヘッダーが無い場合
  • インデックスを設定したい場合
  • 任意の行数スキップして読み込みたい場合
  • 指定した列だけ読み込みたい場合
  • 複数のCSVを1つのDataFrameに読み込みたい場合

それぞれ見てみましょう。

日本語が含まれる場合

日本語が含まれるCSVファイル読み込みたいときに

sample.csv
"col1","col2","col3"
"a","あいうえお","1"
"b","かきくけこ","2"
"c","さしすせそ","3"

普通にread_csvメソッドを使うと、UnicodeDecodeErrorが発生します。

df = pd.read_csv('sample.csv')
print(df)
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte

そこで、encodingパラメータに日本語が扱える文字コード(shift-jiscp932など)を指定することで、エラーを回避することができます。

実際の文字コードには、shift-jisの拡張であるcp932を指定すれば間違いないでしょう。

df = pd.read_csv('sample.csv', encoding='cp932')
print(df)
#    col1   col2    col3
# 0    a  あいうえお    1
# 1    b  かきくけこ    2
# 2    c  さしすせそ    3

郵便番号や電話番号が含まれる場合

郵便番号や電話番号が含まれるCSVファイル読み込みたいときに

sample.csv
"col1","col2","col3"
"1001","8100001","08011112222"
"1002","1008602","07012345678"
"1003","0010010","09087654321"

普通にread_csvメソッドを使うと、住所や番号によっては先頭の0が抜けます

df = pd.read_csv('sample.csv')
print(df)
#    col1     col2        col3
# 0  1001  8100001  8011112222
# 1  1002  1008602  7012345678
# 2  1003    10010  9087654321

0落ちを回避するためには、CSVファイルを読み込むときに各カラムのデータの型を文字列(object)に指定します。

  • 全ての列を文字列にする場合:dtype=object
  • 指定した列だけ文字列にする場合:dtype={'col2': object, 'col3': object}

おまけでdtypeパラメータに設定できる型を下のテーブルにまとめました

Pandas dtypeとPython typeの比較
Pandas dtype Python type
object str
int64 int
float64 float
bool bool
datetime64 NA
timedelta[ns] NA
category NA

全ての列を文字列にして取り込む

df = pd.read_csv('sample.csv', dtype=object)
print(df)
#    col1     col2         col3 
# 0  1001  8100001  08011112222
# 1  1002  1008602  07012345678
# 2  1003  0010010  09087654321
print(df.dtypes)
# col1    object
# col2    object
# col3    object
# dtype: object

指定した列だけ文字列にして取り込む

df = pd.read_csv('sample.csv', dtype={'col2': object, 'col3': object})
print(df)
#    col1     col2         col3 
# 0  1001  8100001  08011112222
# 1  1002  1008602  07012345678
# 2  1003  0010010  09087654321
print(df.dtypes)
# col1     int64
# col2    object
# col3    object
# dtype: object

ヘッダーが無い場合

CSVファイルには、次のようにヘッダーが存在せず、データしか入っていないものもあります。

sample.csv
"1001","a","A"
"1002","b","B"
"1003","c","C"

そのままread_csvメソッドで読み込むと一行目がヘッダーとして読み込まれます。

df = pd.read_csv('sample.csv')
print(df)
#    1001  a  A
# 0  1002  b  B
# 1  1003  c  C

一行目をヘッダーとして読み込ませたくない場合は、namesパラメータに列名のリストを指定します。

df = pd.read_csv('sample.csv', names=['col1', 'col2', 'col3'])
print(df)
#    col1 col2 col3
# 0  1001    a    A
# 1  1002    b    B
# 2  1003    c    C

インデックスを設定したい場合

sample.csv
"col1","col2","col3"
"1001","a","A"
"1002","b","B"
"1003","c","C"

普通にread_csvメソッドで読み込むと、インデックスは0からの連番になります。

df = pd.read_csv('sample.csv')
print(df)
#    col1 col2 col3
# 0  1001    a    A
# 1  1002    b    B
# 2  1003    c    C

読み込むときに指定した列をDataFrameのインデックスに設定したい場合、index_colパラメータに列番号または列名のリストを指定します。

列番号のリストでインデックスを設定する

df = pd.read_csv('sample.csv', index_col=[0])
print(df)
#      col2 col3
# col1          
# 1001    a    A
# 1002    b    B
# 1003    c    C

列名のリストでインデックスを設定する

df = pd.read_csv('sample.csv', index_col=['col1', 'col2'])
print(df)
#           col3
# col1 col2     
# 1001 a       A
# 1002 b       B
# 1003 c       C

任意の行数スキップして読み込みたい場合

  • 先頭から任意の行だけスキップして読み込みたい:skiprows
  • 指定した行番号をスキップして読み込みたい:skiprows
  • 末尾から任意の行だけスキップして読み込みたい:skipfooter
  • 先頭から数行だけ読み込みたい:nrows
sample.csv
"col1","col2"
"1001","a"
"1002","b"
"1003","c"
"1004","d"
"1005","e"
"1006","f"
"1007","g"
"1008","h"
"1009","i"
"1010","j"

先頭から任意の行だけスキップして読み込みたい場合

先頭から任意の行だけスキップして読み込みたい場合は、skiprowsパラメータに整数値を指定します。

df = pd.read_csv('sample.csv', skiprows=3)
print(df)
#    1003  c
# 0  1004  d
# 1  1005  e
# 2  1006  f
# 3  1007  g
# 4  1008  h
# 5  1009  i
# 6  1010  j

指定した行番号をスキップして読み込みたい場合

指定した行番号をスキップして読み込みたい場合は、skiprowsパラメータに配列を指定します。

df = pd.read_csv('sample.csv', skiprows=[0, 2, 3, 9])
print(df)
#    1001  a
# 0  1004  d
# 1  1005  e
# 2  1006  f
# 3  1007  g
# 4  1008  h
# 5  1010  j

末尾から任意の行だけスキップして読み込みたい場合

末尾から任意の行だけスキップして読み込みたい場合は、skipfooterパラメータに整数値を指定します。

環境によって、以下のような警告が表示されるので、engine='python'も一緒に指定しましょう。

ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support skipfooter; you can avoid this warning by specifying engine='python'.
df = pd.read_csv('sample.csv', skipfooter=5, engine='python')
print(df)
#    col1 col2
# 0  1001    a
# 1  1002    b
# 2  1003    c
# 3  1004    d
# 4  1005    e

先頭から数行だけ読み込みたい場合

先頭から数行だけ読み込みたい場合は、nrowsパラメータに整数値(先頭行はカウントしない)を指定します。

サイズが大きいファイルの中身をちら見するときに使うことが多いです。

ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support skipfooter; you can avoid this warning by specifying engine='python'.
df = pd.read_csv('sample.csv', nrows=5)
print(df)
#    col1 col2
# 0  1001    a
# 1  1002    b
# 2  1003    c
# 3  1004    d
# 4  1005    e

指定した列だけ読み込みたい場合

読み込みたい列が決まっている場合、usecolsパラメータを使用します。

usecolsパラメータには読み込む列番号または列名をリストで指定します。1列だけ読み込む場合もリストを使います。

sample.csv
"col1","col2","col3","col4"
"1001","a","A","aA"
"1002","b","B","bB"
"1003","c","C","cC"

列番号のリストで読み込む列を指定する

df = pd.read_csv('sample.csv', usecols=[0, 1])
print(df)
#    col1 col2
# 0  1001    a
# 1  1002    b
# 2  1003    c

列名のリストで読み込む列を指定する

df = pd.read_csv('sample.csv', usecols=["col2", "col3"])
print(df)
#   col2 col3
# 0    a    A
# 1    b    B
# 2    c    C

複数のCSVを1つのDataFrameに読み込みたい場合

複数のCSVファイルを1つのDataFrameにガッチャンコしたいときは

sample1.csv
"col1","col2"
"1001","あいうえお"
"1002","かきくけこ"
"1003","さしすせそ"
sample2.csv
"col1","col2"
"1004","たちつてと"
"1005","なにぬねの"
"1006","はひふへほ"
sample3.csv
"col1","col2"
"1007","まみむめも"
"1008","やゆよ"
"1009","らりるれろ"

forループと標準モジュールのglopを使用し、指定したディレクトリからCSVファイルをまとめて順番にインポートとして、pandas.concatメソッドで、1つのデータフレームにマージします。

from glob import glob

csv_files_dir = glob('sample*.csv') # 『*』がワールドカード
marge_csv = []
for f in csv_files_dir:
	marge_csv.append(pd.read_csv(f, encoding='cp932'))

# 1つのデータフレームに結合する
# ignore_index=Trueでインデックスをリセット
df = pd.concat(marge_csv, ignore_index=True)
print(df)
#    col1   col2
# 0  1004  たちつてと
# 1  1005  なにぬねの
# 2  1006  はひふへほ
# 3  1007  まみむめも
# 4  1008    やゆよ
# 5  1009  らりるれろ
# 6  1001  あいうえお
# 7  1002  かきくけこ
# 8  1003  さしすせそ

ちゃんと結合されていますが、読み込まれるCSVファイルの順番がめちゃくちゃですね。

それもそのはず。変数csv_files_dirに格納する前のglob('sample*.csv')部分を確認します。

print(glob('sample*.csv'))
# ['sample2.csv', 'sample3.csv', 'sample1.csv']

ファイル名で昇順ソートして読み込ませたい場合、組み込み関数のsortedメソッドを使用します。

print(sorted(glob('sample*.csv')))
# ['sample1.csv', 'sample2.csv', 'sample3.csv']

ファイル名で降順ソートして読み込ませたい場合、sortedメソッドのreverseパラメータの値をTrueにします。

print(sorted(glob('sample*.csv'), reverse=True))
# ['sample3.csv', 'sample2.csv', 'sample1.csv']

余談ですが、リスト内包表記を使えば、可読性は置いておいて読み込む部分がワンライナーで書けるので、知っていると便利です。

リスト内包表記を使ったほうが処理が早くなるみたいなので、積極的に使ってみてはどうでしょうか。

BEFORE
marge_csv = []
for f in csv_files_dir:
	marge_csv.append(pd.read_csv(f, encoding='cp932'))
AFTER
marge_csv = [pd.read_csv(f, encoding='cp932') for f in csv_files_dir]

CSVを書き出す

pandas.DataFrameto_csvメソッドを使用すると、DataFrameをCSVファイルとして書き出すことができます。

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html

DataFrameをCSVとして書き出す
df.to_csv('{{ 保存先のCSVファイルのパス }}')

pandas.DataFrame.to_csvでCSVファイルを作成する実践テクニック集は、次の通りです。

  • DataFrameのインデックスを無視して書き出したい場合
  • 日本語が含まれる場合
  • 任意の行数ごとに分割して書き出す
  • 書き出すCSVのデータをダブルクオートしたい場合

それぞれ見てみましょう。

DataFrameのインデックスを無視して書き出したい場合

print(df)
#    col1 col2 col3
# 0  1001    a    A
# 1  1002    b    B
# 2  1003    c    C

DataFrameのインデックスを無視して書き出したい場合は、indexパラメータの値をFalseに設定します。

import csv
df.to_csv('sample.csv', index=False)
sample.csv
col1,col2,col3
1001,a,A
1002,b,B
1003,c,C

日本語が含まれる場合

日本語が含まれた次のようなDataFrameをto_csvで書き出すと、文字コードのデフォルトがshift-jisのExcelでは、文字化けしてしまいます。

print(df)
#    col1 col2 col3
# 0  1001    a    あ
# 1  1002    b    い
# 2  1003    c    う

そこで、encodingパラメータに日本語が扱える文字コード(shift-jiscp932など)を指定することで、エラーを回避することができます。

df.to_csv('sample.csv', encoding='cp932')
sample.csv
,col1,col2,col3f
0,1001,a,あ
1,1002,b,い
2,1003,c,う

また、開発環境によって次のようなエラーが発生することがあります。

UnicodeEncodeError: 'cp932' codec can't encode character '\xa0'

その場合は、次のようにして無理やり回避させます。

with open('sample.csv', mode='w', encoding='cp932', errors='ignore') as f:
	df.to_csv(f)

任意の行数ごとに分割して書き出す

20行ずつ分割してCSVファイルに書き出す
import seaborn as sns # => pip install seaborn

iris = sns.load_dataset('iris')
print(iris)
# 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
# ︙
# 145           6.7          3.0           5.2          2.3  virginica
# 146           6.3          2.5           5.0          1.9  virginica
# 147           6.5          3.0           5.2          2.0  virginica
# 148           6.2          3.4           5.4          2.3  virginica
# 149           5.9          3.0           5.1          1.8  virginica

# CSVファイルを20行ごとに分割し、それぞれCSVファイルとして書き出す
for index, df in iris.groupby(by=iris.index//20):
    df.to_csv('sample_{}.csv'.format(index), index=False)
# sample_0.csv
# ︙
# sample_7.csv

書き出すCSVのデータをダブルクオートしたい場合

CSVファイルには、値がダブルクォーテーションで囲まれているものと囲まれていないものが存在します。

ダブルクオートされていないCSV
col1,col2,col3
1001,a,A
1002,b,B
1003,c,C
ダブルクオートされているCSV
"col1","col2","col3"
"1001","a","A"
"1002","b","B"
"1003","c","C"

システムによってはダブルクオートされたCSVファイルじゃないと、取り込んだ際にエラーが発生することもあります。

quotingパラメータを使用すると、どのようにダブルクオートさせるか指定して出力させることができます。

print(df)
#    col1 col2 col3
# 0  1001    a    A
# 1  1002    b    B
# 2  1003    c    C
import csv
df.to_csv('sample.csv', quoting=csv.QUOTE_ALL)
sample.csv
"","col1","col2","col3"
"0","1001","a","A"
"1","1002","b","B"
"2","1003","c","C"

quotingパラメータには、csv.QUOTE_ALLの値以外にも設定することができます。

デフォルト、csv.QUOTE_MINIMAL

  • quoting=csv.QUOTE_NONE:すべてをタブルクオートさせずに出力したい
  • quoting=csv.QUOTE_ALL:すべてをタブルクオートさせて出力したい
  • quoting=csv.QUOTE_NONNUMERIC:数値だけクオートせずに、それ以外をダブルクオートさせて出力したい
  • quoting=csv.QUOTE_MINIMAL:デリミタや引用符、改行コードのような特別な文字を含む値だけをクオートさせて出力したい