結合の種類
このレッスンでできるようになること。merge の how を使い分け、売れていない商品も残した表を作れるようになります。SQL の OUTER JOIN にあたる話です。
前回の inner 結合では、片方にしか無い行が消えました。これは都合が良いこともあれば、致命的なこともあります。「今月1つも売れなかった商品」を知りたいとき、inner で結合するとその商品は結果に現れません。売れていない商品は明細に行が無いのですから、当然です。しかし現場で本当に見たいのは、まさにその売れていない商品だったりします。
how に何を渡すかで、どちらの表を基準にするかが決まります。
Python
left = sales.merge(products, on="商品名", how="left")
right = sales.merge(products, on="商品名", how="right")how="left" は左の表(ここでは明細)の行をすべて残します。マスタに無い商品名の行も消えず、定価が NaN になります。廃番や表記ゆれを見つけるのに向いています。SQL の LEFT OUTER JOIN と同じです。
how="right" は右の表(ここではマスタ)の行をすべて残します。1件も売れていない商品も行として残り、金額が NaN になります。売れ残りの棚卸しに使う形です。how="outer" にすれば両方を残します。
実務での使い分けは、次のように考えると迷いません。「分析の主語はどちらの表か」を決め、その表を残す側に置きます。売上を分析するなら明細が主語なので left、商品ラインナップを点検するならマスタが主語なので right です。
NaN の後始末も必要です。売れていない商品の金額は NaN のままだと合計に入らないので、fillna(0) で0にしてから集計します。ここで注意したいのは、fillna をすると列が float のままになることです。整数として扱いたいなら、返す直前に int() を通します。0で埋めるという判断そのものにも意味があります。「データが無い」と「0だった」は本来違うものですが、売上の文脈では0と読むのが自然な場面がほとんどです。何を0と決めたのかは、レポートに書き添えておくと親切です。
要件
- how="right" または how="outer" を使ってマスタの行を残す
- 鍵は on=["商品名", "カテゴリ"] にする
- 売上の無い商品の金額は fillna(0) などで0にする
- by_product の値は int にする
- unsold は昇順に並べる
入出力例
sales_including_unsold("売上日,店舗,商品名,カテゴリ,数量,金額
2026-01-05,渋谷店,白磁のマグカップ,食器,2,3600
2026-01-09,横浜店,白磁のマグカップ,食器,4,7200
2026-01-06,横浜店,鉄のフライパン,調理器具,1,7800
", "商品名,カテゴリ,定価
白磁のマグカップ,食器,1800
鉄のフライパン,調理器具,7800
積み重ね収納ボックス,収納,1500
") → {"by_product":{"白磁のマグカップ":10800,"積み重ね収納ボックス":0,"鉄のフライパン":7800},"unsold":["積み重ね収納ボックス"]}
sales_including_unsold("売上日,店舗,商品名,カテゴリ,数量,金額
2026-02-03,渋谷店,鋳物ホーロー鍋,調理器具,1,16800
", "商品名,カテゴリ,定価
鋳物ホーロー鍋,調理器具,16800
布製収納バスケット,収納,1500
藍染めのふきん,雑貨,600
") → {"by_product":{"布製収納バスケット":0,"藍染めのふきん":0,"鋳物ホーロー鍋":16800},"unsold":["布製収納バスケット","藍染めのふきん"]}