2026年10月8日に、mlxtend v0.23.4の公式association_rulesソースを再読しました。association_rulesはsupport、confidence、lift、leverage、conviction、Zhang、Jaccard、certainty、Kulczynskiなどを指標候補に持ち、metricとmin_threshold、support_only、null_values、num_itemsetsを受け取ります。これは頻度表からルールを選ぶAPIの仕様であり、相関を因果効果・統計的有意性・販促利益へ変換する資料ではありません。
アソシエーション分析は、同じ買い物かごなどに現れる組合せを探す方法です。「一緒に買われた」と「隣に置けば売上が増える」は別です。ここでは旧来の牛乳・パンを含む5取引を著者が設定した架空教材として数え直し、ルールを施策候補へ進める前に確認する条件を整理します。
30秒で要点
- サポートは全取引、信頼度は前件を含む取引が分母です。ルールの向きを変えると信頼度は変わります。
- リフトは後件の元々の出現率と比べた比です。1を超えても因果効果、統計的有意性、利益を保証しません。
- 最小サポートを超えた組合せを抽出してからルールを作ります。少数取引の高い値だけで採用しません。
- 商品粒度、期間、キャンペーン、対象取引を先に定義し、別期間での確認と施策の評価を分けます。
表が収まらない場合は、横にスクロールできます
| 用語 | 意味 |
|---|---|
| アイテムセット | 同じ取引に含まれる品目の集合。順序や個数そのものではない |
| 前件・後件 | A→BのAとB。ここでは重複しない品目集合 |
| 頻出アイテムセット | 定めた最小サポート以上の取引割合に現れる集合 |
5取引を、そのまま分母へ戻す
商品名はコードと対応させています。各行が1取引で、同じ商品を複数個買っても、この教材では存在を1回だけ数えます。欠測を「買わなかった」と勝手に置換せず、何が観測できているかを先に決めます。
表が収まらない場合は、横にスクロールできます
| 架空取引 | 買い物かご |
|---|---|
| T1 | milk、bread、butter |
| T2 | bread、eggs |
| T3 | milk、bread |
| T4 | beer、chips |
| T5 | milk、eggs、bread |
全取引数Nは5、milkを含む取引は3、breadは4、両方は3です。公式ルール指標の説明に沿うと、集合A、Bの計算は次のようになります。
- サポート:AとBの両方を含む取引数 / N。
- 信頼度:AとBの両方を含む取引数 / Aを含む取引数。前件が0件なら定義できません。
- リフト:信頼度 / Bのサポート。後件の出現率を基準に比較します。
表が収まらない場合は、横にスクロールできます
| ルール | 両方 / 全取引(サポート) | 両方 / 前件(信頼度) | 後件の出現率 | リフト |
|---|---|---|---|---|
| milk→bread | 3/5 = 0.60 | 3/3 = 1.00 | 4/5 = 0.80 | 1.25 |
| bread→milk | 3/5 = 0.60 | 3/4 = 0.75 | 3/5 = 0.60 | 1.25 |
| eggs→bread | 2/5 = 0.40 | 2/2 = 1.00 | 4/5 = 0.80 | 1.25 |
| bread→eggs | 2/5 = 0.40 | 2/4 = 0.50 | 2/5 = 0.40 | 1.25 |
リフトはこの式では向きを入れ替えても同じですが、信頼度は違います。信頼度1.00のmilk→breadも、確認した前件は3取引だけです。牛乳の購入がパンの購入を引き起こしたという結果ではありません。パンの元々の出現率80%を無視すると、100%という表示を過大評価します。
前処理からApriori、ルール生成まで
TransactionEncoderは品目ごとの有無を真偽値へ変換します。Aprioriはこの表から最小サポートを満たす集合を探します。売上金額や数量、時間順序をこの真偽値表から復元することはできません。次はPython 3.12、pandas 2.2.3、mlxtend 0.23.4を対象とした教材です。後二つはそれぞれ pandas==2.2.3、mlxtend==0.23.4 と版を固定します。実サイトの購買データや外部APIは使いません。対象版の関数定義に合わせ、元の取引件数を明示します。別版へ変更するときは引数・依存関係と出力を再確認してください。
import pandas as pd
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules
transactions = [
['milk', 'bread', 'butter'],
['bread', 'eggs'],
['milk', 'bread'],
['beer', 'chips'],
['milk', 'eggs', 'bread'],
]
te = TransactionEncoder()
ohe = te.fit(transactions).transform(transactions)
df = pd.DataFrame(ohe, columns=te.columns_)
freq = apriori(df, min_support=0.3, use_colnames=True)
rules = association_rules(
freq, num_itemsets=len(transactions), metric='confidence', min_threshold=0.6
)
rules = rules.sort_values('lift', ascending=False)
shown = rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']].copy()
# 同順位や集合の表示順に依存せず、読める形で並べる
for column in ['antecedents', 'consequents']:
shown[column] = shown[column].map(lambda items: ','.join(sorted(items)))
shown = shown.sort_values(['antecedents', 'consequents']).reset_index(drop=True)
print(shown.to_string(index=False))
expected = {
('milk', 'bread'): (0.6, 1.0, 1.25),
('bread', 'milk'): (0.6, 0.75, 1.25),
('eggs', 'bread'): (0.4, 1.0, 1.25),
}
assert len(shown) == len(expected)
for row in shown.itertuples(index=False):
want = expected[(row.antecedents, row.consequents)]
assert all(abs(got - target) < 1e-12
for got, target in zip((row.support, row.confidence, row.lift), want))
この5件でmin_support=0.3を満たすには、整数の取引数として2件以上が必要です。頻出集合はbread、milk、eggs、breadとmilk、breadとeggsの5つになります。butter、beer、chipsは各1件なので残りません。続く信頼度0.6の条件により、表のbread→eggsは落ち、コードは3ルールを出力します。閾値を変えたら「何件が残る条件か」も数え直します。
高い指標でも、検討を続ける理由
表が収まらない場合は、横にスクロールできます
| 観測・設定 | 誤った結論 | 追加で確認すること |
|---|---|---|
| 信頼度が高い | 前件に特有の強い関係だ | 後件が全体でも頻出していないか。リフトと実件数を見る |
| リフトが高い | 有意で、利益も増える | 少数件、探索した候補数、期間外の再現、粗利・在庫・施策費用 |
| サポートが低い | 全部ノイズだから捨てる | 業務上の価値と必要件数。ただし不確実な候補を確定扱いしない |
| convictionが無限大 | 完全な法則を発見した | 信頼度1のとき分母が0になる式の性質と、前件の実件数 |
convictionは (1 - 後件のサポート) / (1 - 信頼度) です。milk→breadでは0.2/0となり、対象実装は無限大を返します。3件で反例がなかっただけなので、無限大を確信の強さとして読みません。レバレッジは同時出現率と、周辺出現率の積との差です。この例では0.60 - 0.60×0.80 = 0.12です。比と差を分けると、同じルールの別の側面が見えます。
カイ二乗検定を追加しても、データを見て多数のルールを選んだ後の問題は消えません。取引の独立性や少ない期待度数、複数比較を検討する必要があります。この5取引だけに有意性や一般化の結論は付けません。
食品の併買を売り場の判断へ進めるなら
旧来の食品ケースなら、まず同一SKUか商品群か、返品をどう扱うか、取引の切り方を決めます。商品をまとめると問いも出現率も変わるので、ルール数を減らすためだけに機械的に統合しません。
次に期間・季節・キャンペーンの有無を確認します。販促で同時に売った商品は、その販促が共通要因かもしれません。別期間に同じ定義を当て、実件数と指標の変化を確かめます。期間を細分化しすぎて各区分が少数になることにも注意します。
棚の隣接、セット割、レコメンドはこの先の施策候補です。粗利、在庫、代替購入、表示できる枠、割引費用を考え、比較できる条件で施策を評価します。週次の観測が業務に合えば使えますが、少数件でも毎週結論を変えるという意味ではありません。併買ルールの探索と施策効果の検証を別の記録にします。
三つの練習問題
- 信頼度0.80でもリフト1に近いのはなぜか。 後件の出現率も0.80なら、0.80/0.80で1です。後件が元々よく現れるため、高い信頼度だけでは前件に特有の関係といえません。
- 2品目から1品目へのルールが多すぎるときはどうするか。 前件の長さ、実件数、意味の重複、施策で使える条件を先に絞ります。見た目の上位リフトだけを選ばず、閾値の選択に使っていない期間でも確認します。
- 季節性が強いときは何を分けるか。 季節・販促・対象顧客と取引定義を記録し、比較可能な期間を設計します。区分後の分母が小さすぎないかも確認し、「分割したから季節性を解決した」とはしません。
顧客・商品全体の見方と組み合わせる
併買だけでは、顧客の継続性や商品の売上構成は分かりません。RFM分析、ABC分析、デシル分析を目的に応じて使い分けます。推定と判断の前提は統計手法の判断ガイドで整理してください。