5. pandasでデータ加工(45分)#
この章では、pandasを使って表形式のデータを読み込み、加工し、集計する流れを体験します。
注釈
入力が遅れた場合などに、この章のNotebookをダウンロードできます。ダウンロードしたファイルを作業フォルダに置き、JupyterLabのファイルブラウザでダブルクリックして開いてください。
ダウンロードしたNotebookには、この章の説明の文章も入っています。図などの一部の書き方は、JupyterLabでは記号のまま表示されます。
自分で作った5_pandas.ipynbと同じ名前のため、上書きしないように注意してください。
5.1. pandasとは#
pandasはオープンソースのデータ分析と加工のためのライブラリです。 表形式のデータを読み込み、加工、集計といった作業ができます。 Excelのような行と列を持つ表のデータを、Pythonのコードで扱えます。 CSVファイルなどからデータを読み込み、必要な行や列を取り出したり、抜けている値を補ったり、複数の表をつなげたり、グループごとに集計したりできます。
pandas公式サイト:https://pandas.pydata.org/
第4章のJupyterLabを起動するの手順で、作業フォルダでJupyterLabを起動してください。
新しいNotebookを作成し、名前を5_pandas.ipynbに変更します。
最初のセルでpandasをインポートします。
pandasはpdという別名でインポートするのが慣例です。
import pandas as pd
5.2. SeriesとDataFrame#
pandasでは、主にSeriesとDataFrameの2種類の形でデータを扱います。
Seriesは、1次元のデータです。 値の並びに、インデックスというラベルが付いています。 次のコードでは、商品名をインデックス、単価を値としたSeriesを作ります。
prices = pd.Series([400, 450, 500], index=["コーヒー", "紅茶", "ケーキ"])
prices
コーヒー 400
紅茶 450
ケーキ 500
dtype: int64
左側の商品名がインデックス、右側の数値が値です。
最後の行のdtypeは値のデータ型で、int64は整数を表します。
インデックスはindexで確認できます。
prices.index
Index(['コーヒー', '紅茶', 'ケーキ'], dtype='str')
DataFrameは、行と列を持つ表です。 列名と列の値を辞書で渡して作ります。 DataFrameの各列はSeriesです。
items = pd.DataFrame({"item": ["コーヒー", "紅茶", "ケーキ"], "price": [400, 450, 500]})
items
| item | price | |
|---|---|---|
| 0 | コーヒー | 400 |
| 1 | 紅茶 | 450 |
| 2 | ケーキ | 500 |
DataFrameの行にもインデックスが付きます。
インデックスを指定せずに作ると、0から始まる番号(RangeIndex)になります。
items.index
RangeIndex(start=0, stop=3, step=1)
列名はcolumnsで確認できます。
items.columns
Index(['item', 'price'], dtype='str')
5.3. CSVファイルの書き出しと読み込み#
CSVファイルは、表形式のデータを保存するテキストファイルです。
表の1行をファイルの1行に書き、値をカンマ(,)で区切って並べます。
多くのツールで読み書きできるため、表形式データの保存によく使われるフォーマットです。
DataFrameはto_csv()メソッドでCSVファイルに書き出せます。
index=Falseを指定すると、インデックスを書き出しません。
items.to_csv("items.csv", index=False)
上のコードを実行すると作業フォルダにitems.csvができます。
ファイルの中身は次のようなテキストです。
1行目が列名で、2行目以降が表の各行です。
item,price
コーヒー,400
紅茶,450
ケーキ,500
JupyterLabのファイルブラウザでitems.csvをダブルクリックすると、表形式で開きます。
左端の番号は表示のための行番号で、DataFrameのインデックスではありません。
items.csvを開いたJupyterLabの画面#
CSVファイルはpd.read_csv()関数で読み込みます。
読み込んだ表は、書き出す前のitemsと同じになります。
items_csv = pd.read_csv("items.csv")
items_csv
| item | price | |
|---|---|---|
| 0 | コーヒー | 400 |
| 1 | 紅茶 | 450 |
| 2 | ケーキ | 500 |
5.4. 売上データを読み込む#
この節からは、架空の店舗の売上データを使います。 2026年4月1日から30日までについて、店舗ごと、商品ごとの毎日の販売数を記録したデータです(実在の店舗とは関係ありません)。 データには次の列があります。
列名 |
内容 |
|---|---|
|
日付 |
|
店舗 |
|
商品 |
|
販売数 |
|
単価 |
次の2つのファイルをダウンロードし、作業フォルダの直下(5_pandas.ipynbと同じ場所)に置いてください。
sales.csv:売上データ(この節で使います)sales_next_month.csv:翌月の売上(「データを連結する」で使います)
注釈
sales.csvの中身は、JupyterLabで開いて確認してください。
Excelで開くと文字化けすることがあります。
pd.read_csv()関数で売上データを読み込みます。
parse_datesに列名を指定すると、その列を日付の型として読み込みます。
第6章で日付を横軸にしたグラフを描くため、ここで日付の型にしておきます。
df = pd.read_csv("sales.csv", parse_dates=["date"])
FileNotFoundErrorと表示された場合は、ファイルが見つからないことを示しています。
sales.csvが作業フォルダの直下にあるか、ファイル名がsales (1).csvなどに変わっていないかを確認してください。
続けて、読み込んだデータの概要を確認します。 加工する前に、どのような列があるか、何行あるか、値の抜けがないかなどを確かめます。
dfだけを実行すると、DataFrameが表示されます。
行が多い場合は、途中の行が「...」で省略され、先頭と末尾の行だけが表示されます。
表の下には、行の数と列の数が表示されます。
df
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38.0 | 400 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24.0 | 450 |
| 2 | 2026-04-01 | 東京店 | ケーキ | 19.0 | 500 |
| 3 | 2026-04-01 | 大阪店 | コーヒー | 35.0 | 400 |
| 4 | 2026-04-01 | 大阪店 | 紅茶 | 20.0 | 450 |
| ... | ... | ... | ... | ... | ... |
| 265 | 2026-04-30 | 大阪店 | 紅茶 | 18.0 | 450 |
| 266 | 2026-04-30 | 大阪店 | ケーキ | 14.0 | 500 |
| 267 | 2026-04-30 | 名古屋店 | コーヒー | 22.0 | 400 |
| 268 | 2026-04-30 | 名古屋店 | 紅茶 | 17.0 | 450 |
| 269 | 2026-04-30 | 名古屋店 | ケーキ | 11.0 | 500 |
270 rows × 5 columns
head()メソッドは先頭の5行を表示します。
df.head()
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38.0 | 400 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24.0 | 450 |
| 2 | 2026-04-01 | 東京店 | ケーキ | 19.0 | 500 |
| 3 | 2026-04-01 | 大阪店 | コーヒー | 35.0 | 400 |
| 4 | 2026-04-01 | 大阪店 | 紅茶 | 20.0 | 450 |
同様に、tail()メソッドは末尾の5行を表示します。
df.tail()
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 265 | 2026-04-30 | 大阪店 | 紅茶 | 18.0 | 450 |
| 266 | 2026-04-30 | 大阪店 | ケーキ | 14.0 | 500 |
| 267 | 2026-04-30 | 名古屋店 | コーヒー | 22.0 | 400 |
| 268 | 2026-04-30 | 名古屋店 | 紅茶 | 17.0 | 450 |
| 269 | 2026-04-30 | 名古屋店 | ケーキ | 11.0 | 500 |
describe()メソッドは、数値の列の統計(件数、平均、標準偏差、最小、最大など)を表示します。
df.describe()
| date | quantity | price | |
|---|---|---|---|
| count | 270 | 268.000000 | 270.000000 |
| mean | 2026-04-15 12:00:00 | 20.884328 | 450.000000 |
| min | 2026-04-01 00:00:00 | 9.000000 | 400.000000 |
| 25% | 2026-04-08 00:00:00 | 14.000000 | 400.000000 |
| 50% | 2026-04-15 12:00:00 | 19.000000 | 450.000000 |
| 75% | 2026-04-23 00:00:00 | 25.000000 | 500.000000 |
| max | 2026-04-30 00:00:00 | 43.000000 | 500.000000 |
| std | NaN | 8.376312 | 40.900641 |
日付の列(date)も表示され、最小(min)と最大(max)でデータの期間が分かります。
項目 |
意味 |
|---|---|
|
データの件数(欠損値(後述)などは件数から除外される) |
|
平均値 |
|
最小値 |
|
小さい方から25%の位置の値 |
|
中央値(小さい方から50%の位置の値) |
|
小さい方から75%の位置の値 |
|
最大値 |
|
標準偏差(値のばらつきの大きさ) |
shapeは、行の数と列の数を表示します。
df.shape
(270, 5)
5.5. データを抽出する#
注釈
コードを実行した結果が資料と違うときやエラーになったときは、「売上データを読み込む」のセルから順に実行し直してください。
DataFrameから、必要な列や行だけを取り出します。
列名を[]で指定すると、1つの列をSeriesとして取り出せます。
df["store"]
0 東京店
1 東京店
2 東京店
3 大阪店
4 大阪店
...
265 大阪店
266 大阪店
267 名古屋店
268 名古屋店
269 名古屋店
Name: store, Length: 270, dtype: str
出力の最後のdtype: strは、この列の型が文字列型(str)であることを表します。
pandas 3.0からは文字列の列がstr型と表示されます(それ以前のバージョンではobject型と表示されます)。
列名のリストを指定すると、複数の列をDataFrameとして取り出せます。
df[["store", "quantity"]]
| store | quantity | |
|---|---|---|
| 0 | 東京店 | 38.0 |
| 1 | 東京店 | 24.0 |
| 2 | 東京店 | 19.0 |
| 3 | 大阪店 | 35.0 |
| 4 | 大阪店 | 20.0 |
| ... | ... | ... |
| 265 | 大阪店 | 18.0 |
| 266 | 大阪店 | 14.0 |
| 267 | 名古屋店 | 22.0 |
| 268 | 名古屋店 | 17.0 |
| 269 | 名古屋店 | 11.0 |
270 rows × 2 columns
[]に開始:終了の形でスライスと同じ表記を指定すると、行を範囲で取り出せます。
スライスと同様、開始の行は含まれ、終了の行は含まれません。
開始や終了の数値を省略すると、それぞれ先頭から、末尾までを取り出します。
次のコードでは、先頭の2行(0行目と1行目)を取り出します。
df[:2]
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38.0 | 400 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24.0 | 450 |
locとiloc#
行と列を指定して取り出すにはlocやilocを使います。
どちらも[行, 列]の形で指定し、:で範囲を指定できます。
locは、インデックスのラベルと列名で指定します。
次のコードでは、インデックスが0から2までの行の、storeからquantityまでの列を取り出します。
locでは、範囲の終わりのラベル(2とquantity)も含まれます。
df.loc[0:2, "store":"quantity"]
| store | item | quantity | |
|---|---|---|---|
| 0 | 東京店 | コーヒー | 38.0 |
| 1 | 東京店 | 紅茶 | 24.0 |
| 2 | 東京店 | ケーキ | 19.0 |
ilocは、行のインデックス番号と列のカラム番号で指定します。どちらも0から始まる連番です。
次のコードでは、0行目と1行目の、1列目から3列目(store、item、quantity)を取り出します。
ilocでは、[]の範囲の指定と同じく、範囲の終わりは含まれません。
df.iloc[0:2, 1:4]
| store | item | quantity | |
|---|---|---|---|
| 0 | 東京店 | コーヒー | 38.0 |
| 1 | 東京店 | 紅茶 | 24.0 |
条件で抽出#
列に対して条件を書くと、行ごとに条件を満たすかがTrueまたはFalseで返されます。
df["store"] == "東京店"
0 True
1 True
2 True
3 False
4 False
...
265 False
266 False
267 False
268 False
269 False
Name: store, Length: 270, dtype: bool
この条件をDataFrameの[]に入れると、Trueの行だけを抽出できます。
次のコードでは、東京店の行だけを取り出してdf_tokyoに代入します。
df_tokyo = df[df["store"] == "東京店"]
df_tokyo.head(10)
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38.0 | 400 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24.0 | 450 |
| 2 | 2026-04-01 | 東京店 | ケーキ | 19.0 | 500 |
| 9 | 2026-04-02 | 東京店 | コーヒー | 38.0 | 400 |
| 10 | 2026-04-02 | 東京店 | 紅茶 | 25.0 | 450 |
| 11 | 2026-04-02 | 東京店 | ケーキ | 14.0 | 500 |
| 18 | 2026-04-03 | 東京店 | コーヒー | 33.0 | 400 |
| 19 | 2026-04-03 | 東京店 | 紅茶 | 25.0 | 450 |
| 20 | 2026-04-03 | 東京店 | ケーキ | 19.0 | 500 |
| 27 | 2026-04-04 | 東京店 | コーヒー | 29.0 | 400 |
取り出したDataFrameのインデックスは、元となったDataFrameのインデックス値のままで、間が飛ばされていることがわかります。
そのため、df_tokyoではlocとilocに同じ数字を指定しても、違う行が選ばれます。
locではインデックスが9の行が選ばれます。
df_tokyo.loc[9]
date 2026-04-02 00:00:00
store 東京店
item コーヒー
quantity 38.0
price 400
Name: 9, dtype: object
ilocでは上から10番目(0から数えて9番目)の行が選ばれます。
df_tokyo.iloc[9]
date 2026-04-04 00:00:00
store 東京店
item コーヒー
quantity 29.0
price 400
Name: 27, dtype: object
5.6. データを整形する#
分析しやすいように、データの並び順や値を整えます。
sort_values()メソッドは、指定した列の値で行を並べ替えます。
デフォルトでは昇順で並べ替えが行われ、ascending=Falseを指定すると降順に並べ替えます。
次のコードでは、販売数の降順(多い順)に並べ替えを行い、その先頭5行を表示します。
df.sort_values("quantity", ascending=False).head()
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 99 | 2026-04-12 | 東京店 | コーヒー | 43.0 | 400 |
| 126 | 2026-04-15 | 東京店 | コーヒー | 43.0 | 400 |
| 243 | 2026-04-28 | 東京店 | コーヒー | 43.0 | 400 |
| 261 | 2026-04-30 | 東京店 | コーヒー | 42.0 | 400 |
| 117 | 2026-04-14 | 東京店 | コーヒー | 42.0 | 400 |
並べ替えた結果を変数に代入していないため、元のdfの並び順は変わりません。
欠損値の処理#
値が入っていないことを欠損値と呼びます。 実際のデータでは、記録漏れなどで欠損値が含まれることがよくあります。 欠損値があると計算の結果が正しくならないため、分析の前に確認して対処します。
isna()メソッドは、値が欠損値かどうかをTrueまたはFalseで返します。
続けてsum()メソッドを使うと、列ごとに欠損値の数を数えられます。
なお、isnull()メソッドはisna()メソッドと同じ働きをします。
df.isna().sum()
date 0
store 0
item 0
quantity 2
price 0
dtype: int64
quantityの列に欠損値があります。
欠損値のある行を取り出して確認します。
df[df["quantity"].isna()]
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 23 | 2026-04-03 | 大阪店 | ケーキ | NaN | 500 |
| 69 | 2026-04-08 | 名古屋店 | コーヒー | NaN | 400 |
欠損値はNaNと表示されます。
NaNはNot a Numberの略で非数を表します。
並べ替えの結果でquantityが40.0のように小数で表示されていたのは、欠損値があるためです。
欠損値を含む列は、整数の列でも小数の型になります。
欠損値の対処には、欠損値が存在する行を削除する方法と、欠損値をほかの値に置き換える方法があります。
dropna()メソッドは、欠損値のある行を削除します。
ここでは行の数が減ることを確かめるだけで、元のdfは変えません。
df.dropna().shape
(268, 5)
ここでは、販売数の欠損値を0に置き換えます。
fillna()メソッドで欠損値を0に置き換え、その結果をquantity列に代入します。
df["quantity"] = df["quantity"].fillna(0)
注釈
書籍やWebの記事には、df["quantity"].fillna(0, inplace=True)のようにinplace=Trueを指定する例があります。
pandas 3.0では、この書き方では元のdfが変わりません。
この節のように、結果を列に代入してください。
欠損値がなくなったので、astype()メソッドでquantity列を整数(int)型に変換します。
df["quantity"] = df["quantity"].astype(int)
欠損値だった2つの行をlocで指定し、quantity列の値が0に置き換わったことを確かめます。
df.loc[[23, 69]]
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 23 | 2026-04-03 | 大阪店 | ケーキ | 0 | 500 |
| 69 | 2026-04-08 | 名古屋店 | コーヒー | 0 | 400 |
最後に、販売数と単価を掛けて、売上金額の列(amount)をDataFrameに追加します。
列どうしの計算は全ての行に対して行われ、全てのamount列に計算結果が入ります。
df["amount"] = df["quantity"] * df["price"]
df.head()
| date | store | item | quantity | price | amount | |
|---|---|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38 | 400 | 15200 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24 | 450 | 10800 |
| 2 | 2026-04-01 | 東京店 | ケーキ | 19 | 500 | 9500 |
| 3 | 2026-04-01 | 大阪店 | コーヒー | 35 | 400 | 14000 |
| 4 | 2026-04-01 | 大阪店 | 紅茶 | 20 | 450 | 9000 |
5.7. データを連結する#
ここでは、複数のDataFrameを1つにまとめる連結について説明します。
「売上データを読み込む」でダウンロードした翌月の売上(sales_next_month.csv)を読み込みdf_next_monthに代入します。
CSVファイルには2026年5月1日の売上が3行入っています。
df_next_month = pd.read_csv("sales_next_month.csv", parse_dates=["date"])
df_next_month
| date | store | item | quantity | price | |
|---|---|---|---|---|---|
| 0 | 2026-05-01 | 東京店 | コーヒー | 36 | 400 |
| 1 | 2026-05-01 | 大阪店 | 紅茶 | 19 | 450 |
| 2 | 2026-05-01 | 名古屋店 | ケーキ | 12 | 500 |
FileNotFoundErrorと表示された場合は、「売上データを読み込む」と同じく、ファイルの場所と名前を確認してください。
pd.concat()関数にDataFrameのリストを渡すと、指定されたDataFrameを行方向に連結します。
ignore_index=Trueを指定すると、インデックスを0から振り直します。
pd.concat([df, df_next_month], ignore_index=True).tail()
| date | store | item | quantity | price | amount | |
|---|---|---|---|---|---|---|
| 268 | 2026-04-30 | 名古屋店 | 紅茶 | 17 | 450 | 7650.0 |
| 269 | 2026-04-30 | 名古屋店 | ケーキ | 11 | 500 | 5500.0 |
| 270 | 2026-05-01 | 東京店 | コーヒー | 36 | 400 | NaN |
| 271 | 2026-05-01 | 大阪店 | 紅茶 | 19 | 450 | NaN |
| 272 | 2026-05-01 | 名古屋店 | ケーキ | 12 | 500 | NaN |
売上の後ろに翌月の売上の3行が連結されました。
翌月の売上にはamountの列がないため、amountは欠損値になります。
この結果は後の節では使わないため、代入していません。
axis=1を指定すると、DataFrameを列方向に連結します。
例として、売上の先頭の3行をdateとstoreの列、itemとquantityの列の2つのDataFrameに分けます。
left = df[["date", "store"]].head(3)
left
| date | store | |
|---|---|---|
| 0 | 2026-04-01 | 東京店 |
| 1 | 2026-04-01 | 東京店 |
| 2 | 2026-04-01 | 東京店 |
right = df[["item", "quantity"]].head(3)
right
| item | quantity | |
|---|---|---|
| 0 | コーヒー | 38 |
| 1 | 紅茶 | 24 |
| 2 | ケーキ | 19 |
2つのDataFrameを横に連結すると、元の4列の表に戻ります。
pd.concat([left, right], axis=1)
| date | store | item | quantity | |
|---|---|---|---|---|
| 0 | 2026-04-01 | 東京店 | コーヒー | 38 |
| 1 | 2026-04-01 | 東京店 | 紅茶 | 24 |
| 2 | 2026-04-01 | 東京店 | ケーキ | 19 |
5.8. データを集計する#
集計は、データをグループごとにまとめて、合計や平均などを計算することです。
groupby()メソッドに列名を指定すると、その列の値ごとにグループに分けます。
次のコードでは、店舗ごとにグループ化し、売上金額(amount)をsum()メソッドで合計します。
df.groupby("store")["amount"].sum()
store
名古屋店 641850
大阪店 815050
東京店 988500
Name: amount, dtype: int64
5.9. さまざまなファイル形式の読み込みと書き出し#
pandasは、CSVファイルのほかにもさまざまなファイル形式の読み込みと書き出しに対応しています。
読み込みにはpd.read_csv()関数のようなread_からはじまるpandasの関数を、書き出しにはto_csv()メソッドのようなto_からはじまるDataFrameのメソッドを使います。
ファイル形式によっては、pandasのほかに追加のライブラリのインストールが必要です。
ファイル形式 |
読み込み |
書き出し |
追加のライブラリ |
|---|---|---|---|
CSV |
|
|
不要 |
Excel |
|
|
openpyxl |
JSON |
|
|
不要 |
Parquet |
|
|
pyarrow |
HTML |
|
|
読み込みにlxmlなど |
最後に、加工したデータをto_csv()メソッドでCSVファイルに書き出します。
このファイルは第6章でグラフを作るときに使います。
df.to_csv("sales_clean.csv", index=False)
作業フォルダにsales_clean.csvができます。
この章のコードを実行できなかった場合は、sales_clean.csvをダウンロードして作業フォルダに置いてください。
5.10. この章のまとめ#
この章では以下を行いました。
節 |
行ったこと |
使った関数・メソッドなど |
|---|---|---|
SeriesとDataFrame |
SeriesとDataFrameの作成、インデックスと列名の確認 |
|
CSVファイルの書き出しと読み込み |
DataFrameのCSVファイルへの書き出しと読み込み |
|
売上データを読み込む |
売上データの読み込みと概要の確認 |
|
データを抽出する |
さまざまな方法で指定した列や行を抽出 |
|
データを整形する |
並べ替え、欠損値の処理、列の追加 |
|
データを連結する |
DataFrameを行方向または列方向に連結 |
|
データを集計する |
店舗ごとの売上金額の集計 |
|
さまざまなファイル形式の読み込みと書き出し |
ファイル形式ごとの読み込みと書き出しの関数・メソッドの確認 |
|
次の第6章では、書き出したsales_clean.csvを使って、Matplotlibでさまざまなグラフを描画します。