CHAPTER 6 ・ pandas を一歩進める / Lesson 26

条件で区分けする(where・map・cut)

金額帯のランク付け、コードを名前に変換、条件で値を入れ替え。IFやVLOOKUPの置き換えです。

目安 22分 ・ インストール不要

このレッスンが終わったら

「条件で列を作るには np.where、対応表の変換には map、区間分けには cut。1行ずつ回す apply は最後の手段」と言える。

STEP 1

こんな場面、ありませんか?

売上明細の1行ずつに、金額に応じたランク(A・B・C)を付けたい。Excelなら、IF を入れ子にして、列を足す作業です。基礎編の for と if で書くと、こうなります。ここでは、30万円以上を A、10万円以上を B、それ未満を C とします。

基礎編の書き方:for と if でランクを付けるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

A が19件、B が30件、C が79件と、正しく動きます。ただ、「空のリストを作る → 1行ずつ回す → 追加する → 列にする」という手順を、毎回書くことになります。pandas には、列全体に一度で条件を当てる道具があります。この回では、その使い分けを学びます。

STEP 2

列全体に、条件を当てる

2通りに分けるなら np.where、3通り以上なら np.select です。numpy(np)は、pandas の内側で使われている、数値計算の道具です。pandas と一緒に、そのまま使えます。

まず、「30万円以上なら大口、そうでなければ通常」の2通りです。Excelの =IF(条件, 値1, 値2) と同じ形です。

np.where:2通りに分けるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

次は、A・B・C の3通りです。条件を上から順に調べて、最初に当てはまったものを採用します。

np.select:3通り以上に分けるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

さきほどの for の書き方と、同じ件数になりました。6行が、1つの式になっています。

STEP 3

仕組みを読む

np.select を1つずつ日本語にしてみる
np.select(条件を上から順に調べて、列を作る
[df["金額"] >= 300000, df["金額"] >= 100000],条件のリスト。1つ目:30万円以上か。2つ目:10万円以上か
["A", "B"],それぞれが当てはまったときの値。条件と同じ順番に並べる
default="C",どの条件にも当てはまらなかったときの値
)全部の行に、一度に当てる

日本語にすると

金額が30万円以上なら A、そうでなくて10万円以上なら B、どちらでもなければ C にしてください。

条件は上から調べ、最初に当てはまったものが採用されます。45万円は、1つ目にも2つ目にも当てはまりますが、最初の A になります。順番を逆にすると、全部 B になってしまうので、順番に注意します。

やりたいことと、使う道具の対応は、次のとおりです。

やりたいこと使う道具
2通りに分ける(IF)np.where(条件, 値1, 値2)
3通り以上に分ける(IF の入れ子)np.select([条件, ...], [値, ...], default=値)
コードを名前に置き換える(VLOOKUP のかんたん版)列.map(辞書)
金額などを、区間で分けるpd.cut(列, bins=[...], labels=[...])
文字に含まれるか調べる/一部を取り出す列.str.contains(...) / 列.str.extract(...)
上のどれでもできないとき列.apply(関数)(最後の手段)
STEP 4

変えて、試す

① コードを名前に置き換える。 map は、辞書の「キー → 値」の対応表で、列の値を置き換えます。辞書にないコードは、NaN(欠損値)になります。

map:商品コードを商品名にするpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

最後の行は、辞書にない「P99」を試した結果で、nan になりました。対応表が小さいときは map が手軽です。付けたい列がいくつもあるときは、Lesson 23 の merge を使います。

② 金額を、区間で分ける。 pd.cut は、区切りの数字(bins)で区間を作り、名前(labels)を付けます。right=False は、「区切りの数字を、右の区間に入れる」指定です。(10万円ちょうどは B。30万円ちょうどは A)

pd.cut:金額をランクに分けるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

np.select と同じ結果です。区切りの数字を並べるだけなので、区間が多いときは pd.cut のほうが読みやすくなります。

③ 摘要から、請求番号を取り出す。 銀行の入金明細の「摘要」には、請求番号が混ざっています。str.extract で、パターンに合う部分だけを取り出せます(INV-2025-0001 の形を表す書き方は、Lesson 20 の正規表現です)。取り出せなかった行は NaN になるので、請求番号のない入金を見つけられます。

str.extract:摘要から請求番号を取り出すpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

str.contains("INV") は「INV を含むか」を調べます。10件中9件が含んでいて、1件は含んでいません。含まない1件は、「請求番号なし」の入金として、人が確認する対象です。

④ 複数の条件で、「要確認」のフラグを付ける。 経費の一覧に、確認が必要な行の目印と理由を付けます。np.select の条件を、確認したい順に並べます。

経費の「要確認」フラグpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

6件が見つかりました。ex["摘要"].isna() は、摘要が空欄の行です。1行が複数の条件に当てはまるときは、最初の条件の理由だけが付きます。理由を全部並べる作り方は、Lesson 38 で学びます。

STEP 5

自分で書く

お題① 経費の金額が、3,000円未満なら「小額」、10,000円未満なら「中額」、それ以外は「高額」とします。「高額」は何件でしょうか。

お題①:高額の経費の件数pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

np.select([ex["金額"] < 3000, ex["金額"] < 10000], ["小額", "中額"], default="高額") で、区分の列を作ります。件数は、(ex["区分"] == "高額").sum() で数えられます。

答えの例を見る
import numpy as np
import pandas as pd

ex = pd.read_csv("expenses.csv")
ex["区分"] = np.select(
  [ex["金額"] < 3000, ex["金額"] < 10000],
  ["小額", "中額"],
  default="高額",
)
print((ex["区分"] == "高額").sum())

お題② payments.csv の摘要から請求番号を取り出し、請求番号が取り出せなかった入金の、入金額の合計を表示してください。

お題②:請求番号のない入金の合計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

pay["摘要"].str.extract(r"(INV-\d{4}-\d{4})", expand=False) で請求番号を取り出し、.isna() が True の行を取り出します。その入金額の合計を .sum() で求めます。

答えの例を見る
import pandas as pd

pay = pd.read_csv("payments.csv")
pay["請求番号"] = pay["摘要"].str.extract(r"(INV-\d{4}-\d{4})", expand=False)
print(pay[pay["請求番号"].isna()]["入金額"].sum())

お題③ 経費のうち、「領収書がなし」または「摘要が空欄」の行は、何件でしょうか。(「または」は |、条件ごとに ( ) で囲みます)

お題③:領収書なし、または摘要が空欄pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

(ex["領収書"] == "なし") | ex["摘要"].isna() で、当てはまる行が True になります。True の数は .sum() で数えられます。

答えの例を見る
import pandas as pd

ex = pd.read_csv("expenses.csv")
flag = (ex["領収書"] == "なし") | ex["摘要"].isna()
print(flag.sum())
STEP 6

仕事ならどう使う?

理解チェック金額を A・B・C・D の4通りに分けたいとき、まず考える道具は、どれでしょう?

理解チェック商品コードの列に map(辞書) を使いました。辞書にないコード P99 があるとき、結果はどうなりますか?