빈발 패턴 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""빈발 패턴과 지지도 검증.
문서: 10.빈발 패턴 (예시, 정의, 카드), 11.연관 규칙 (예시, 카드)
주장:
1. 거래 5개(맥주·견과·기저귀 / 맥주·커피·기저귀 / 맥주·기저귀·달걀 / 견과·달걀·우유 / 견과·커피·기저귀·달걀·우유)에서
지지 개수 맥주 3, 기저귀 4, 견과 3, {맥주, 기저귀} 3, {맥주, 달걀} 1, {견과, 달걀} 2.
2. min_sup 50%면 1항목 빈발 4개(기저귀 80%, 맥주·견과·달걀 60%), 2항목 빈발은 {맥주, 기저귀} 하나뿐, 3항목 빈발은 없다.
3. 기저귀 -> 맥주 (60%, 75%), 맥주 -> 기저귀 (60%, 100%).
4. 빈발 집합의 모든 부분집합은 빈발이다(무작위 자료로 확인). 길이 100인 빈발 집합의 공집합 아닌 부분집합은 2^100 - 1 ≈ 1.27 × 10^30개.
"""
from itertools import combinations
import random
DB = [{"Beer", "Nuts", "Diaper"}, {"Beer", "Coffee", "Diaper"}, {"Beer", "Diaper", "Eggs"},
{"Nuts", "Eggs", "Milk"}, {"Nuts", "Coffee", "Diaper", "Eggs", "Milk"}]
def sup(db, X):
return sum(1 for t in db if set(X) <= t)
def main():
assert (sup(DB, ["Beer"]), sup(DB, ["Diaper"]), sup(DB, ["Nuts"])) == (3, 4, 3)
assert (sup(DB, ["Beer", "Diaper"]), sup(DB, ["Beer", "Eggs"]), sup(DB, ["Nuts", "Eggs"])) == (3, 1, 2)
print("[OK] 영수증 5장의 지지 개수")
items = sorted({i for t in DB for i in t})
freq = {k: [c for c in combinations(items, k) if sup(DB, c) / 5 >= 0.5] for k in (1, 2, 3)}
assert sorted(x[0] for x in freq[1]) == ["Beer", "Diaper", "Eggs", "Nuts"]
assert freq[2] == [("Beer", "Diaper")] and freq[3] == []
print("[OK] min_sup 50%: 빈발 1항목 4개, 2항목 {Beer, Diaper} 하나, 3항목 없음")
s = sup(DB, ["Beer", "Diaper"]) / 5
assert s == 0.6 and sup(DB, ["Beer", "Diaper"]) / sup(DB, ["Diaper"]) == 0.75 and sup(DB, ["Beer", "Diaper"]) / sup(DB, ["Beer"]) == 1
print("[OK] 규칙: Diaper -> Beer (60%, 75%), Beer -> Diaper (60%, 100%)")
rnd = random.Random(5)
for _ in range(200):
db = [set(rnd.sample("ABCDEF", rnd.randint(1, 5))) for _ in range(rnd.randint(3, 10))]
ms = rnd.randint(1, 3)
for r in range(1, 5):
for c in combinations("ABCDEF", r):
if sup(db, c) >= ms:
assert all(sup(db, s) >= ms for k in range(1, r) for s in combinations(c, k))
print("[OK] 무작위 자료 200개: 빈발 집합의 부분집합은 모두 빈발 (아프리오리 성질)")
n = 2 ** 100 - 1
assert 1.26e30 < n < 1.27e30
print(f"[OK] 길이 100 빈발 집합의 부분집합 {n:.3e}개")
# 카드 C2 (10): 같은 DB에서 {Nuts, Milk}의 지지 개수와 상대 지지도
assert sup(DB, ["Nuts", "Milk"]) == 2
# 카드 C3 (11): Eggs -> Nuts 의 지지도와 신뢰도
assert sup(DB, ["Eggs", "Nuts"]) / 5 == 0.4 and round(sup(DB, ["Eggs", "Nuts"]) / sup(DB, ["Eggs"]), 3) == 0.667
print("[OK] 카드: {Nuts, Milk} 2개(40%), Eggs -> Nuts (40%, 66.7%)")
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()