よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

csvモジュールでcsvファイルを読む

今回はCSVファイルの読み込みを行います。

PythonCSVファイルの読み書きをするにはcsvモジュールをimport csvして使います。 標準モジュールなのでインストールの必要はありません。

作業環境


Androidスマホ
Python3.6と3.7
メモ帳アプリ Jota+  
Qpython


目次



CSVファイルとは?


Comma Separated Values またはCharacter Separated Values、略してCSV

カンマ、Tabや任意の文字で値を切り離した、その名の通りの形式のファイルです。   拡張子はcsv。 Tab区切りを特にtsvというようです。


csvはメモ帳などテキストエディタで開けますし、Excelなどのスプレッドシートでもそのまま開いて読むことができます。 スプレッドシートで開いた場合、カンマで区切られた値が各セルに収まります。

「統計 csv」でネット検索すると人口や経済のデータがたくさん見つかりますように、非常によく使われる形式です。


メモ帳でCSVファイルを作ってみよう


この記事はスマホで書いてますのでスマホでの操作で説明しますが、WindowsでもMacでもUbuntuでも、同じようにできます。

手順

  1. メモ帳などテキストエディタを開く
  2. 値をカンマで区切ったデータを書く
  3. 拡張子を「.csv」にして文字コードutf-8shift_jisか(適切なもの)で保存する

これだけ。簡単!
じゃ、やっていきます。


スマホアプリのJota+で書いたところ(utf-8で保存した)  

f:id:chayarokurokuro:20190905223133j:plain

↓ 作ったcsvファイルをGoogleスプレッドシートで開いたところ

f:id:chayarokurokuro:20190905223154j:plain

csvファイルをExcelで開いたところ

f:id:chayarokurokuro:20190905223204j:plain

  • 値はダブルがシングルのクォーテーションで囲む
  • 数値にクォーテーションは不要
  • カンマで区切る
  • 行の最後はカンマ不要で、改行をする


たったこれだけで簡単にできました。  


Excelの文字化けは後回し!!!


PythonCSVファイルを読む


CSVファイルをPythonで読むには幾つか方法がありますが、

  1. 単に開いて読む
  2. csvモジュールを使って読む
  3. numpyを使って読む
  4. pandasを使って読む


この4つの内、numpy以外の3つを試してみます。


csvファイルをネットからPythonでダウンロード


この際なので、読み取る為のCSVファイルを、Pythonを使ってネットからダウンロードしてみます。

下のPythonコードを実行すると自動的にcsvファイルがダウンロードされます。


ダウンロード先のページはこちら
国勢調査 時系列データ CSV形式による主要時系列データ | ファイル | 統計データを探す | 政府統計の総合窓口

c01.csvというファイル名をダウンロードしますが、名前を変えずにPythonスクリプトと同じフォルダに保存します。


Web上のURLからファイルをダウンロードするには、標準モジュールのurllibか、非標準モジュールのrequestsを使うと出来そうです。

今回はインストール不要なurllibを使います。

また、sslモジュールのインポートと認証回避のコメント部分の一行を書かないとエラーが出ましたので追加しました。


実行上の注意!

下記のコードは「既にダウンロード済みの場合はダウンロードしない」というような賢い仕様になっておりません。

実行する度にダウンロードするポンコツ仕様です。

実行に注意してください。政府のサーバーなので…

import urllib.request
import ssl

# 認証回避
ssl._create_default_https_context = ssl._create_unverified_context


url = "https://www.e-stat.go.jp/stat-search/file-download?statInfId=000031524010&fileKind=1"
save_name = "c01.csv"

urllib.request.urlretrieve(url,save_name)
('c01.csv', <http.client.HTTPMessage at 0xe7c59510>)


コードを実行したら、エクスプローラなどで上記のPythonスクリプトを実行したカレントディレクトリにc01.csvが保存されたと思いますので確認してみて下さい。


CSVファイルを単に読む


テキストファイルを読む時に

with open(ファイル名,"r") as f:
    print(f.read())

とするのと同様にcsvを開いてみます。


実行上の注意!

ダウンロードしたc01.csvのファイルは983行あります。
下記のコードには、そのファイルを読み取って標準出力(print)させておりますが、出力が1万行を超えるものが含まれております。

実行する前に確認して行うようにしてください。
(スマホでやってフリーズ気味)

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = f.read()
    print("・タイプ :",type(r))
    print("・要素数 :",len(r))
    print(r)
・タイプ : <class 'str'>
・要素数 : 48871
"都道府県コード","都道府県名","元号","和暦(年)","西暦(年)","注","人口(総数)","人口(男)","人口(女)"
"00","全国","大正",9,1920,"",55963053,28044185,27918868
"01","北海道","大正",9,1920,"",2359183,1244322,1114861

(途中省略)

"47","沖縄県","平成",27,2015,"",1433566,704619,728947
"1) 沖縄県は調査されなかったため,含まれていない。"
"2) 長野県西筑摩群山口村と岐阜県中津川市の境界紛争地域人口(男39人,女34人)は全国に含まれているが,長野県及び岐阜県のいずれにも含まれていない。"

読み込めました。 実行結果の一番最初に、str型で48871文字と出ています。


encodingを指定せずに実行したところ、UnicodeDecodeErrorが出た。 encoding="shift_jis"を指定したら上手く行きました。

次は一行ずつ読んでみます。

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = f.readlines()
    print(r)
    print("・タイプ :\n",type(r))
    print("\n・要素数 :\n",len(r))
    print("\n・1行目 :\n",r[0])
    print("\n・行ごとの要素のタイプ :",type(r[0]))
['"都道府県コード","都道府県名","元号","和暦(年)","西暦(年)","注","人口(総数)","人口(男)","人口(女)"\n', '"00","全国","大正",9,1920,"",55963053,28044185,27918868\n', '"01","北海道",   (途中省略)   ,全国に含まれているが,長野県及び岐阜県のいずれにも含まれていない。"\n']
・タイプ :
 <class 'list'>

・要素数 :
 983

・1行目 :
 "都道府県コード","都道府県名","元号","和暦(年)","西暦(年)","注","人口(総数)","人口(男)","人口(女)"


・行ごとの要素のタイプ : <class 'str'>

今度はリスト型で要素数が983です。 readlines()で読ませたので983行あるということです。

インデックスを指定すると行をstr型で取得できました。

for文で1行ずつ出力させてみます。

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = f.readlines()
    for i in r:
        print("・タイプ :",type(i))
        print("・要素数 :",len(i))
        print("\n===============\n")
        print(i)
    ・タイプ : <class 'str'>
    ・要素数 : 68
    
    ===============
    
    "都道府県コード","都道府県名","元号","和暦(年)","西暦(年)","注","人口(総数)","人口(男)","人口(女)"
    
    ・タイプ : <class 'str'>
    ・要素数 : 52
    
    ===============
    
    "00","全国","大正",9,1920,"",55963053,28044185,27918868
    
    ・タイプ : <class 'str'>
    ・要素数 : 50
    
    ===============
    
    "01","北海道","大正",9,1920,"",2359183,1244322,1114861
    
    ・タイプ : <class 'str'>
    ・要素数 : 47
    ===============
   (途中省略) 
    ===============   
    "2) 長野県西筑摩群山口村と岐阜県中津川市の境界紛争地域人口(男39人,女34人)は全国に含まれているが,長野県及び岐阜県のいずれにも含まれていない。"



行ごとに取得できました。

しかし、ここまで見てきたように、単にcsvをopen()で開くだけだと1文字単位でしか読み込めません。

せっかく値ごとをカンマで区切った苦労が活きてこない。

奥さん、そこでcsvモジュールの出番です。


csvモジュールを使って読む


ファイルをopen()する所までは上と一緒ですが、その後にcsv.reader()というのを使います。
csv.readerです。readではありません。罠です。ご注意下さい。

では。

import csv

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = csv.reader(f)
    print(r)
    print("・タイプ :",type(r))
<_csv.reader object at 0xe7c931b0>
・タイプ : <class '_csv.reader'>

おっ、print(r)の所でcsvの内容が出力できると思いきや、オブジェクトが表示されましたよ。

こういう時はだいたいfor分で取り出せる。知らんけど。

いざ。

import csv

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = csv.reader(f)
    for i in r:
        print(i)
        print("・タイプ :",type(i))
        print("・要素数 :",len(i))
        print("\n===============\n")
['都道府県コード', '都道府県名', '元号', '和暦(年)', '西暦(年)', '注', '人口(総数)', '人口(男)', '人口(女)']
・タイプ : <class 'list'>
・要素数 : 9

===============

['00', '全国', '大正', '9', '1920', '', '55963053', '28044185', '27918868']
・タイプ : <class 'list'>
・要素数 : 9

===============

['01', '北海道', '大正', '9', '1920', '', '2359183', '1244322', '1114861']
・タイプ : <class 'list'>
・要素数 : 9

['46', '鹿児島県', '平成', '22', '2010', '', '1706242', '796896', '909346']
・タイプ : <class 'list'>
・要素数 : 9

===============

['47', '沖縄県', '平成', '27', '2015', '', '1433566', '704619', '728947']
・タイプ : <class 'list'>
・要素数 : 9

(途中省略) 

素数が9個ずつ入ったリストが各行ごとで取得できている。

そしたらば、行と列をインデックスで指定すれば、個別に値を取得できそう。

まずは、全体を一つのリストにします。

そこからインデックスを指定し、3行目の3列目を取り出します。 という事はインデックスは2と2。

import csv

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = csv.reader(f)
    
    # 全体をリストにする
    lst = [i for i in r]
    print("\n【 全体のリスト 】\n",lst)
    
    # 行と列をインデックス指定
    print("\n【3行3列目の値】\n",lst[2][2])    
            
【 全体のリスト 】
 [['都道府県コード', '都道府県名', '元号', '和暦(年)', '西暦(年)', '注', '人口(総数)', '人口(男)', '人口(女)'], ['00', '全国', '大正', '9', '1920', '', '55963053', '28044185', '27918868'],  '704619', '728947'],  (途中省略)  ['2)\u3000長野県西筑摩群山口村と岐阜県中津川市の境界紛争地域人口(男39人,女34人)は全国に含まれているが,長野県及び岐阜県のいずれにも含まれていない。']]

【3行3列目の値】
 大正
  1. 全体がリスト=[[行ごとのリスト]]
  2. 行ごとのリスト=[列の要素のリスト]

1と2が入れ子になってます。

全体リストからインデックスでその行の列を指定し、各々の値を取得できることが出来るようになりました。



delimiter(区切り文字)指定


csvは通常はカンマで値を区切っているので、csv.readerの区切り文字はデフォルトでカンマになっています。(指定しなければカンマ区切り)

区切り文字をスペースやTabやその他にも変える事ができます。

delimiterの指定はcsv.readerの中で行います。

スペースで区切る場合は以下のように。

import csv

file_name = "c01.csv"

with open (file_name,"r",encoding="shift_jis") as f:
    r = csv.reader(f,delimiter=" ")

スペースで区切りを読むので、読み込んだcsvファイルが要素毎にスペースで切り離して書かれてある必要かあります。
Tabで区切る場合も同様に、csvファイルがTabで値を区切った書き方がしてあるとして、次のようにします。

import csv

file_name = "c01.csv"

with open (file_name,"r") as f:
    r = csv.reader(f,delimiter="\t")


引用符


CSVファイルを作った時に値をダブルクォーテーション"で囲みました。 デフォルトでダブルクォーテーションが引用符になっているようですが、値に"を含む場合がある。 そういう時はシングルクォーテーションを引用符に設定し、値に"を含めるようにできます。

with open (file_name,"r") as f:
    r = csv.reader(f,quotechar="'")

じゃあ、"'も値に入れたい場合は次のように。

with open (file_name,"r") as f:
    r = csv.reader(f,quoting=csv.QUOTE_NONE)


改行コード


改行コードを、Windows\nではなく\r\nで、古いMacOS\rを、Linux系は\nを読むとか。

そんな訳で、OSが変わると改行で上手くいかない事があるそうなので、newline=""を指定しておくのが無難とのこと。

こちらはopen()の中で指定する。

with open (file_name,"r",newline="") as f:
    r = csv.reader(f)




長くなりましたので、一旦ここで切ります。

csvファイルはnumpyでも読めるようですが、Pandasで読む方が楽なようですので、次回はPandasを試します。

一応参考
numpyでのCSVファイルの読み書きはこちら
NumPyでCSVファイルを読み込み・書き込み(入力・出力)


csvファイルをExcelで開いて文字化けしたときの対処法


AndroidスマホなどLinux系OSで作ったファイルをWindowsに移したりして開くと、頻繁にこうした文字化けが起こります。 どうにかならんもんかのぅ。

リンクだけ載せておきます。 CSVファイルが文字化けしてExcelで見れない時の対処法について|Excel実務のノウハウのお勉強

スマートフォンの文字化けを直したい | 日経 xTECH(クロステック)