{ "cells": [ { "cell_type": "markdown", "id": "viz-0", "metadata": { "cell_marker": "\"\"\"" }, "source": "# Drawing a recovered structure\n\n[](https://colab.research.google.com/github/Abel-ai-lab/andrey/blob/main/examples/viz.ipynb) {download}`Download this notebook <../../../examples/viz.ipynb>`\n\n`andrey.viz.draw` renders a recovered graph to a self-contained SVG. The grammar is simple:\nmarks are drawn by shape (arrowhead, hollow circle, tail), so\ncolor stays a redundant channel; resolved edges are a calm graphite backbone and color is reserved\nfor what remains uncertain." }, { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", "metadata": {}, "source": [ "## Start\n", "\n", "Choose a CPU runtime and run this cell first. In Colab it installs `andrey-core==0.9.0` from PyPI;\n", "local kernels use the installed environment." ] }, { "cell_type": "code", "execution_count": 1, "id": "acae54e37e7d407bbb7b55eff062a284", "metadata": { "tags": [ "colab-setup" ] }, "outputs": [], "source": [ "import os\n", "import sys\n", "\n", "IN_COLAB = \"google.colab\" in sys.modules\n", "\n", "if IN_COLAB:\n", " os.environ[\"ANDREY_DEVICE\"] = \"cpu\"\n", " !pip install --quiet \"andrey-core[viz]==0.9.0\" pandas" ] }, { "cell_type": "code", "execution_count": 2, "id": "viz-1", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "import andrey\n", "import andrey.viz" ] }, { "cell_type": "markdown", "id": "viz-2", "metadata": {}, "source": [ "Recover a CPDAG from linear-Gaussian data over a four-node structure: a fork `x0 -> x1`,\n", "`x0 -> x2`, closing into a collider at `x3`. The columns come in an order drawn from the seed, so\n", "their order says nothing about the causal order." ] }, { "cell_type": "code", "execution_count": 3, "id": "viz-3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "kind: cpdag\n", "edges: [('x1', 'x3', 'directed'), ('x1', 'x0', 'undirected'), ('x2', 'x3', 'directed'), ('x2', 'x0', 'undirected')]\n" ] } ], "source": [ "EDGES = [(\"x0\", \"x1\"), (\"x0\", \"x2\"), (\"x1\", \"x3\"), (\"x2\", \"x3\")] # cause first\n", "\n", "\n", "def sample(seed: int, n: int = 2000, shuffle: bool = True) -> pd.DataFrame:\n", " \"\"\"Draw n rows of x0..x3, the columns in an order drawn from the seed.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " x0 = rng.standard_normal(n)\n", " x1 = 0.9 * x0 + rng.standard_normal(n)\n", " x2 = 0.8 * x0 + rng.standard_normal(n)\n", " x3 = 0.9 * x1 + 0.9 * x2 + rng.standard_normal(n)\n", " frame = pd.DataFrame({\"x0\": x0, \"x1\": x1, \"x2\": x2, \"x3\": x3})\n", " return frame[rng.permutation(frame.columns)] if shuffle else frame\n", "\n", "\n", "data = sample(seed=0)\n", "out = andrey.ges(data)\n", "print(\"kind:\", out.structure.kind)\n", "print(\"edges:\", out.structure.oriented_edges())" ] }, { "cell_type": "markdown", "id": "viz-4", "metadata": {}, "source": [ "Draw it in both themes at once. `draw()` returns an `SVG`, a `str` subclass carrying a\n", "`_repr_html_`, so composing a figure is plain string assembly: the `Panels` helper below\n", "stitches the light and dark renders into one captioned row, and every piece it stitches is markup\n", "`draw()` already produced. Both palettes are first-class, so a figure is never shipped in one\n", "theme only. The DataFrame's column names label the nodes: the collider at `x3` is oriented (both\n", "edges the graphite backbone), and whatever the CPDAG leaves unoriented is the undirected amber." ] }, { "cell_type": "code", "execution_count": 4, "id": "viz-5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "