Pythonデータ分析 #2 データ読み込み — CSV、Excel、そして最初の探索
第1回で環境を作り、DataFrame が何かを見ました。今回は分析の実際の出発点である ファイルの読み込み を扱います。講座用のデータはいつもきれいですが、実務で受け取るファイルは違います。エンコーディングが違い、区切り文字がタブで、最初の 2 行がタイトルの飾りで、数値カラムにカンマが混ざっています。この記事では、そういうファイルを pandas で読む方法と、読み込んだ直後に必ず通るべき確認ルーチンを固めます。
分析は他人が作ったファイルから始まります #
データ分析業務の最初の入力は、ほとんどの場合、自分が作っていないファイルです。取引先から届いた Excel、社内システムからダウンロードした CSV、API が返した JSON といったものです。これらのファイルは作った人の環境をそのまま抱えているので、読む側が形式を合わせる必要があります。pandas の読み込み関数が数十個もの引数を持っている理由がこれです。全部覚える必要はなく、よくぶつかる 4 つを知っていれば大半は解決します。
read_csvの基本 #
いちばんよく使う関数です。きちんと作られた CSV なら 1 行で終わります。
import pandas as pd
df = pd.read_csv("sales.csv")問題は、きちんと作られていない CSV です。よく使う引数を 1 つずつ見ていきます。
encoding: 日本語環境最大の落とし穴 #
Windows の Excel で「CSV で保存」したファイルは、UTF-8 ではなく Shift_JIS(正確には Windows 拡張の cp932)で保存されている場合が多いです。こういうファイルをそのまま読むと、こうなります。
df = pd.read_csv("sales.csv")
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 0 ...UnicodeDecodeError が出たら、ほぼ確実に Shift_JIS のファイルです。encoding 引数で解決します。
df = pd.read_csv("sales.csv", encoding="cp932")逆のケースもあります。UTF-8 のファイルなのに日本語が文字化けして見えるなら、encoding="utf-8-sig" を試してみてください。ファイルの先頭に BOM という見えない印が付いた UTF-8 ファイルを処理するエンコーディングです。
sep: 区切り文字がカンマでないとき #
名前は CSV なのに、実際はタブやセミコロンで区切られたファイルがよくあります。読み込んだのにカラムが 1 つに固まって出てきたら、区切り文字の問題です。
df = pd.read_csv("sales.tsv", sep="\t") # タブ区切り
df = pd.read_csv("export.csv", sep=";") # セミコロン区切りheader: 先頭行がデータのヘッダーでないとき #
システムからダウンロードしたファイルは、先頭行に「2026年7月 売上集計」のようなタイトルが入っていることがあります。実際のカラム名が 3 行目なら header=2 と指定します。0 から数える点に注意してください。
df = pd.read_csv("report.csv", header=2) # 3行目をカラム名に
df = pd.read_csv("raw.csv", header=None) # ヘッダーがそもそもないファイルdtype: 型を先に指定する #
商品コードのように「数字に見えるが数値ではない」カラムがあります。001234 のようなコードをそのまま読むと、pandas が整数 1234 に変えて先頭の 0 を飛ばしてしまいます。dtype で防ぎます。
df = pd.read_csv("sales.csv", dtype={"product_code": str})read_excel: シートのあるファイル #
Excel ファイルは read_excel で読みます。CSV と最も違う点は、シート という次元が 1 つ多いことです。
df = pd.read_excel("report.xlsx") # 最初のシート
df = pd.read_excel("report.xlsx", sheet_name="7月") # 名前で指定
df = pd.read_excel("report.xlsx", sheet_name=1) # 順番で指定 (0から)
# すべてのシートを一度に: シート名をキーとする辞書が返る
sheets = pd.read_excel("report.xlsx", sheet_name=None)openpyxl パッケージが必要なので、第1回で作ったプロジェクトに uv add openpyxl で追加すれば使えます。Excel を読み書きして書式まで扱う自動化視点の整理は、業務自動化シリーズ第2回に別途あります。このシリーズでは「分析のために DataFrame として取り込む」ところまでを扱います。
read_jsonとクリップボード #
API レスポンスを保存した JSON ファイルは read_json で読みます。
df = pd.read_json("data.json")そして、意外とよく使う機能が 1 つあります。Excel や Web ページの表をコピーした状態で実行すると、クリップボードの内容をそのまま DataFrame にしてくれます。
df = pd.read_clipboard()ファイルに保存するほどでもない小さな表を素早く確認したいときに便利です。
読み込み直後の確認ルーチン #
ファイルを読むのに成功したら終わり、ではありません。読み込んだ直後にデータの状態を確認する習慣 が、このシリーズで最も強調したい部分です。小さな販売データを例に、流れを追ってみます。
df = pd.read_csv("sales.csv", encoding="cp932")
df.head() date product region qty price
0 2026-04-01 キーボード 東京 3 45000
1 2026-04-01 マウス 大阪 5 12000
2 2026-04-02 モニター 東京 1 320,000
3 2026-04-02 キーボード 名古屋 2 45000
4 2026-04-03 マウス 東京 NaN 12000head() は先頭 5 行、tail() は末尾 5 行を見せてくれます。出力を見ただけで、すでに 2 つ気づくことがあります。price にカンマの混ざった値があり、qty に NaN(欠損値)があります。次は info() です。
df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 12 entries, 0 to 11
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 12 non-null object
1 product 12 non-null object
2 region 12 non-null object
3 qty 11 non-null float64
4 price 12 non-null object
dtype: memory usage: 608.0+ bytesinfo() 1 回で 3 つのことが読み取れます。
- 行数: 全体で 12 行。元ファイルの行数と一致するか比較します
- 欠損: qty が 11 non-null なので、1 個が空いています
- dtype: 各カラムの型。ここで異常のシグナルをつかみます
数値カラムの分布は describe() で確認します。
df.describe() qty
count 11.000000
mean 2.818182
std 1.401298
min 1.000000
25% 2.000000
50% 3.000000
75% 3.500000
max 5.000000平均、最小値、最大値をざっと眺めると、マイナスの数量や異常に大きい値のような外れ値を早い段階で発見できます。行数と列数だけ素早く見たいときは shape を使います。
df.shape
# (12, 5)dtypeが間違っているというシグナル #
上の info() 出力で最も重要な発見は、price が object だという点です。価格は当然数値であるべきなのに、文字列として読み込まれました。さらに describe() の出力から price がそもそも抜けています。数値カラムではないので、統計の対象から除外されたのです。
原因は head の出力で見た 320,000 です。カンマの混ざった値が 1 つでもあると、pandas はそのカラム全体を文字列として読みます。空白が混ざった場合(" 45000")も同じ症状になります。直す方法は 2 つです。
df = pd.read_csv("sales.csv", encoding="cp932", thousands=",")thousands="," は、桁区切りのカンマとして解釈せよという意味です。読み込みの時点で解決するので、いちばんきれいです。すでに読み込んでしまった後なら、文字列処理をしてから変換します。
df["price"] = df["price"].str.replace(",", "").astype(int)
df.info()
# ...
# 4 price 12 non-null int64price が int64 に変わり、これで describe() にも含まれます。どちらの方法でも、変換後に info() で結果を確認し直すところまでがワンセットです。
保存: to_csvとto_excel #
整理した DataFrame をファイルに書き出すときは to_csv、to_excel を使います。ここに落とし穴が 1 つあります。
df.to_csv("sales_clean.csv")このまま保存すると、ファイルの最初のカラムに 0, 1, 2, ... というインデックスが一緒に保存されます。このファイルを読み直すと、Unnamed: 0 という正体不明のカラムが生まれます。よそから受け取ったファイルに Unnamed: 0 カラムがあったら、十中八九この失敗の痕跡です。index=False を基本の習慣に しておくのが良いです。
df.to_csv("sales_clean.csv", index=False, encoding="utf-8-sig")
df.to_excel("sales_clean.xlsx", index=False, sheet_name="整理済み")encoding="utf-8-sig" で保存すると、Windows の Excel でダブルクリックして開いても日本語が文字化けしません。成果物を Excel ユーザーに渡すなら、このオプションまで押さえておくのが安全です。
まとめ #
今回扱った流れです。
- 実務のデータは汚いファイルから始まり、読む側が形式を合わせます
read_csvの中核引数 4 つ:encoding(Shift_JIS の落とし穴)、sep、header、dtyperead_excelはsheet_nameでシートを指定し、sheet_name=Noneなら全体を辞書で受け取ります- 読み込み直後のルーチン:
head()/tail()→info()(行数・欠損・dtype)→describe()→shape - 数値であるべきカラムが object なら、カンマか空白が原因で、
thousands=","またはstr.replace+astypeで直します - 保存は
index=Falseを基本に、Excel に渡す用途ではutf-8-sigまで押さえます
次回(#3 選択とフィルタ)では、読み込んだデータから必要な部分だけを取り出す方法を扱います。カラムの選択、loc と iloc の違い、条件で行を絞るブールインデックスまでが範囲です。