CSVの読み込みとデータの確認
read_csvでファイルを読み込み、shape、head、info、describeを使って直ちに確認します。正しく読み込まれていないファイルは、エラーを出さないまま誤った結果をもたらすからです。
どうやって学びますか?
解決する問題
これまでは、表のすべての値をコード内に手作業で入力してきました。3行程度なら、それでも問題ありません。
実際のデータはコードの中には存在しません。誰かからメールで送られてきたり、システムからエクスポートされたり、スプレッドシートから保存されたファイルの中にあります。そして、そのファイルには12行ではなく、1万2000行ものデータが入っています。
そうしたファイルは、通常次のようになっています。プレーンテキストで、1行目に列名があり、その後は1行に1データずつ、カンマで区切られて並んでいます:
product,quantity,price
pen,12,15.0
notebook,5,60.0これがCSV(comma-separated values:カンマ区切り値)です。これを表に変換するのはpandasならわずか1行であり、pandasで最もよく使われる行でもあります。
しかし、その行を書いた直後に身につけるべき習慣があります。それこそがこの章の真のテーマです。「ファイルを読み込んだら、必ず中身を確認する」ということです。何行読み込まれたか、列名は想定通りか、数値列が本当に数値として読み込まれたか。この30秒を惜しむと、エラーが一切出ないまま計算結果だけが間違っているという形で、ずっと後になってミスが表面化します。
この章を終える頃には
pd.read_csv()を使ってファイルを読み込み、それに伴うパスの問題に対処できるようになるhead()、tail()、shape、columnsを使ってファイルをすばやく確認できるようになるinfo()を読み解き、行数、空のセル、すべての列の型を一度に確認できるようになるdescribe()を使って数値列の要約を確認できるようになるFileNotFoundErrorや誤った区切り文字を認識し、修正できるようになる
前提条件: SeriesとDataFrame — pandasの2つの基本。
まずファイルを作成する
プロジェクトフォルダ内に、sales.csvという名前のファイルを以下の6行をそのまま記述して作成します:
product,quantity,price
pen,12,15.0
notebook,5,60.0
bag,2,850.0
bottle,7,120.0
eraser,30,8.01行目はデータではなく、列名(ヘッダー)です。残りの5行が5つのデータ行となります。
1行で読み込む
import pandas as pd
sales = pd.read_csv("sales.csv")
print(sales)product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
3 bottle 7 120.0
4 eraser 30 8.0この1行のコードは、次の3つのことを人知れず実行しています:
第1に、1行目をヘッダーとして扱いました。そのため、product、quantity、priceはデータ行ではなく列名になりました。
第2に、インデックスを構築しました。左端に並んでいる0から4までの数字です。これはファイルの中には存在しなかったものです。
第3に、そしてこれが最も重要な点ですが、各列の型を推測しました。ファイル内のデータはすべてテキストであり、12という値も実際には2つの文字にすぎません。pandasは各列を確認し、それが数値なのかテキストなのかを判断しました。
この推測は通常は正しく機能します。しかし、もし間違っていたとしてもエラーは一切発生しません。だからこそ、自分の目で確認する必要があるのです。
ファイルを読み込んだ後に確認すべき4つのこと
shape — どれだけのデータが読み込まれたか
print(sales.shape)(5, 3)5行3列です。元のファイルには6行ありましたが、ヘッダーは行ではないため、ここでは5行になります。もしこの数値が想定よりも大幅に少なかったり、ぴったり1行多かったりした場合、それは何かが誤って読み込まれた最初の兆候です。
head()とtail() — 先頭と末尾
print(sales.head(3))product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0実際のファイルにある1万2000行をすべて出力すると、ターミナルが埋め尽くされてしまい、何も把握できません。head()は先頭の数行を表示します(引数を指定しない場合は5行)。tail()は末尾に対して同じことを行います。末尾を確認することは特に役立ちます。エクスポートされたファイルには、末尾に余計な空行や合計行が含まれていることがよくあるからです。
info() — すべてを一目で把握する
sales.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 product 5 non-null object
1 quantity 5 non-null int64
2 price 5 non-null float64
dtypes: float64(1), int64(1), object(1)
memory usage: 252.0+ bytesprint(sales.info())ではなく、sales.info()と記述している点に注目してください。このメソッド自体が出力を行い、何も返さないため、printで囲むと末尾に余計なNoneが出力されてしまいます。
これはファイルを読み込んだ後に実行する処理として最も役立ちます。2つの重要な事実を一度に教えてくれるからです:
Non-Null Count — 各列で実際に値が入っているセルの数です。ここでは3列すべてが5 non-nullとなっているため、空のセルはありません。どこかに4 non-nullがあれば、その列に1つ空のセルがあることを意味し、計算結果にも影響を与えます。
Dtype — 推測されたデータ型です。quantityはint64、priceはfloat64、productはobjectとして読み込まれました。期待通りの結果です。
(最終行のmemory usageの数値は使用している環境によって異なる場合がありますが、気にする必要はありません。)
describe() — 数値列の要約
print(sales.describe())quantity price
count 5.00000 5.000000
mean 11.20000 210.600000
std 11.12205 360.217712
min 2.00000 8.000000
25% 5.00000 15.000000
50% 7.00000 60.000000
75% 12.00000 120.000000
max 30.00000 850.000000数値列だけを抜き出し、それぞれに対して8つの計算を行います。productはテキストであるため含まれていません。
ここで1つ注目すべき点があります。priceのmean(平均値)は210.6ですが、50%(中央値)はわずか60です。これほど大きな差があるということは、他から大きくかけ離れて高い値が1つ存在することを意味しており、maxを見ればそれが850であるとわかります。これがdescribe()の読み方です。平均値と中央値が乖離しているときは、そこに注目してください。
完全な例
report.py:
import pandas as pd
sales = pd.read_csv("sales.csv")
print("Rows, columns:", sales.shape)
print()
print(sales.head(3))
print()
print("Columns :", list(sales.columns))
print("Total qty :", sales["quantity"].sum())
print("Avg price :", round(sales["price"].mean(), 2))Rows, columns: (5, 3)
product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
Columns : ['product', 'quantity', 'price']
Total qty : 56
Avg price : 210.6短いプログラムですが、その構造に注目する価値があります。読み込み、確認し、その後に計算しています。shapeやhead()は計算ではなく、「正しいファイルを読み込めたか?」「想定通りの行数があるか?」という問いかけです。
そして、list(sales.columns)を出力しているのにも理由があります。リストとして出力すると、各列名が引用符で囲まれて表示されるため、'quantity 'のように末尾にスペースが含まれている場合でもすぐに気づくことができます。他の方法で出力するとスペースが見落とされ、後になってKeyErrorとして現れることになります。
問題が起きたとき
FileNotFoundError: [Errno 2] No such file or directory: 'sales.csv' Pythonがカレントフォルダ内を探したものの、見つかりませんでした。これはPythonコースの第2章と同じ問題です。ターミナルの現在地とファイルの場所が異なっています。pwdを実行して現在地を確認し、dirでファイルが存在するか確認してから、cdで移動してください。Windowsでは、実際のファイル名がsales.csv.txtになっていることもあります。エクスプローラーでファイル名拡張子の表示をオンにしてください。
ファイル全体が1つの列として読み込まれてしまった 区切り文字がカンマではありません。ヨーロッパの多くの地域で保存されたファイルでは、カンマが小数点として使われるため、セミコロンが使用されます。pd.read_csv("sales.csv", sep=";")と記述してください。タブ区切りファイルの場合はsep="\t"とします。
数値列がobjectとして読み込まれてしまった 列のどこかにテキストが混入しています — N/A、-、空のセル、または桁区切りのカンマ(1,200)などです。df["col"].unique()を実行して、実際に何が入っているかを確認してください。通常、異常な値はすぐに目立ちます。修正するにはpd.to_numeric(df["col"], errors="coerce")を使います。これにより数値以外のすべての値が空の値に変換されます。これについては欠損値を扱う章で詳しく説明します。
UnicodeDecodeError ファイルがUTF-8として保存されていません。スプレッドシートから書き出したファイルではよく発生します。pd.read_csv("sales.csv", encoding="utf-8-sig")を試し、それでも解決しない場合はencoding="latin-1"を試してください。
ファイルにヘッダーがないのに、データの先頭行が列名になってしまった pd.read_csv("sales.csv", header=None)と記述すると、列名は0, 1, 2になります。列名を付けるには、names=["product", "quantity", "price"]を追加します。
sales.info()の後に余計なNoneが出力された print(sales.info())と記述されています。info()自体が出力を行い、何も返さないため、printはその返されたNoneを出力しています。単にsales.info()と記述してください。
ステップ 4 / 6 — 予想する
理解度をチェック
ファイルには6行あります。shapeは何を出力しますか?
from io import StringIO
import pandas as pd
# Stands in for sales.csv, so this snippet runs on its own.
RAW = """product,quantity,price
pen,12,15.0
notebook,5,60.0
bag,2,850.0
bottle,7,120.0
eraser,30,8.0
"""
sales = pd.read_csv(StringIO(RAW))
print(sales.shape)- A(5, 3)
- B(5, 4)
- C(3, 5)
- D(6, 3)
info()のNon-Null Count列は何を示していますか?
- A各列で実際に値が入っているセルの数 — 行数より少ない場合は、そこに空のセルが存在することを意味する
- B各列の空のセルの数
- C各列が占めるメモリ容量
- D各列に存在する個別の値の数
ファイルはセミコロン区切りですが、sepが指定されていません。何が出力されますか?
from io import StringIO
import pandas as pd
# Stands in for sales.csv, so this snippet runs on its own.
RAW = """product;quantity
pen;12
notebook;5
"""
df = pd.read_csv(StringIO(RAW))
print(df.shape)
print(list(df.columns))- A(3, 1) ['product;quantity']
- B(2, 1) ['product;quantity']
- C(2, 2) ['product', 'quantity']
- D`ParserError`
回答にはアカウントが必要です
ログインして答えを確認
問題は上にあります。頭の中で答えを考えることがいちばん大切です。ログインすると、正解・解説・3 段階のヒントを確認できます。
やってみよう
少なくとも8人分のデータを持ち、名前、年齢、点数の3つの列からなるstudents.csvというファイルを作成してください。
次に、以下の処理を行うreport.pyを作成します:
- ファイルを読み込む
shapeとlist(columns)を出力するhead(3)とtail(2)を出力するinfo()を実行し、3つの列すべての型が想定通りであるか確認するdescribe()を出力し、さらに点数の列の平均値と最大値を個別に出力する
その後、意図的にエラーや不具合を発生させてみましょう。3つのパターンを試し、それぞれどのような結果になるか観察してください:
- 点数の列のセル1つに
N/Aと入力してみる。info()のDtypeの表示はどう変わりましたか? 平均値には何が起きましたか? - ファイル内のすべてのカンマをセミコロンに置き換えてみる。
shapeはどう表示されましたか? それはなぜですか? - 1行目のヘッダーを削除してみる。 何行読み込まれましたか? また、列名はどうなりましたか?
これら3つは実際の現場でも日常的に発生するものであり、それぞれに明確な兆候があります。今日あらかじめ意図的に体験しておくことで、次回からは一目で原因を見抜けるようになります。