{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Learning a CStree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows how to learn a CStree from observational data using an exhaustive search procedure. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The autoreload extension is already loaded. To reload it, use:\n", " %reload_ext autoreload\n" ] } ], "source": [ "import numpy as np\n", "import networkx as nx\n", "\n", "import cslearn.cstree as ct\n", "import cslearn.stage as st\n", "import cslearn.learning as ctl\n", "import cslearn.scoring as sc\n", "\n", "%load_ext autoreload\n", "%autoreload 2" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Create a CStree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first create a CStree which we know can be represented by a single DAG, shown below. We sample the stage probabilities from the Beta distribution with total pseudo counts 1." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "()\n", "\n", "ø\n", "\n", "\n", "\n", "(1,)\n", "\n", "1\n", "\n", "\n", "\n", "()->(1,)\n", "\n", "\n", "0.37\n", "\n", "\n", "\n", "(0,)\n", "\n", "0\n", "\n", "\n", "\n", "()->(0,)\n", "\n", "\n", "0.63\n", "\n", "\n", "\n", "(1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1,)->(1, 1)\n", "\n", "\n", "0.92\n", "\n", "\n", "\n", "(1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1,)->(1, 0)\n", "\n", "\n", "0.08\n", "\n", "\n", "\n", "(0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0,)->(0, 1)\n", "\n", "\n", "0.89\n", "\n", "\n", "\n", "(0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0,)->(0, 0)\n", "\n", "\n", "0.11\n", "\n", "\n", "\n", "(1, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 1)\n", "\n", "\n", "0.33\n", "\n", "\n", "\n", "(1, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 0)\n", "\n", "\n", "0.67\n", "\n", "\n", "\n", "(1, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 1)\n", "\n", "\n", "0.59\n", "\n", "\n", "\n", "(1, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 0)\n", "\n", "\n", "0.41\n", "\n", "\n", "\n", "(0, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 1)\n", "\n", "\n", "0.33\n", "\n", "\n", "\n", "(0, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 0)\n", "\n", "\n", "0.67\n", "\n", "\n", "\n", "(0, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 1)\n", "\n", "\n", "0.59\n", "\n", "\n", "\n", "(0, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 0)\n", "\n", "\n", "0.41\n", "\n", "\n", "\n", "a\n", "\n", "a\n", "\n", "\n", "\n", "b\n", "\n", "b\n", "\n", "\n", "\n", "a->b\n", "\n", "\n", "\n", "\n", "\n", "c\n", "\n", "c\n", "\n", "\n", "\n", "b->c\n", "\n", "\n", "\n", "\n", "\n", "-\n", "\n", "-\n", "\n", "\n", "\n", "-->a\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ ">" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(5)\n", "tree = ct.CStree([2] * 3, labels=[\"a\", \"b\", \"c\"])\n", "\n", "tree.update_stages(\n", " {\n", " 0: [{\"context\": {0: 0}}, {\"context\": {0: 1}}],\n", " 1: [{\"context\": {1: 0}}, {\"context\": {1: 1}}],\n", " }\n", ")\n", "\n", "tree.sample_stage_parameters(alpha=1.0)\n", "tree.plot(full=True)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of DAGs: 1\n", "Context: None\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "a\n", "\n", "a\n", "\n", "\n", "\n", "b\n", "\n", "b\n", "\n", "\n", "\n", "a->b\n", "\n", "\n", "\n", "\n", "\n", "c\n", "\n", "c\n", "\n", "\n", "\n", "b->c\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ ">" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "true_adags = tree.to_minimal_context_agraphs()\n", "print(\"Number of DAGs:\", len(true_adags))\n", "print(\"Context:\", list(true_adags.keys())[0])\n", "list(true_adags.values())[0]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Draw samples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We draw 10000 samples from this CStree." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "df = tree.sample(10000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calculate score tables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The structure learning (learning of CStree) uses pre-calculated scores. We restrict the number of variables in each context to be at most 2 and use the BDeu score with pseudo count 1. By letting poss_cvars=None, the possible context variables for a variable are unrestricted." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Context score tables: 100%|██████████| 3/3 [00:00<00:00, 160.33it/s]\n", "Creating #stagings tables: 100%|██████████| 3/3 [00:00<00:00, 2533.81it/s]\n", "Order score tables: 100%|██████████| 3/3 [00:00<00:00, 2697.88it/s]\n" ] } ], "source": [ "score_table, context_scores, _ = sc.order_score_tables(\n", " df, max_cvars=2, poss_cvars=None, alpha_tot=1.0, method=\"BDeu\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dict score_table contains the order score tables and context_scores, the scores for every context of every variable. " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Find the optimal order by exhaustive search" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first find the optimal order using an exhaustive search." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "optimal order: ['b', 'c', 'a'], score -16332.559328979401\n" ] } ], "source": [ "optord, score = ctl._find_optimal_order(score_table)\n", "print(\"optimal order: {}, score {}\".format(optord, score))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Find the optimal CStree (staging of each level) of the best order" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Given the optimal order, we find the optomial CStree consistent with this order." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "opttree = ctl._optimal_cstree_given_order(optord, context_scores)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We plot the tree before estimating the parameters." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "()\n", "\n", "ø\n", "\n", "\n", "\n", "(1,)\n", "\n", "1\n", "\n", "\n", "\n", "()->(1,)\n", "\n", "\n", "\n", "\n", "\n", "(0,)\n", "\n", "0\n", "\n", "\n", "\n", "()->(0,)\n", "\n", "\n", "\n", "\n", "\n", "(1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1,)->(1, 1)\n", "\n", "\n", "\n", "\n", "\n", "(1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1,)->(1, 0)\n", "\n", "\n", "\n", "\n", "\n", "(0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0,)->(0, 1)\n", "\n", "\n", "\n", "\n", "\n", "(0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0,)->(0, 0)\n", "\n", "\n", "\n", "\n", "\n", "(1, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 1)\n", "\n", "\n", "\n", "\n", "\n", "(1, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 0)\n", "\n", "\n", "\n", "\n", "\n", "(1, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 1)\n", "\n", "\n", "\n", "\n", "\n", "(1, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 0)\n", "\n", "\n", "\n", "\n", "\n", "(0, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 1)\n", "\n", "\n", "\n", "\n", "\n", "(0, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 0)\n", "\n", "\n", "\n", "\n", "\n", "(0, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 1)\n", "\n", "\n", "\n", "\n", "\n", "(0, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 0)\n", "\n", "\n", "\n", "\n", "\n", "b\n", "\n", "b\n", "\n", "\n", "\n", "c\n", "\n", "c\n", "\n", "\n", "\n", "b->c\n", "\n", "\n", "\n", "\n", "\n", "a\n", "\n", "a\n", "\n", "\n", "\n", "c->a\n", "\n", "\n", "\n", "\n", "\n", "-\n", "\n", "-\n", "\n", "\n", "\n", "-->b\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ ">" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opttree.plot(full=True)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Estimate the parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Estimate the stage parameters using the BDeu prior with a total pseudo count per level of 1." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "()\n", "\n", "ø\n", "\n", "\n", "\n", "(1,)\n", "\n", "1\n", "\n", "\n", "\n", "()->(1,)\n", "\n", "\n", "0.9\n", "\n", "\n", "\n", "(0,)\n", "\n", "0\n", "\n", "\n", "\n", "()->(0,)\n", "\n", "\n", "0.1\n", "\n", "\n", "\n", "(1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1,)->(1, 1)\n", "\n", "\n", "0.33\n", "\n", "\n", "\n", "(1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1,)->(1, 0)\n", "\n", "\n", "0.67\n", "\n", "\n", "\n", "(0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0,)->(0, 1)\n", "\n", "\n", "0.58\n", "\n", "\n", "\n", "(0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0,)->(0, 0)\n", "\n", "\n", "0.42\n", "\n", "\n", "\n", "(1, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 1)\n", "\n", "\n", "0.38\n", "\n", "\n", "\n", "(1, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 1)->(1, 1, 0)\n", "\n", "\n", "0.62\n", "\n", "\n", "\n", "(1, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 1)\n", "\n", "\n", "0.38\n", "\n", "\n", "\n", "(1, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(1, 0)->(1, 0, 0)\n", "\n", "\n", "0.62\n", "\n", "\n", "\n", "(0, 1, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 1)\n", "\n", "\n", "0.31\n", "\n", "\n", "\n", "(0, 1, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 1)->(0, 1, 0)\n", "\n", "\n", "0.69\n", "\n", "\n", "\n", "(0, 0, 1)\n", "\n", "1\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 1)\n", "\n", "\n", "0.31\n", "\n", "\n", "\n", "(0, 0, 0)\n", "\n", "0\n", "\n", "\n", "\n", "(0, 0)->(0, 0, 0)\n", "\n", "\n", "0.69\n", "\n", "\n", "\n", "b\n", "\n", "b\n", "\n", "\n", "\n", "c\n", "\n", "c\n", "\n", "\n", "\n", "b->c\n", "\n", "\n", "\n", "\n", "\n", "a\n", "\n", "a\n", "\n", "\n", "\n", "c->a\n", "\n", "\n", "\n", "\n", "\n", "-\n", "\n", "-\n", "\n", "\n", "\n", "-->b\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ ">" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opttree.estimate_stage_parameters(df, alpha_tot=1.0, method=\"BDeu\")\n", "opttree.plot(full=True)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Plot the minimal context DAGs" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of DAGs: 1\n", "Context: None\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "b\n", "\n", "b\n", "\n", "\n", "\n", "c\n", "\n", "c\n", "\n", "\n", "\n", "b->c\n", "\n", "\n", "\n", "\n", "\n", "a\n", "\n", "a\n", "\n", "\n", "\n", "b->a\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ ">" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opt_adags = opttree.to_minimal_context_agraphs()\n", "print(\"Number of DAGs:\", len(opt_adags))\n", "print(\"Context:\", list(opt_adags.keys())[0])\n", "list(opt_adags.values())[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that this has the same CSI/CI statement as the original DAG representation." ] } ], "metadata": { "kernelspec": { "display_name": "cstree_env", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }