Python でアソシエーション分析 - Orange3-Associate

前回 と同様のアソシエーション分析を PythonOrange で試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20180109/

はじめに

データセット

前回 と同じデータファイルを使います。

data.basket
C,S,M,R
T,Y,C
P,Y,C,M
O,W,L
R
O,U,R,L
P
W,C
T,O,W,B,C
C,T,W,B,Y,F,D
・・・
R,P,S
B,
B,S
B,F
C,F,N

インストール

今回は Orange3 を使います。

Orange3 ではアソシエーション分析に関する処理を Orange3-Associate へ分離しているようなので、これらをインストールしておきます。

Orange3 インストール

Orange3 自体は conda コマンドでインストールできるようです。

Orange3 インストール例
> conda install orange3

より新しいバージョンをインストールするには conda-forge を使えば良さそうです。

Orange3 インストール例(conda-forge 利用)
> conda config --add channels conda-forge
> conda install orange3

Orange3-Associate インストール

試した時点では、Orange3-Associate を conda や pip でインストールできなかったので、ソースを取得してインストールしました。

Orange3-Associate インストール例
> git clone https://github.com/biolab/orange3-associate.git
> cd orange3-associate
> python setup.py install

実装と実行

前回 と同様の処理を実装してみます。

(a) リフト値なし

まずは、Orange.data.Table でデータファイルを読み込みます。 (ファイル名の拡張子は .basket とする必要がありそう)

その結果、tbl 変数の内容は [[C=1.000, S=1.000, M=1.000, R=1.000], [C=1.000, T=1.000, Y=1.000], ・・・] のようになります。

C や S のような文字列では処理できないようなので、OneHot.encode で One hot 表現化します。

その結果、X 変数の内容は [[0, 1, 2, 3], [0, 4, 5], ・・・] のようになります。(出現した順に 0 からの連番が割り当てられるようです)

frequent_itemsets で組み合わせ毎の発生件数をカウントします。 第 2引数の min_support で抽出する support(支持度)の最小値を指定できます。以下のサンプルでは 5 と指定しているので 5件以上のものが抽出されます。(比率の指定も可能)

itemsets 変数の内容は {frozenset({11}): 42, frozenset({0}): 41, frozenset({0, 11}): 12, ・・・} のようになります。

association_rules でアソシエーションルールの抽出を行います。 第 2引数の min_confidence で抽出する confidence(確信度)の最小値を指定できます。

ただし、association_rules ではリフト値を取得できないようです。

PQ 変数の内容は frozenset({11, 7}) のようになるので、元の文字列へ戻すために OneHot.decode で処理します。

OneHot.decode の結果は [(11, ContinuousVariable(name='B', number_of_decimals=3), 0), (7, ContinuousVariable(name='O', number_of_decimals=3), 0)] のようになるので ContinuousVariable の name の値を取り出しています。

sample.py
import sys
import Orange
from orangecontrib.associate.fpgrowth import *

data_file = sys.argv[1]

# データファイル読み込み
tbl = Orange.data.Table(data_file)

X, mapping = OneHot.encode(tbl)

itemsets = dict(frequent_itemsets(X, 5))

# アソシエーションルールの抽出
rules = association_rules(itemsets, 0.7)

def decode_onehot(d):
    items = OneHot.decode(d, tbl, mapping)
    # ContinuousVariable の name 値を取得
    return list(map(lambda v: v[1].name, items))

for P, Q, support, confidence in rules:
    lhs = decode_onehot(P)
    rhs = decode_onehot(Q)

    print(f"lhs = {lhs}, rhs = {rhs}, support = {support}, confidence = {confidence}")

実行結果は以下の通り。

実行結果
> python sample.py data.basket

lhs = ['B', 'O'], rhs = ['W'], support = 5, confidence = 0.8333333333333334
lhs = ['B', 'T'], rhs = ['C'], support = 5, confidence = 1.0
lhs = ['N'], rhs = ['C'], support = 10, confidence = 0.7142857142857143
lhs = ['T'], rhs = ['C'], support = 8, confidence = 0.8

(b) リフト値あり

リフト値の取得には rules_stats を使います。

rules_stats の第 3引数にはデータセットの件数(今回は 100)を指定します。(この値はリフト値の算出に使われる)

sample2.py
import sys
import Orange
from orangecontrib.associate.fpgrowth import *

data_file = sys.argv[1]

tbl = Orange.data.Table(data_file)

X, mapping = OneHot.encode(tbl)

itemsets = dict(frequent_itemsets(X, 5))

# アソシエーションルールの抽出
rules = association_rules(itemsets, 0.7)

# リフト値を含んだ結果を取得
stats = rules_stats(rules, itemsets, len(X))

def decode_onehot(d):
    items = OneHot.decode(d, tbl, mapping)
    return list(map(lambda v: v[1].name, items))

# リフト値(7番目の要素)でソート
for s in sorted(stats, key = lambda x: x[6], reverse = True):

    lhs = decode_onehot(s[0])
    rhs = decode_onehot(s[1])

    support = s[2]
    confidence = s[3]
    lift = s[6]

    print(f"lhs = {lhs}, rhs = {rhs}, support = {support}, confidence = {confidence}, lift = {lift}")

実行結果は以下の通り。

R の arules を使った 前回 と概ね同じ結果になりました。

実行結果
> python sample2.py data.basket

lhs = ['B', 'O'], rhs = ['W'], support = 5, confidence = 0.8333333333333334, lift = 3.333333333333334
lhs = ['B', 'T'], rhs = ['C'], support = 5, confidence = 1.0, lift = 2.4390243902439024
lhs = ['T'], rhs = ['C'], support = 8, confidence = 0.8, lift = 1.951219512195122
lhs = ['N'], rhs = ['C'], support = 10, confidence = 0.7142857142857143, lift = 1.7421602787456447