<jupyter_start><jupyter_text>causalnex trial 01NOTEARSを用いた因果グラフ推定、ベイジアンネットの推定。- from_pandas- BayesianNetwork  必要パッケージ インストール & import<jupyter_code>!pip install causalnex
!pip install networkx
!pip install pgmpy
from IPython.display import clear_output
clear_output()
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import networkx as nx
import warnings; warnings.simplefilter("ignore")
%matplotlib inline
# import causalnex
from causalnex.structure.notears import from_pandas #, from_pandas_lasso
#from causalnex.structure.pytorch import from_pandas as from_pandas_pytorch

from causalnex.network import BayesianNetwork
from causalnex.discretiser import Discretiser
# from causalnex.structure import DAGClassifier, DAGRegressor
# networkx_util
from util.networkx_util import NetworkxUtil
nx_util = NetworkxUtil() # networkxの図示, printのためのutil<jupyter_output><empty_output><jupyter_text>サンプルデータ作成<jupyter_code>def generate_sample_data(n_samples=1000):
    """
    サンプルデータ生成
    | c1 --> x1
    | c2 --> x1
    | c1 --> y
    | c2 --> y
    | x1 --> y
    | x2 は独立
    """
    np.random.seed(0)
    c1 = np.random.normal(0, 0.5, size=n_samples)
    c2 = np.random.choice(2, size=n_samples)

    np.random.seed(1)
    x1 = np.random.normal(1, 2, size=n_samples) + c1 + c2/10

    np.random.seed(2)
    y = np.random.uniform(-1,2, size=n_samples) + x1 + c1 + c2 /10

    np.random.seed(3)
    x2 = np.random.uniform(-2, 2, size=n_samples)
    raw_data = pd.DataFrame({"x1": x1, "y":y, "x2": x2, "c1": c1, "c2":c2})
    return raw_data
n_samples = 2000
struct_data = generate_sample_data(n_samples)
print(struct_data.shape)<jupyter_output>(2000, 5)<jupyter_text>NO TEARSで因果グラフ推定<jupyter_code># NOTEARSを実行, from_pandas_lassoでL1 penaltyをつけて推定することが可能
sm = from_pandas( 
    struct_data,
    tabu_edges = [],
    tabu_parent_nodes = None,
    tabu_child_nodes = None,
)
sm.threshold_till_dag() # DAGになるように閾値をあげる
sm.remove_edges_below_threshold(0.2) # 係数の閾値を0.2にする
nx_util.plot_structure_model(structure_model=sm, layout_method="circular") # この条件で正しく推定できていることがわかる。
# largest subgraphを抽出
sm = sm.get_largest_subgraph()
nx_util.plot_structure_model(sm, layout_method="random", figsize=(9,6))
# 係数をprint
nx_util.print_weights(sm)<jupyter_output>[ x1 ] ---> [ y ]		Weight	1.05582
[ c1 ] ---> [ x1 ]		Weight	0.93608
[ c1 ] ---> [ y ]		Weight	0.91724
[ c2 ] ---> [ x1 ]		Weight	1.05973
[ c2 ] ---> [ y ]		Weight	0.49489<jupyter_text>NO TEARSでTabuを指定して因果グラフを推定<jupyter_code># struct data
sm_with_tabu = from_pandas(
    struct_data,
    tabu_edges = [("c1", "x1")], # c1 --> x1 に線が引かれなくなる。
    tabu_parent_nodes = ["c2"], # 親ノードにならなくなる。矢印元にならなくなる。
    tabu_child_nodes = ["y"], # 子ノードにならなくなる。矢印が刺されなくなる。
)
sm_with_tabu.threshold_till_dag()
sm_with_tabu.remove_edges_below_threshold(0.2)

nx_util.plot_structure_model(structure_model=sm_with_tabu, layout_method="circular") # 
nx_util.print_weights(sm_with_tabu) # 係数を print<jupyter_output>[ y ] ---> [ x1 ]		Weight	0.75815
[ y ] ---> [ c2 ]		Weight	0.22665
[ c1 ] ---> [ c2 ]		Weight	-0.32098<jupyter_text>NO TEARSで推定した因果グラフから、ベイジアンネットを推定<jupyter_code># ベイジアンネットを推定するために離散化
discretised_data = struct_data.copy().drop(["x2"], axis=1)
discretised_data["y"] = Discretiser(method="quantile", num_buckets=5).fit_transform(discretised_data["y"].values)
discretised_data["x1"] = Discretiser(method="uniform", num_buckets=5).fit_transform(discretised_data["x1"].values)
discretised_data["c1"] = Discretiser(method="percentiles", percentile_split_points=[0.25, 0.5, 0.75]).fit_transform(discretised_data["c1"].values)
discretised_data["c2"] = Discretiser(method="fixed", numeric_split_points=[0,1]).transform(discretised_data["c2"].values)
# load estimated causal graph
bn = BayesianNetwork(sm)

bn = bn.fit_node_states(discretised_data) # 取りうるすべての状態を指定、 本来testデータに新出のカテゴリがある場合には判定し、変換する必要がある
bn = bn.fit_cpds(discretised_data, method="BayesianEstimator", bayes_prior="K2")
# 条件付き確率を推定
bn.cpds["y"]<jupyter_output><empty_output><jupyter_text>推定したモデルから予測値を算出<jupyter_code>pred_target_col = "y"
parents_columns = bn.cpds[pred_target_col].columns.names # 予測対象カラムの親ノードのみ必要であるためsliceする、sliceせずとも実行可能
y_pred = bn.predict(discretised_data[parents_columns], pred_target_col)
y_predict_proba = bn.predict_probability(discretised_data[parents_columns], pred_target_col) # 予測確率
# 実測値
discretised_data["y"].head(5)
# 予測値
y_pred.head(5)
# 予測確率
y_predict_proba.head(5)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13021891220943355, 'fraction_numerical': 0.0349808169713383, 'mean_word_length': 5.957613814756672, 'pattern_counts': {'":': 5, '<': 22, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '80111', 'license': 'Apache-2.0', 'n_tokens_mistral': 2085, 'n_tokens_neox': 2003, 'n_words': 273, 'path': 'notebooks/202105_causalnex_trial01.ipynb', 'repo_name': 'yuminaga-tech/blog', 'size': '4431'}