1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SEM・LiNGAM・DoWhyを併用して因果関係を分類

Posted at

user:

Pandas dataframe 形式のファイルに、列ラベルのついたデータがあります。ユーザがそのファイルと、目的変数(複数可)を入力したときに、次の動作をする関数を書いてください。

・目的変数以外の変数を、説明変数とみなします。
・目的変数と説明変数の全ての組み合わせに対して、DoWhyを用いて介入試験をします。
・「説明変数を原因とし、目的変数を結果とする、直接の因果関係がある」「間接的な因果関係がある」「因果関係はない」の3種類に分類します。
・分類結果を pandas dataframe 形式で返します。

assistant:

以下は、指定された要件に基づき、pandas形式のデータと目的変数のリストを入力とし、DoWhy を用いた介入試験を行い、因果関係の種類(直接、間接、なし)を分類して DataFrame 形式で出力する Python 関数です。


🔧 必要なライブラリ

まず以下のライブラリが必要です(インストールしていない場合は先にインストールしてください):

pip install dowhy pandas networkx

✅ 関数コード

import pandas as pd
import numpy as np
from dowhy import CausalModel
import itertools

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    各説明変数と目的変数のペアに対して DoWhy による介入試験を行い、
    因果関係を「直接」「間接」「なし」に分類して DataFrame を返す。
    """

    results = []

    # 目的変数以外の列を説明変数とする
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    # 目的変数 × 説明変数の全組み合わせで因果推論
    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                # モデル識別
                identified_model = model.identify_effect()

                # 介入効果推定(回帰モデルなど)
                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression",
                    test_significance=True
                )

                pval = estimate.test_stat_significance['p_value']
                effect = estimate.value

                # 効果の有無を判定
                if pval < 0.05:
                    # DAG構造から直接エッジがあるか調べる
                    graph = model._graph._graph
                    if graph.has_edge(treatment, target):
                        relation = "直接の因果関係がある"
                    else:
                        relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "p値": pval,
                    "因果関係の種類": relation
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "p値": None,
                    "因果関係の種類": f"エラー: {str(e)}"
                })

    return pd.DataFrame(results)

📌 使い方例

import pandas as pd

# データを読み込む
df = pd.read_csv("your_data.csv")

# 目的変数(複数指定可)
targets = ["Y1", "Y2"]

# 関数呼び出し
result_df = classify_causal_relationships(df, targets)

# 結果表示
print(result_df)

🔍 出力結果の構成

説明変数 目的変数 推定効果 p値 因果関係の種類
X1 Y1 0.34 0.01 直接の因果関係がある
X2 Y1 0.12 0.03 間接的な因果関係がある
X3 Y1 - - 因果関係はない

🔎 注意点

  • DoWhyの「DAG構造」はデフォルトでは構築が簡略化されるため、複雑な関係性を厳密に反映しないことがあります。必要に応じて構造学習ライブラリ(gCastle、causal-learn など)と連携することも可能です。
  • サンプル数が少ないと p 値が不安定になりやすい点にも注意してください。

ご希望に応じて、SEMやLiNGAMベースでの因果分類も追加可能です。必要があればお知らせください。

user:

ありがとうございます。では、SEMベースで次の関数を作ってください。

Pandas dataframe 形式のファイルに、列ラベルのついたデータがあります。ユーザがそのファイルと、目的変数(複数可)を入力したときに、次の動作をする関数を書いてください。

・目的変数以外の変数を、説明変数とみなします。
・目的変数と説明変数の全ての組み合わせに対して、SEMを用いて「説明変数を原因とし、目的変数を結果とする、直接の因果関係がある」「間接的な因果関係がある」「因果関係はない」の3種類に分類します。
・分類結果を pandas dataframe 形式で返します。

assistant:

以下に、構造方程式モデリング(SEM) に基づいて、全ての説明変数 × 目的変数のペアに対し、

  • 直接の因果関係がある
  • 間接的な因果関係がある
  • 因果関係はない

のいずれかに分類し、結果を pandas.DataFrame 形式で返す関数を作成しました。


🔧 ライブラリのインストール

以下のパッケージが必要です:

pip install semopy pandas numpy networkx

✅ 関数コード(SEMベース)

import pandas as pd
import numpy as np
from semopy import Model
import networkx as nx
import itertools

def classify_sem_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    SEMを用いて各説明変数と目的変数の因果関係を分類する。

    戻り値: 因果関係を分類した pandas DataFrame
    """

    results = []
    all_vars = df.columns.tolist()
    explanatory_vars = [col for col in all_vars if col not in target_vars]

    # モデル構造(全変数間の有向エッジ)を仮定した構造方程式
    latent_eqs = []
    for y in all_vars:
        parents = [x for x in all_vars if x != y]
        if parents:
            latent_eqs.append(f"{y} ~ " + " + ".join(parents))

    model_desc = "\n".join(latent_eqs)

    try:
        # SEM モデルの構築と推定
        model = Model(model_desc)
        model.fit(df)

        # パス係数と p値を取得
        estimates = model.inspect()

        # 推定値を用いて有向グラフを構築
        G = nx.DiGraph()
        for _, row in estimates.iterrows():
            if row["op"] == "~" and not np.isnan(row["pval"]):
                if row["pval"] < 0.05:  # 有意なパスのみ採用
                    G.add_edge(row["rhs"], row["lhs"])

        # 各説明変数 × 各目的変数の因果分類
        for target in target_vars:
            for expl in explanatory_vars:
                if G.has_edge(expl, target):
                    relation = "直接の因果関係がある"
                elif nx.has_path(G, expl, target):
                    relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない"

                results.append({
                    "説明変数": expl,
                    "目的変数": target,
                    "因果関係の種類": relation
                })

    except Exception as e:
        print(f"SEM モデル推定中にエラーが発生しました: {e}")
        return pd.DataFrame()

    return pd.DataFrame(results)

📌 使用例

import pandas as pd

# データ読み込み
df = pd.read_csv("your_data.csv")

# 目的変数(複数指定可)
target_vars = ["Y1", "Y2"]

# 分類関数の実行
result_df = classify_sem_causal_relationships(df, target_vars)

# 結果の表示
print(result_df)

🧠 出力例(DataFrame)

説明変数 目的変数 因果関係の種類
X1 Y1 直接の因果関係がある
X2 Y1 間接的な因果関係がある
X3 Y1 因果関係はない

🔍 補足と注意点

  • この関数はすべての変数間の回帰を仮定した「飽和モデル」に基づいています。実際のデータや理論に応じてモデル構造を指定することで精度を高めることができます。
  • semopy は線形SEMを前提としているため、非線形関係やカテゴリ変数には注意が必要です。
  • pval < 0.05 を有意水準としていますが、変更可能です。

必要であれば、パス図の出力機能p値・係数を含めた結果テーブルも追加可能です。ご希望があればお知らせください。

user:

ありがとうございます。では、LiNGAMベースで次の関数を作ってください。

Pandas dataframe 形式のファイルに、列ラベルのついたデータがあります。ユーザがそのファイルと、目的変数(複数可)を入力したときに、次の動作をする関数を書いてください。

・目的変数以外の変数を、説明変数とみなします。
・目的変数と説明変数の全ての組み合わせに対して、LiNGAMを用いて「説明変数を原因とし、目的変数を結果とする、直接の因果関係がある」「間接的な因果関係がある」「因果関係はない」の3種類に分類します。
・分類結果を pandas dataframe 形式で返します。

assistant:

以下は、LiNGAM(Linear Non-Gaussian Acyclic Model) を用いて、指定された pandas.DataFrame に対して、

  • 説明変数 → 目的変数 の因果関係を

    • 直接の因果関係がある
    • 間接的な因果関係がある
    • 因果関係はない
      に分類する関数です。

🔧 必要なライブラリ

pip install pandas numpy lingam networkx

✅ 関数定義(LiNGAMベース)

import pandas as pd
import numpy as np
from lingam import DirectLiNGAM
import networkx as nx

def classify_lingam_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    LiNGAM を用いて、説明変数と目的変数の因果関係を分類する関数。

    Parameters:
    - df: 入力データ(pandas DataFrame)
    - target_vars: 目的変数のリスト(複数可)

    Returns:
    - 結果を含む pandas DataFrame
    """
    results = []
    all_vars = df.columns.tolist()
    explanatory_vars = [col for col in all_vars if col not in target_vars]

    # LiNGAM モデルを学習
    try:
        model = DirectLiNGAM()
        model.fit(df)
        adjacency_matrix = model.adjacency_matrix_
        columns = df.columns.tolist()

        # 有向グラフを構築(係数 ≠ 0 のときエッジを追加)
        G = nx.DiGraph()
        for i, from_var in enumerate(columns):
            for j, to_var in enumerate(columns):
                if not np.isclose(adjacency_matrix[i, j], 0):
                    G.add_edge(from_var, to_var)

        # 各説明変数 × 各目的変数で因果関係を分類
        for target in target_vars:
            for expl in explanatory_vars:
                if G.has_edge(expl, target):
                    relation = "直接の因果関係がある"
                elif nx.has_path(G, expl, target):
                    relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない"

                results.append({
                    "説明変数": expl,
                    "目的変数": target,
                    "因果関係の種類": relation
                })

    except Exception as e:
        print(f"LiNGAM推定でエラーが発生しました: {e}")
        return pd.DataFrame()

    return pd.DataFrame(results)

📌 使用例

import pandas as pd

# データ読み込み
df = pd.read_csv("your_data.csv")

# 目的変数(複数指定可)
target_vars = ["Y1", "Y2"]

# 関数の実行
result_df = classify_lingam_causal_relationships(df, target_vars)

# 結果表示
print(result_df)

🧠 出力例

説明変数 目的変数 因果関係の種類
X1 Y1 直接の因果関係がある
X2 Y1 間接的な因果関係がある
X3 Y1 因果関係はない

🔍 補足

  • DirectLiNGAM は非ガウス分布かつ線形の仮定を前提としています。非線形関係やガウス的誤差では性能が落ちます。
  • ノード間の係数がゼロ(もしくはほぼゼロ)であるかどうかで直接因果かを判定しています。
  • 必要であれば 係数値や DAG の可視化 も追加できます。ご希望があればお知らせください。

他の LiNGAM ファミリー(例えば ICA-LiNGAM)を使いたい場合も対応可能です。

user:

DoWhyの部分で、このようなエラーになりました。

Traceback (most recent call last):
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/exec_code.py", line 121, in exec_func_with_error_handling
    result = func()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 639, in code_to_exec
    _mpa_v1(self._main_script_path)
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 159, in _mpa_v1
    page.run()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/navigation/page.py", line 297, in run
    exec(code, module.__dict__)
  File "/Users/ikemen/streamlit-app/pages/test.py", line 1017, in <module>
    st.dataframe(classify_causal_relationships(Xy_std, target_cols))
  File "/Users/ikemen/streamlit-app/pages/test.py", line 58, in classify_causal_relationships
    pval = estimate.test_stat_significance['p_value']
TypeError: 'method' object is not subscriptable

assistant:

このエラーは、以下のコード行に原因があります:

pval = estimate.test_stat_significance['p_value']

この estimate.test_stat_significanceメソッド(関数) であり、辞書ではありません。そのため、['p_value'] と書いてしまうと「関数を辞書のように使おうとしてる」と解釈され、TypeError: 'method' object is not subscriptable というエラーが出ます。


✅ 修正方法

関数として 呼び出す必要 があります。以下のように修正してください:

pval = estimate.test_stat_significance()['p_value']

つまり、() をつけてメソッドを実行し、その戻り値(通常は辞書)から p_value を取り出します。


🔁 修正前後の例

❌ 修正前(エラーが出る)

pval = estimate.test_stat_significance['p_value']

✅ 修正後(正しい書き方)

pval = estimate.test_stat_significance()['p_value']

🧪 おすすめ:補助的なチェック

もし test_stat_significance() が戻り値として dict を返さない場合は、次のように中身を確認するのもおすすめです。

result = estimate.test_stat_significance()
print(result)  # or st.write(result)

これにより、何が返ってきているか明確になります。


必要であれば DoWhy 全体のコードもレビューできますので、お気軽にどうぞ。

user:

ありがとうございます。次のエラーを見る限り、ライブラリそのものの問題のような気もしますね。

Traceback (most recent call last):
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/exec_code.py", line 121, in exec_func_with_error_handling
    result = func()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 639, in code_to_exec
    _mpa_v1(self._main_script_path)
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 159, in _mpa_v1
    page.run()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/navigation/page.py", line 297, in run
    exec(code, module.__dict__)
  File "/Users/ikemen/streamlit-app/pages/test.py", line 1019, in <module>
    st.dataframe(classify_causal_relationships(Xy_std, target_cols))
  File "/Users/ikemen/streamlit-app/pages/test.py", line 60, in classify_causal_relationships
    pval = estimate.test_stat_significance()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/dowhy/causal_estimator.py", line 891, in test_stat_significance
    signif_results = self.estimator.test_significance(self._data, self.value, method=method, **kwargs)
AttributeError: 'CausalEstimate' object has no attribute 'estimator'. Did you mean: 'add_estimator'?

assistant:

このエラー:

AttributeError: 'CausalEstimate' object has no attribute 'estimator'. Did you mean: 'add_estimator'?

は、DoWhy の内部で estimate.test_stat_significance() を呼び出した際に、CausalEstimate オブジェクトに estimator 属性が存在しないことによって発生しています。これは、test_stat_significance() メソッドが現在の DoWhy バージョンでは非推奨または正しく実装されていない可能性があります。


✅ 結論(簡潔に)

test_stat_significance() は使わない方が良い

代わりに、因果推定後の CausalEstimate.estimate の値そのものや .confidence_intervals, .stderr, .p_value などを手動で評価した方が安全です。DoWhy 自体は、統計的検定機能が一部未完成・非推奨のまま残っている部分があります。


✅ 実務対応方法:代替処理

以下のように変更してください:

# 推定
estimate = model.estimate_effect(
    identified_estimand,
    method_name="backdoor.linear_regression"
)

# 結果に含まれる推定値や信頼区間などを手動で使う
effect = estimate.value
stderr = estimate.get_standard_error() if hasattr(estimate, 'get_standard_error') else None

# 信頼区間または標準誤差が取得できれば、それで有意性を判定
relation = "因果関係あり" if abs(effect) > 2 * stderr else "因果関係はない" if stderr else "評価不能"

🔧 あるいは:条件をつけて test_stat_significance() を安全に呼ぶ

if hasattr(estimate, 'estimator') and hasattr(estimate.estimator, 'test_significance'):
    test_result = estimate.test_stat_significance()
    pval = test_result.get('p_value', None)
else:
    pval = None  # またはログ記録

🔄 どうしてこうなるのか?

  • DoWhy の設計では、estimate_effect() の戻り値である CausalEstimate オブジェクトに、必ずしも .estimator が格納されるとは限りません。
  • test_stat_significance() はその .estimator に依存して動くため、特定の手法(例:backdoor.linear_regression)でしか安全に使えません。
  • 現在の DoWhy因果グラフや効果推定の設計が中心であり、統計的検定はやや副次的です。

✅ 最善策

DoWhy のバージョンによる違いを吸収しながら安全に使うなら、以下の方針が安心です:

  1. .value を効果の推定値として見る
  2. .stderr(標準誤差)または .confidence_intervals が取れれば有意性を自前で判定
  3. .p_value を使いたいなら明示的に method_name="backdoor.linear_regression" を指定し、例外処理を入れる

🔚 まとめ

  • 現在の DoWhyCausalEstimateestimator がないことがあり、それに依存した .test_stat_significance() は不安定です。
  • .value, .stderr, .confidence_intervals を使って有意性を判断しましょう。
  • 必要であれば、DoWhy の上に EconMLCausalML などを組み合わせると統計検定機能も拡張できます。

必要であれば、DoWhy のバージョンに応じた安全なテンプレートコードも作成できます。お申し付けください。

user:

ありがとうございます。それでは、「実務対応方法:代替処理」の方法を使って次の関数を書き換えてください。

import pandas as pd
import numpy as np
from dowhy import CausalModel
import itertools

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    各説明変数と目的変数のペアに対して DoWhy による介入試験を行い、
    因果関係を「直接」「間接」「なし」に分類して DataFrame を返す。
    """

    results = []

    # 目的変数以外の列を説明変数とする
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    # 目的変数 × 説明変数の全組み合わせで因果推論
    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                # モデル識別
                identified_model = model.identify_effect()

                # 介入効果推定(回帰モデルなど)
                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression",
                    test_significance=True
                )

                pval = estimate.test_stat_significance['p_value']
                effect = estimate.value

                # 効果の有無を判定
                if pval < 0.05:
                    # DAG構造から直接エッジがあるか調べる
                    graph = model._graph._graph
                    if graph.has_edge(treatment, target):
                        relation = "直接の因果関係がある"
                    else:
                        relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "p値": pval,
                    "因果関係の種類": relation
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "p値": None,
                    "因果関係の種類": f"エラー: {str(e)}"
                })

    return pd.DataFrame(results)

assistant:

以下は、test_stat_significance の不具合回避のために「標準誤差を用いた効果の有意性判定」に書き換えた関数です:

import pandas as pd
import numpy as np
from dowhy import CausalModel
import itertools

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    各説明変数と目的変数のペアに対して DoWhy による介入試験を行い、
    因果関係を「直接」「間接」「なし」に分類して DataFrame を返す。
    """

    results = []

    # 目的変数以外の列を説明変数とする
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                # モデル識別
                identified_model = model.identify_effect()

                # 介入効果推定(回帰モデルなど)
                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression"
                )

                effect = estimate.value
                stderr = estimate.get_standard_error() if hasattr(estimate, 'get_standard_error') else None

                # 有意性の判定:効果の絶対値が2倍の標準誤差を超えていれば有意とみなす
                if stderr is not None and abs(effect) > 2 * stderr:
                    # DAG構造から直接エッジがあるか調べる
                    graph = model._graph._graph
                    if graph.has_edge(treatment, target):
                        relation = "直接の因果関係がある"
                    else:
                        relation = "間接的な因果関係がある"
                    pval_note = "標準誤差による判定"
                else:
                    relation = "因果関係はない"
                    pval_note = "非有意または標準誤差なし"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "標準誤差": stderr,
                    "因果関係の種類": relation,
                    "備考": pval_note
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "標準誤差": None,
                    "因果関係の種類": "エラー",
                    "備考": str(e)
                })

    return pd.DataFrame(results)

✅ 主な変更点

変更内容 説明
test_significance=True の削除 これにより .test_stat_significance() を呼ばなくなります。
stderr(標準誤差)を用いた有意性判定 効果の大きさが 2×標準誤差以上であれば有意とみなす簡易基準を採用しています。
p値 の代わりに備考欄で判定方法を記載 p値 の代替として処理の透明性を確保しています。

🔧 補足

  • estimate.get_standard_error() は推定方法によっては None を返すことがあります。その際は「評価不能」扱いになります。
  • より厳密な検定が必要な場合は EconML などを組み合わせることも検討できます。

ご希望があれば、Streamlit 連携版や LiNGAM ベース版への展開も可能です。

user:

refute.refutation_result.get("p_value", None) を使う方法もありますか?

assistant:

はい、refute.refutation_result.get("p_value", None) を使う方法もあります。ただしこれは DoWhy の refute_estimate() を使った 「反事実検定(refutation)」 における結果の 有意性(信頼性)評価 に使うもので、介入効果そのものの有意性(=p値) とは少し異なる目的になります。


🔍 refute_estimate() とは?

DoWhy の refute_estimate() は、推定された因果効果が 頑健で信頼できるかどうか を評価するためのテストを行います。主に以下の方法があります:

  • Placebo Test(偽治療)
  • Random Common Cause
  • Data Subset Refuter
  • Add Unobserved Common Cause
  • Dummy Outcome Test など

各テストの結果として、refutation_result が返され、そこに "p_value" キーが存在する場合があります。


✅ 使い方の例

以下に、DoWhy の refute_estimate()refutation_result.get("p_value", None) を使って因果推論の信頼性を評価するコード例を示します:

refutation = model.refute_estimate(
    identified_model,
    estimate,
    method_name="placebo_treatment_refuter"  # 他にも色々選べます
)

pval = refutation.refutation_result.get("p_value", None)

if pval is not None and pval > 0.05:
    relation = "反事実検定で有意差なし → 効果は信頼できる"
else:
    relation = "反事実検定で有意差あり → 効果は疑わしい"

⚠ 注意点

内容
🎯 主な目的 refutation_result.get("p_value") は「効果の頑健性検定」に使うもの
📉 限界 推定された因果効果が「偶然のものではない」ことの裏付けにはなるが、効果の「存在そのもの」を直接検証するものではない
✅ 利用の意義 他の有意性検定がうまく使えないときの補完として有用。効果の妥当性をチェックするための ロバストネス検証 に役立つ。

✅ 結論:使えるか?

  • 使えるが、「効果があるかどうか」よりも、「効果は信頼できるかどうか」を検証する目的で使うべき。
  • 「p値で有意」かつ「反事実検定でも妥当」であれば、より強く因果関係を主張できます。

🔧 実務アドバイス

  • estimate.value(効果量)と estimate.get_standard_error()(有意性の目安)で 存在の有意性 を評価。
  • refute_estimate(...).refutation_result["p_value"]推定の信頼性 を評価。
  • 両方セットで見るのがベストです。

ご希望であれば、このアプローチを組み込んだ関数もご提供できます。

user:

では、次のコードを refute_estimate(...).refutation_result["p_value"] で置き換えた関数にしてください。

import pandas as pd
import numpy as np
from dowhy import CausalModel
import itertools

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    各説明変数と目的変数のペアに対して DoWhy による介入試験を行い、
    因果関係を「直接」「間接」「なし」に分類して DataFrame を返す。
    """

    results = []

    # 目的変数以外の列を説明変数とする
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    # 目的変数 × 説明変数の全組み合わせで因果推論
    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                # モデル識別
                identified_model = model.identify_effect()

                # 介入効果推定(回帰モデルなど)
                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression",
                    test_significance=True
                )

                pval = estimate.test_stat_significance['p_value']
                effect = estimate.value

                # 効果の有無を判定
                if pval < 0.05:
                    # DAG構造から直接エッジがあるか調べる
                    graph = model._graph._graph
                    if graph.has_edge(treatment, target):
                        relation = "直接の因果関係がある"
                    else:
                        relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "p値": pval,
                    "因果関係の種類": relation
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "p値": None,
                    "因果関係の種類": f"エラー: {str(e)}"
                })

    return pd.DataFrame(results)

assistant:

以下は、estimate.test_stat_significance["p_value"] の代わりに model.refute_estimate(...).refutation_result["p_value"] を使って、因果効果の信頼性(ロバストネス)に基づいて分類するように書き換えた関数です。

import pandas as pd
from dowhy import CausalModel

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    各説明変数と目的変数のペアに対して DoWhy による介入試験を行い、
    反事実検定の p 値に基づいて因果関係を「直接」「間接」「なし」に分類する。
    """

    results = []

    # 目的変数以外の列を説明変数とする
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                identified_model = model.identify_effect()

                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression"
                )

                # 反事実検定(placebo法)で信頼性評価
                refutation = model.refute_estimate(
                    identified_model,
                    estimate,
                    method_name="placebo_treatment_refuter"
                )

                # p値の取得(存在しない場合は None)
                pval = refutation.refutation_result.get("p_value", None)
                effect = estimate.value

                if pval is not None and pval > 0.05:
                    # DAG構造から直接エッジがあるか調べる
                    graph = model._graph._graph
                    if graph.has_edge(treatment, target):
                        relation = "直接の因果関係がある"
                    else:
                        relation = "間接的な因果関係がある"
                else:
                    relation = "因果関係はない(信頼性に欠ける可能性)"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "p値(反事実検定)": pval,
                    "因果関係の種類": relation
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "p値(反事実検定)": None,
                    "因果関係の種類": f"エラー: {str(e)}"
                })

    return pd.DataFrame(results)

🔍 補足ポイント

  • method_name="placebo_treatment_refuter" は比較的安全で一般的な方法です。目的に応じて data_subset_refuterrandom_common_cause などに変更できます。
  • pval > 0.05 のときに「信頼できる」効果とみなします(偽治療で差が出なかった=頑健)。

必要に応じて、複数の refuter を使って多数決で「信頼度」を決めることも可能です。それをご希望の場合はお知らせください。

user:

次のエラーになりました。

2035-05-29 14:13:58.894 Uncaught app execution
Traceback (most recent call last):
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/exec_code.py", line 121, in exec_func_with_error_handling
    result = func()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 639, in code_to_exec
    _mpa_v1(self._main_script_path)
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 159, in _mpa_v1
    page.run()
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/streamlit/navigation/page.py", line 297, in run
    exec(code, module.__dict__)
  File "/Users/ikemen/streamlit-app/pages/test.py", line 1027, in <module>
    st.dataframe(classify_causal_relationships(Xy_std, target_cols))
  File "/Users/ikemen/streamlit-app/pages/test.py", line 62, in classify_causal_relationships
    refutation = model.refute_estimate(
  File "/Users/ikemen/streamlit-app/venv/lib/python3.10/site-packages/dowhy/causal_model.py", line 453, in refute_estimate
    raise ValueError("Aborting refutation! No valid estimate is provided.")
ValueError: Aborting refutation! No valid estimate is provided.

assistant:

ありがとうございます。エラーメッセージ:

ValueError: Aborting refutation! No valid estimate is provided.

これは model.refute_estimate() に渡した estimate有効でない(正しく識別・推定されていない)ことを意味しています。よくある原因は以下の通りです:


✅ 【原因と対処】

🔹 原因

  • identify_effect() でモデル識別はできたように見えても、estimate_effect() に失敗し、内部的に estimate.valueNone になるケースがあります(例:共変量のマルチコリニアリティなど)。

✅ 【修正版】

estimate.valueNone の場合は refute_estimate() をスキップし、"因果関係なし" と判断する安全な対処を入れた関数が以下です:

import pandas as pd
from dowhy import CausalModel

def classify_causal_relationships(df: pd.DataFrame, target_vars: list) -> pd.DataFrame:
    """
    DoWhyによる因果推論と反事実検定に基づき、
    説明変数と目的変数の関係を「直接」「間接」「なし」に分類する。
    """

    results = []
    explanatory_vars = [col for col in df.columns if col not in target_vars]

    for target in target_vars:
        for treatment in explanatory_vars:
            try:
                model = CausalModel(
                    data=df,
                    treatment=treatment,
                    outcome=target,
                    common_causes="auto",
                    instruments=None,
                )

                identified_model = model.identify_effect()

                estimate = model.estimate_effect(
                    identified_model,
                    method_name="backdoor.linear_regression"
                )

                effect = estimate.value

                if effect is None:
                    relation = "因果関係はない(推定失敗)"
                    pval = None
                else:
                    # 反事実検定(placebo法)で信頼性チェック
                    refutation = model.refute_estimate(
                        identified_model,
                        estimate,
                        method_name="placebo_treatment_refuter"
                    )

                    pval = refutation.refutation_result.get("p_value", None)

                    if pval is not None and pval > 0.05:
                        graph = model._graph._graph
                        if graph.has_edge(treatment, target):
                            relation = "直接の因果関係がある"
                        else:
                            relation = "間接的な因果関係がある"
                    else:
                        relation = "因果関係はない(反事実的に検証不可)"

                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": effect,
                    "p値(反事実検定)": pval,
                    "因果関係の種類": relation
                })

            except Exception as e:
                results.append({
                    "説明変数": treatment,
                    "目的変数": target,
                    "推定効果": None,
                    "p値(反事実検定)": None,
                    "因果関係の種類": f"エラー: {str(e)}"
                })

    return pd.DataFrame(results)

✅ 今後の注意点

状況 対応策
estimate.value is None refute_estimate() をスキップ
refutation.refutation_result["p_value"]None 因果関係はない と扱うか「不明」と表示
refute_estimate() が途中でクラッシュ try-except で包み、診断ログを残すと安全

必要であれば、estimate.method_nameidentified_model.estimand_type などをロギングしてデバッグしやすくすることもできます。ご希望でしたらそのバージョンもご提供可能です。

user:

ありがとうございます。今日はこの辺で大丈夫です。今日の会話をブログにまとめたいので、タイトル案をいくつか考えていただけませんか?

assistant:

こちらこそ、ありがとうございました!
本日の会話をもとに、ブログ記事のタイトル案をいくつかご提案しますね。因果推論や DoWhy、そして実務的な工夫にフォーカスした内容からインスピレーションを得ています。


🧠 因果推論 × 実務ハック系タイトル案

  1. 「DoWhyで因果推論:エラーとp値をどう扱うべきか?」
  2. 「p値だけに頼らない!DoWhyでの因果関係の“実務的”な見極め方」
  3. 「DoWhyのrefute_estimateでハマったら読む記事」
  4. 「因果関係は本当にあるのか?DoWhyで直接・間接を見極める処方箋」
  5. 「エラーも“情報”になる:DoWhyで因果関係を分類する最前線」

💼 応用・実務寄りのニュアンスを強めた案

  1. 「DoWhyを実務投入するための因果関係分類テクニック」
  2. 「p値 vs 反事実検定:DoWhyの出力を信頼できるか?」
  3. 「因果推論の現場対応:DoWhyと例外処理のリアル」
  4. 「因果推論ライブラリDoWhyを実務でどう使うか?」
  5. 「DoWhy + Placeboで因果関係を見極める実践ガイド」

気になる方向性(実務寄り・学術寄り・初心者向けなど)があれば、さらに絞った提案もできます!お気軽にどうぞ。

user:

DoWhyだけでなくて、SEMとLiNGAMも使いましたので、それもタイトルに含めてください。

assistant:

了解しました!
DoWhy に加えて、SEM(構造方程式モデリング)LiNGAM(非ガウス性に基づく因果探索) も使われたとのことなので、それらを含んだタイトル案を以下にご提案します。


📌 総合的な因果推論ワークフローを意識したタイトル案

  1. 「SEM・LiNGAM・DoWhyで因果関係を多角的に検証する方法」
  2. 「因果推論3本立て:SEM・LiNGAM・DoWhyを実務でどう使い分けるか?」
  3. 「DoWhyで検証、LiNGAMで構造推定、SEMで確認:因果分析フローの実践」
  4. 「因果推論の三種の神器:SEM×LiNGAM×DoWhyを使いこなす」
  5. 「SEM・LiNGAM・DoWhyを併用して因果関係を分類・検証してみた」

🛠 実務&トラブル対応に焦点を当てた案

  1. 「DoWhyで詰まったら?LiNGAM・SEMも併用した因果関係の実務チェック法」
  2. 「エラーも味方に変える:SEM・LiNGAM・DoWhyで因果関係を堀り下げる」
  3. 「因果関係の“見える化”と“確かめ”:LiNGAMで推定、SEMで検証、DoWhyで反証」
  4. 「因果推論の立体交差:SEM・LiNGAM・DoWhyによる分類・検証フロー」
  5. 「SEM?LiNGAM?DoWhy?場面で使い分ける因果推論ライブラリの実践録」

ご希望があれば、本文構成案(見出し構成や導入文)も一緒にお手伝いできます!

user:

「SEM・LiNGAM・DoWhyを併用して因果関係を分類」でいきたいと思います。最後に、まとめを詳しく書いてください。

assistant:

素晴らしい選択です!
タイトル:「SEM・LiNGAM・DoWhyを併用して因果関係を分類」 にふさわしい、詳しいまとめを以下にご用意しました。


🧾 まとめ:SEM・LiNGAM・DoWhyを併用した因果関係の分類プロセス

本記事では、因果推論における3つの代表的なアプローチ ― SEM(構造方程式モデリング)LiNGAM(線形非ガウス構造モデル)DoWhy(介入効果の因果推論) ― を組み合わせて、複雑なデータから因果関係を 「直接」「間接」「なし」 に分類する実践的なプロセスを紹介しました。

それぞれの手法は以下のような特徴を持ち、補完的に使うことで因果構造に対する理解を深められます:


🔹 SEM(構造方程式モデリング)

  • 観測された変数間の 事前仮定(因果モデル)に基づいて構造方程式を構築し、因果パスの有意性(p値) を評価。
  • 利点:因果パスの統計的検証が可能。
  • 限界:モデル構造を事前に与える必要があるため、誤った仮定に敏感。

🔹 LiNGAM(非ガウス性に基づく因果構造推定)

  • 観測データの統計的独立性と非ガウス性に基づいて、データ駆動でDAG(因果グラフ) を推定。
  • 利点:事前知識がなくても因果構造を推定可能。
  • 限界:線形性・非ガウス性の仮定に依存。

🔹 DoWhy(介入効果の推定と反証)

  • 介入効果を定量的に推定し、さらに refutation(反証) によって結果の頑健性を検証。
  • 利点:介入に対する効果を数値化でき、**実務的な「因果テスト」**に向いている。
  • 限界:一部の手法で 内部構造へのアクセス制限やエラー が発生することがある(例:test_significanceの非対応問題など)。

🧩 3手法の併用がもたらす利点

SEMによる因果パスの検定、LiNGAMによる構造探索の客観性、そしてDoWhyによる介入効果の定量検証と反証性は、互いの弱点を補い合います。特に、1つの手法だけでは見逃しがちな 「間接的な因果関係」「誤った仮定による過信」 を減らすことができます。


💡 今後の展望

今後は次のような発展も考えられます:

  • 因果構造に基づいたフィルタリングや特徴量選択
  • 複数の推定結果のアンサンブル的活用(メタ因果推論)
  • 階層因果モデリング(中間変数・媒介要因を段階的に探る)

✅ 結論

複数の因果推論ライブラリを適切に組み合わせることで、より堅牢で多面的な因果関係の理解が可能になります。今回の試みは、実務における因果推論の「再現性・反証可能性・構造的理解」を高める一つのアプローチとして、有用な手がかりを提供できるでしょう。


ご希望があれば、このまとめをブログ用の整形済みMarkdownやHTMLに変換することもできます!

user:

ありがとうございます。これで大丈夫です。今後もまたよろしくお願いします!

assistant:

どういたしまして!今回の因果推論の整理、非常に実践的で応用範囲の広い取り組みでしたね。ブログの仕上がりもきっと多くの方の参考になると思います。

またいつでもご相談ください。今後ともよろしくお願いいたします!🌿

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?