5. pandasでデータ加工(45分)#

この章では、pandasを使って表形式のデータを読み込み、加工し、集計する流れを体験します。

注釈

入力が遅れた場合などに、この章のNotebookをダウンロードできます。ダウンロードしたファイルを作業フォルダに置き、JupyterLabのファイルブラウザでダブルクリックして開いてください。 ダウンロードしたNotebookには、この章の説明の文章も入っています。図などの一部の書き方は、JupyterLabでは記号のまま表示されます。

自分で作った5_pandas.ipynbと同じ名前のため、上書きしないように注意してください。

5.1. pandasとは#

pandasはオープンソースのデータ分析と加工のためのライブラリです。 表形式のデータを読み込み、加工、集計といった作業ができます。 Excelのような行と列を持つ表のデータを、Pythonのコードで扱えます。 CSVファイルなどからデータを読み込み、必要な行や列を取り出したり、抜けている値を補ったり、複数の表をつなげたり、グループごとに集計したりできます。

第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の画面

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日までについて、店舗ごと、商品ごとの毎日の販売数を記録したデータです(実在の店舗とは関係ありません)。 データには次の列があります。

売上データの列#

列名

内容

date

日付

store

店舗

item

商品

quantity

販売数

price

単価

次の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)でデータの期間が分かります。

describe()メソッドの出力の意味#

項目

意味

count

データの件数(欠損値(後述)などは件数から除外される)

mean

平均値

min

最小値

25%

小さい方から25%の位置の値

50%

中央値(小さい方から50%の位置の値)

75%

小さい方から75%の位置の値

max

最大値

std

標準偏差(値のばらつきの大きさ)

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

pd.read_csv()

to_csv()

不要

Excel

pd.read_excel()

to_excel()

openpyxl

JSON

pd.read_json()

to_json()

不要

Parquet

pd.read_parquet()

to_parquet()

pyarrow

HTML

pd.read_html()

to_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の作成、インデックスと列名の確認

pd.Series()、pd.DataFrame()、index、columns

CSVファイルの書き出しと読み込み

DataFrameのCSVファイルへの書き出しと読み込み

to_csv()、pd.read_csv()

売上データを読み込む

売上データの読み込みと概要の確認

pd.read_csv()、head()、tail()、describe()、shape

データを抽出する

さまざまな方法で指定した列や行を抽出

[]、loc、iloc

データを整形する

並べ替え、欠損値の処理、列の追加

sort_values()、isna()、dropna()、fillna()、astype()

データを連結する

DataFrameを行方向または列方向に連結

pd.concat()

データを集計する

店舗ごとの売上金額の集計

groupby()、sum()

さまざまなファイル形式の読み込みと書き出し

ファイル形式ごとの読み込みと書き出しの関数・メソッドの確認

pd.read_excel()、to_excel()など

次の第6章では、書き出したsales_clean.csvを使って、Matplotlibでさまざまなグラフを描画します。