CSVを読み込んだあと、列の値が 1、2、A、B のようなコードで入っていて、「この数字や記号が何を意味しているのか分かりにくい」と感じることがあります。
たとえば、次のようなデータです。
| gender_code | member_rank | 意味 |
|---|---|---|
| 1 | A | 男性・通常会員 |
| 2 | B | 女性・優良会員 |
| 3 | D | 対応表にまだ登録していないコード |
このまま value_counts() や groupby() で集計することもできますが、結果が 1 や A のままだと読み取りにくくなります。
そこで使いやすいのが、Pandasの map() です。
map() は、1列の値を、辞書などの対応表に従って別の値へ変換したいときに使います。
この記事では、pandas map()の使い方を、辞書で値を変換する基本から、新しい列を作る方法、辞書にない値が NaN になる注意点まで、初心者向けに整理します。
- まず結論:map()は「対応表で1列を変換する」ときに使う
- この記事でわかること
- Pandas DataFrame入門シリーズでの位置づけ
- map()はどんな場面で使うのか
- map()と似た機能の使い分け
- サンプルデータを作成する
- 変換前に値の種類を確認する
- 辞書を使って数値コードをラベルに変換する
- map()で新しい列を作る
- 辞書にない値がNaNになる例
- fillna()で未対応コードを補う
- 文字列のコードをカテゴリ名に変換する
- map()とreplace()の違い
- map()とastype()・merge()・apply()の違い
- 文字列の「1」と数値の1は別物として扱われる
- map()後に集計へ進む
- よくあるつまずきと確認ポイント
- まとめ
- 次に読みたい関連記事
まず結論:map()は「対応表で1列を変換する」ときに使う
結論
pandasのmap()は、DataFrameの1列に入っている値を、辞書などの対応表に従って別の値へ変換したいときに使います。
たとえば、1を男性、2を女性、Aを通常会員のように、コード値を人が読めるラベルへ変換するときに便利です。
この記事では、まず辞書を使った基本形に絞って説明します。
難しい関数処理や速度比較には深入りせず、CSVを読み込んだあとに実際によく使う前処理として、map() の使いどころを整理します。
この記事でわかること
map()がどんな場面で役立つか- 辞書を使って値を変換する基本
- 数値コードや文字列コードをラベルに変換する方法
map()で新しい列を作る方法- 辞書にない値が
NaNになる理由と確認方法 map()とreplace()、astype()、merge()の違い- 集計前の前処理として
map()を使う流れ
Pandas DataFrame入門シリーズでの位置づけ
map() は、DataFrameを集計・可視化する前に、値を読みやすく整えるための前処理でよく使います。
流れとしては、次の位置づけです。
| 流れ | よく使う処理 | 役割 |
|---|---|---|
| 読み込み | read_csv() |
CSVをDataFrameとして読み込む |
| 確認 | head() / info() |
データの中身や型を確認する |
| 値の確認 | value_counts() |
どんな値が入っているか確認する |
| 値の変換 | map() / replace() |
コード値や表記ゆれを整える |
| 欠損値処理 | fillna() |
足りない値を補う |
| 型変換 | astype() |
数値・文字列などの型を整える |
| 集計 | groupby() |
整えた列を使って集計する |
DataFrameの基本から確認したい場合は、Pandas DataFrame入門 もあわせて確認すると流れがつかみやすくなります。
map()はどんな場面で使うのか
結論からいうと、map() は 1列の値を、対応表に従って別の値へ変換したいときに使います。
たとえば、次のような変換です。
| 変換前 | 変換後 |
|---|---|
1 |
男性 |
2 |
女性 |
A |
通常会員 |
B |
優良会員 |
C |
VIP |
map() は、特に次のような場面で便利です。
- 数値コードをわかりやすいラベルに変換したい
A、B、Cのようなランクコードを名前に変えたい- 既存列をもとに、新しい説明用の列を作りたい
groupby()やグラフ化の前に、集計結果を読みやすくしたい
ここで大事なのは、map() は基本的に DataFrame全体ではなく、1列、つまりSeriesに使うという点です。
map()と似た機能の使い分け
Pandasには、値を変える処理がいくつかあります。
ここで先に整理しておくと、map() を使う場面がわかりやすくなります。
| やりたいこと | 向いている方法 | 例 | 初心者向けの判断基準 |
|---|---|---|---|
| 1列の値を対応表どおりに変換したい | map() |
1 → 男性、A → 通常会員 |
コード値をラベルに変えたいとき |
| 表記ゆれを置換したい | replace() |
Tokyo と tokyo を 東京 にそろえる |
既存値の表記をそろえたいとき |
| 欠損値を埋めたい | fillna() |
NaN → 未分類 |
足りない値を補いたいとき |
| データ型を変えたい | astype() |
文字列の "100" を数値の 100 にする |
値の意味ではなく型を変えたいとき |
| 別DataFrameの対応表と結合したい | merge() |
商品マスタと売上データを結合する | 対応表が別の表としてあるとき |
| 複雑な条件や関数を使いたい | apply() |
複数条件で判定する | 単純な対応表では足りないとき |
この記事では、まず初心者が使いやすい 辞書を使った map() に絞って説明します。
表記ゆれの置換を詳しく知りたい場合は、pandas replace()の使い方 を参考にしてください。
型変換を確認したい場合は、pandas astype()の使い方 が近い内容です。
サンプルデータを作成する
ここでは、会員データを例にします。
gender_code には性別コード、member_rank には会員ランクのコードが入っているとします。
また、あえて対応表にない値も入れて、map() でよくあるつまずきを確認できるようにします。
import pandas as pd
data = {
"customer_id": [101, 102, 103, 104, 105, 106],
"gender_code": [1, 2, 1, 3, 2, 1],
"member_rank": ["A", "B", "C", "A", "D", "B"],
"purchase_amount": [1200, 3500, 5800, 900, 1500, 4200]
}
df = pd.DataFrame(data)
df
| customer_id | gender_code | member_rank | purchase_amount | |
|---|---|---|---|---|
| 0 | 101 | 1 | A | 1200 |
| 1 | 102 | 2 | B | 3500 |
| 2 | 103 | 1 | C | 5800 |
| 3 | 104 | 3 | A | 900 |
| 4 | 105 | 2 | D | 1500 |
| 5 | 106 | 1 | B | 4200 |
このデータでは、gender_code の 1 や 2、member_rank の A や B は、そのままだと意味が伝わりにくい状態です。
| 列名 | 今の状態 | 変換したい内容 |
|---|---|---|
gender_code |
1、2、3 |
男性、女性、未対応など |
member_rank |
A、B、C、D |
通常会員、優良会員、VIP、未分類など |
このようなときに、map() で対応表を使って変換します。
変換前に値の種類を確認する
map() を使う前に、まずどんな値が入っているかを確認しておくと安全です。
特に、辞書にない値があると NaN になることがあるため、変換前に value_counts() で確認しておくと失敗に気づきやすくなります。
print("gender_code の値の種類")
display(df["gender_code"].value_counts(dropna=False))
print("member_rank の値の種類")
display(df["member_rank"].value_counts(dropna=False))
gender_code の値の種類
| count | |
|---|---|
| 1 | 3 |
| 2 | 2 |
| 3 | 1 |
member_rank の値の種類
| count | |
|---|---|
| A | 2 |
| B | 2 |
| C | 1 |
| D | 1 |
ここで、gender_code には 1、2、3 があり、member_rank には A、B、C、D があることがわかります。
この確認をせずに変換すると、「辞書に入れ忘れた値」があとから NaN として出てきて、原因がわかりにくくなることがあります。
value_counts() の基本は、pandas value_counts()の使い方 でも詳しく解説しています。
辞書を使って数値コードをラベルに変換する
まずは、gender_code の 1 と 2 を、それぞれ 男性、女性 に変換します。
map() では、次のように「変換前の値」と「変換後の値」を辞書で用意します。
gender_map = {
1: "男性",
2: "女性"
}
df["gender_code"].map(gender_map)
| gender_code | |
|---|---|
| 0 | 男性 |
| 1 | 女性 |
| 2 | 男性 |
| 3 | NaN |
| 4 | 女性 |
| 5 | 男性 |
結果を見ると、1 は 男性、2 は 女性 に変換されています。
一方で、3 は辞書に含まれていないため、NaN になっています。
ここが map() で初心者がつまずきやすいポイントです。
| 元の値 | 辞書にあるか | map()後の値 |
|---|---|---|
1 |
ある | 男性 |
2 |
ある | 女性 |
3 |
ない | NaN |
NaN は必ずしもエラーではありません。
「対応表にない値があった」というサインとして確認することが大切です。
map()で新しい列を作る
初心者のうちは、元の列をいきなり上書きするより、新しい列として追加するほうが安全です。
たとえば、gender_code はそのまま残し、変換後の値を gender_label という新しい列に入れます。
df["gender_label"] = df["gender_code"].map(gender_map)
df
| customer_id | gender_code | member_rank | purchase_amount | gender_label | |
|---|---|---|---|---|---|
| 0 | 101 | 1 | A | 1200 | 男性 |
| 1 | 102 | 2 | B | 3500 | 女性 |
| 2 | 103 | 1 | C | 5800 | 男性 |
| 3 | 104 | 3 | A | 900 | NaN |
| 4 | 105 | 2 | D | 1500 | 女性 |
| 5 | 106 | 1 | B | 4200 | 男性 |
新しい列に入れると、変換前と変換後を横に並べて確認できます。
| 方法 | 元データを残せるか | 初心者におすすめか | 確認しやすさ |
|---|---|---|---|
| 既存列を上書きする | 残らない | 慣れてから | 変換前を確認しにくい |
| 新しい列を追加する | 残る | おすすめ | 変換前後を比べやすい |
上書き自体が悪いわけではありません。
ただし、最初は gender_label のような新しい列を作るほうが、ミスに気づきやすくなります。
新しい列の作り方を広く確認したい場合は、pandasで新しい列を追加する方法 も参考になります。
辞書にない値がNaNになる例
map() では、辞書にない値は元の値のまま残るのではなく、NaN になることがあります。
この性質は便利でもあります。
なぜなら、「対応表に入っていない値がある」と気づけるからです。
print("gender_label の欠損数")
display(df["gender_label"].isnull().sum())
print("gender_label の値の種類")
display(df["gender_label"].value_counts(dropna=False))
gender_label の欠損数
np.int64(1)
gender_label の値の種類
| count | |
|---|---|
| 男性 | 3 |
| 女性 | 2 |
| NaN | 1 |
この結果から、gender_label に NaN があることが確認できます。
今回のサンプルでは、gender_code の 3 を辞書に入れていないため、NaN になっています。
つまり、3 は「欠損値そのもの」ではなく、対応表にまだ登録していないコードとして扱います。
このように、map() 後は次の確認をすると安心です。
isnull().sum()でNaNの数を確認するvalue_counts(dropna=False)で変換後の値を確認する- 必要に応じて、辞書に値を追加するか、
fillna()で補う
欠損値の確認や考え方は、Pandas info()とdescribe()の違い や pandas fillna()の使い方 ともつながります。
fillna()で未対応コードを補う
辞書にない値を NaN のままにしたくない場合は、fillna() で補う方法があります。
今回は、gender_code の 3 を「未対応」として表示します。
df["gender_label"] = df["gender_code"].map(gender_map).fillna("未対応")
df
| customer_id | gender_code | member_rank | purchase_amount | gender_label | |
|---|---|---|---|---|---|
| 0 | 101 | 1 | A | 1200 | 男性 |
| 1 | 102 | 2 | B | 3500 | 女性 |
| 2 | 103 | 1 | C | 5800 | 男性 |
| 3 | 104 | 3 | A | 900 | 未対応 |
| 4 | 105 | 2 | D | 1500 | 女性 |
| 5 | 106 | 1 | B | 4200 | 男性 |
fillna("未対応") を使うことで、辞書にない値をわかりやすく表示できました。
ただし、すべての NaN を機械的に「未対応」にしてよいとは限りません。
本当に未対応コードなのか、入力ミスなのか、欠損値なのかは、データの意味を確認して判断する必要があります。
文字列のコードをカテゴリ名に変換する
次に、member_rank の A、B、C を会員ランク名に変換します。
ここでも、元の member_rank を残し、変換後の値を rank_label という新しい列にします。
rank_map = {
"A": "通常会員",
"B": "優良会員",
"C": "VIP"
}
df["rank_label"] = df["member_rank"].map(rank_map)
df
| customer_id | gender_code | member_rank | purchase_amount | gender_label | rank_label | |
|---|---|---|---|---|---|---|
| 0 | 101 | 1 | A | 1200 | 男性 | 通常会員 |
| 1 | 102 | 2 | B | 3500 | 女性 | 優良会員 |
| 2 | 103 | 1 | C | 5800 | 男性 | VIP |
| 3 | 104 | 3 | A | 900 | 未対応 | 通常会員 |
| 4 | 105 | 2 | D | 1500 | 女性 | NaN |
| 5 | 106 | 1 | B | 4200 | 男性 | 優良会員 |
A、B、C は変換できましたが、D は辞書にないため NaN になっています。
ここでも、変換前後を確認しておきます。
print("rank_label の値の種類")
display(df["rank_label"].value_counts(dropna=False))
print("rank_label の欠損数")
display(df["rank_label"].isnull().sum())
rank_label の値の種類
| count | |
|---|---|
| 通常会員 | 2 |
| 優良会員 | 2 |
| VIP | 1 |
| NaN | 1 |
rank_label の欠損数
np.int64(1)
D を未分類として扱うなら、fillna("未分類") のように補うことができます。
df["rank_label"] = df["member_rank"].map(rank_map).fillna("未分類")
df
| customer_id | gender_code | member_rank | purchase_amount | gender_label | rank_label | |
|---|---|---|---|---|---|---|
| 0 | 101 | 1 | A | 1200 | 男性 | 通常会員 |
| 1 | 102 | 2 | B | 3500 | 女性 | 優良会員 |
| 2 | 103 | 1 | C | 5800 | 男性 | VIP |
| 3 | 104 | 3 | A | 900 | 未対応 | 通常会員 |
| 4 | 105 | 2 | D | 1500 | 女性 | 未分類 |
| 5 | 106 | 1 | B | 4200 | 男性 | 優良会員 |
これで、会員ランクのコードを読みやすいラベルに変換できました。
| 変換前の列 | 変換後の列 | 役割 |
|---|---|---|
gender_code |
gender_label |
性別コードをラベルに変換 |
member_rank |
rank_label |
会員ランクコードをラベルに変換 |
このように、map() は「コードの意味を人が読める形にする」前処理で役立ちます。
map()とreplace()の違い
迷ったら、コード値をラベルに変えるなら map()、表記ゆれを直すなら replace() と考えるとわかりやすいです。
map() と replace() はどちらも値を変える処理なので、混同しやすいです。
大きく分けると、次のように考えるとわかりやすいです。
| 比較 | map() |
replace() |
|---|---|---|
| 主な使いどころ | 対応表に従って1列を変換する | 表記ゆれや特定の値を置換する |
| 辞書にない値 | NaN になりやすい |
元の値が残ることが多い |
| 例 | 1 → 男性、A → 通常会員 |
tokyo → 東京、なし → NaN |
| 初心者向けの判断 | コード値をラベルにしたいならこちら | 表記ゆれを直したいならこちら |
違いを確認するために、簡単な例を見てみます。
s = pd.Series([1, 2, 3])
mapping = {
1: "男性",
2: "女性"
}
print("map() の結果")
display(s.map(mapping))
print("replace() の結果")
display(s.replace(mapping))
map() の結果
| 0 | |
|---|---|
| 0 | 男性 |
| 1 | 女性 |
| 2 | NaN |
replace() の結果
| 0 | |
|---|---|
| 0 | 男性 |
| 1 | 女性 |
| 2 | 3 |
この例では、3 が辞書にありません。
map()では、3がNaNになりますreplace()では、3がそのまま残ります
そのため、対応表にない値を見つけたい場合は map() が気づきやすいことがあります。
一方で、表記ゆれを一部だけ直したい場合は replace() のほうが自然なこともあります。
replace() の詳しい使い方は、pandas replace()の使い方 で確認できます。
map()とastype()・merge()・apply()の違い
map() と似て見える処理も、目的が違います。
map()とastype()の違い
astype() は、値の意味を変えるのではなく、データ型を変えるためのメソッドです。
たとえば、文字列の "100" を数値の 100 に変えるような場面では astype() を使います。
一方、1 を 男性 に変えるように、値の意味をラベルへ変えるなら map() が向いています。
map()とmerge()の違い
小さな対応表を辞書で持てるなら、map() がシンプルです。
一方で、商品マスタ、店舗マスタ、顧客マスタのように、対応表が別のDataFrameとしてある場合は merge() が向いています。
本格的な表結合を学ぶ場合は、pandas mergeの使い方 が近い内容です。
map()とapply()の違い
単純な対応変換なら、まずは map() で十分なことが多いです。
apply() は、複雑な関数を使いたいときに便利ですが、この記事では深入りしません。
初心者のうちは、まず「辞書で対応変換するなら map()」と覚えると迷いにくくなります。
文字列の「1」と数値の1は別物として扱われる
map() では、辞書のキーと列の値が一致している必要があります。
たとえば、列の値が文字列の "1" なのに、辞書のキーが数値の 1 だと、うまく変換されないことがあります。
df_type = pd.DataFrame({
"gender_code": ["1", "2", "1", "3"]
})
gender_map_number_key = {
1: "男性",
2: "女性"
}
df_type["label_ng"] = df_type["gender_code"].map(gender_map_number_key)
df_type
| gender_code | label_ng | |
|---|---|---|
| 0 | 1 | NaN |
| 1 | 2 | NaN |
| 2 | 1 | NaN |
| 3 | 3 | NaN |
gender_code は見た目は 1 や 2 ですが、実際には文字列です。
そのため、数値の 1、2 をキーにした辞書では対応できず、NaN になります。
この場合は、辞書のキーを文字列にそろえるか、列の型を変換します。
gender_map_string_key = {
"1": "男性",
"2": "女性"
}
df_type["label_ok"] = df_type["gender_code"].map(gender_map_string_key).fillna("未対応")
df_type
| gender_code | label_ng | label_ok | |
|---|---|---|---|
| 0 | 1 | NaN | 男性 |
| 1 | 2 | NaN | 女性 |
| 2 | 1 | NaN | 男性 |
| 3 | 3 | NaN | 未対応 |
このように、map() でうまく変換できないときは、次の点を確認しましょう。
- 列の値が数値なのか文字列なのか
- 辞書のキーと列の値の型が合っているか
- 辞書にない値が含まれていないか
- 変換後に
NaNが増えていないか
型の確認や変換は、pandas astype()の使い方 とつながります。
map()後に集計へ進む
map() は、変換して終わりではありません。
読みやすいラベルに整えたあと、groupby() や可視化につなげると、分析結果がわかりやすくなります。
たとえば、会員ランク別に購入金額を集計してみます。
rank_summary = (
df.groupby("rank_label", as_index=False)["purchase_amount"]
.sum()
.rename(columns={"purchase_amount": "total_purchase_amount"})
)
rank_summary
| rank_label | total_purchase_amount | |
|---|---|---|
| 0 | VIP | 5800 |
| 1 | 優良会員 | 7700 |
| 2 | 未分類 | 1500 |
| 3 | 通常会員 | 2100 |
member_rank の A、B、C、D のまま集計するより、通常会員、優良会員、VIP、未分類 のように表示されたほうが、結果を読み取りやすくなります。
このように、map() は次の流れの中で役立ちます。
- CSVを読み込む
head()やinfo()で確認するvalue_counts()で値の種類を見るmap()でコード値をラベルに変換する- 必要に応じて
fillna()やastype()で整える groupby()やグラフで集計・可視化する
集計の基本は Pandas groupby×aggの使い方 に進むと理解しやすくなります。
グラフ化まで進めたい場合は、Matplotlib 棒グラフ入門 も関連します。
よくあるつまずきと確認ポイント
map() でうまく変換できないときは、次のポイントを確認してください。
| つまずき | 原因になりやすいこと | 確認方法 |
|---|---|---|
変換後にNaNが出る |
辞書にない値がある | value_counts(dropna=False) |
すべてNaNになる |
値の型と辞書のキーの型が違う | info() や astype() |
| 元の値を残したい | 既存列を上書きしている | 新しい列に追加する |
| 表記ゆれ修正をしたい | map()よりreplace()が向く場合がある |
変換目的を確認する |
| 別表の情報を付けたい | map()よりmerge()が向く場合がある |
対応表がDataFrameか確認する |
特に大切なのは、変換前と変換後の確認です。
map() のコードだけを見るのではなく、変換前後の表を見比べるとミスに気づきやすくなります。
まとめ
この記事では、pandas map() の使い方を、辞書で値を変換する前処理として解説しました。
重要なポイントは、次のとおりです。
map()は、1列の値を対応表に従って変換するときに使う- 数値コードや文字列コードを、読みやすいラベルへ変換できる
- 初心者のうちは、元の列を上書きせず、新しい列として追加すると安全
- 辞書にない値は
NaNになりやすい NaNが出たら、エラーと決めつけず、対応表にない値があるサインとして確認するmap()前後では、value_counts()やisnull().sum()で確認するとよい- 表記ゆれ修正は
replace()、型変換はastype()、別表との結合はmerge()と使い分ける map()で値を整えると、groupby()やグラフ化の結果が読みやすくなる
map() は派手な機能ではありませんが、CSVを読み込んだあとに「コード値を人が読める形に整える」ための大切な前処理です。
まずは、対応表で1列を変換する、コード値を辞書でラベルに変換するという使い方から覚えておくと、DataFrameの集計や可視化へ進みやすくなります。
次に読みたい関連記事
この記事の内容は、次の記事とつながっています。
- Pandas DataFrame入門|作り方・基本操作をわかりやすく解説
- Google Colab CSV 読み込み&保存入門
- Pandas head()の使い方|先頭の表示・行数指定を例で解説
- Pandas info()とdescribe()の違い|欠損値・型・統計量の見方を例で解説
- pandas value_counts()の使い方|件数集計・割合表示・欠損値の数え方を解説
- pandas replace()の使い方|値の置換・表記ゆれ・欠損値変換を解説
- pandasで新しい列を追加する方法|df[‘列名’]・assign・条件付き列追加を初心者向けに解説
- pandas fillna()の使い方|欠損値を0・平均値・中央値・最頻値で埋める方法を初心者向けに解説
- pandas astype()の使い方|文字列・数値への型変換とエラー対処を初心者向けに解説
- pandas mergeの使い方|DataFrame結合の違いと実例
- Pandas groupby×aggの使い方|基本の集計とaggの書き方を例で解説
- Matplotlib 棒グラフ入門|横棒・グループ化・積み上げまで解説
pandasのmap()は何をするメソッドですか?
map() は、Series、つまりDataFrameの1列の値を、辞書や関数などに従って別の値へ変換するメソッドです。
初心者向けには、まず「1列のコード値を対応表でラベルに変換する方法」と考えるとわかりやすいです。
map()とreplace()は何が違いますか?
map() は、対応表に従って1列を変換したいときに向いています。replace() は、表記ゆれや特定の値を置換したいときに向いています。
たとえば、1 → 男性、2 → 女性 のようなコード変換なら map() が自然です。
一方、tokyo、Tokyo、TOKYO を 東京 にそろえるような表記ゆれ修正なら replace() が向いています。
map()で辞書にない値がNaNになるのはなぜですか?
map() は、辞書にあるキーを使って値を変換します。
そのため、辞書にない値は対応先が見つからず、NaN になることがあります。
これはエラーとは限らず、「対応表に登録していない値がある」というサインです。value_counts(dropna=False) や isnull().sum() で確認しましょう。
map()で新しい列を作るにはどうすればよいですか?
次のように、変換結果を新しい列に代入します。df["gender_label"] = df["gender_code"].map(gender_map)
初心者のうちは、元の列を上書きするより、新しい列を作って変換前後を確認する方法がおすすめです。
数値の1と文字列の”1″でmap()の結果は変わりますか?
変わることがあります。map() では、列の値と辞書のキーが一致している必要があります。
列の値が文字列の "1" なら、辞書のキーも "1" にする必要があります。
数値の 1 をキーにした辞書では対応できず、NaN になることがあります。
map()とastype()は何が違いますか?
map() は、値の意味を別の値へ変換するために使います。astype() は、データ型を変えるために使います。
たとえば、1 → 男性 は map()、"100" → 100 のような型変換は astype() と考えるとわかりやすいです。
map()とmerge()はどう使い分けますか?
小さな対応表を辞書で書けるなら、map() がシンプルです。
一方、対応表が別のDataFrameとしてある場合や、複数列をもとに結合したい場合は merge() が向いています。
map()したあとに結果を確認するにはどうすればよいですか?
次のような方法で確認できます。df.head() で変換前後の列を確認するvalue_counts(dropna=False) で変換後の値の種類を確認するisnull().sum() で NaN の数を確認するmap() は変換して終わりではなく、変換後の確認までセットで行うと安全です。
コメント