Export results#
PyPhi’s result objects and substrates have their own analysis methods, but
you will often want to hand the numbers to another tool: a plotting library, a
statistics package, or a probabilistic-graphical-model toolkit. This guide
shows the three export paths PyPhi provides: a Pandas DataFrame view of a
cause-effect structure, a labeled xarray view of a substrate’s transition
probabilities, and a dynamic Bayesian network view of a substrate’s dynamics.
import pyphi
pyphi.config.progress_bars = False
We use the IIT 4.0 paper’s three-unit Fig 1A substrate throughout.
from pyphi import examples
substrate = examples.iit4_2023_fig1a_substrate()
state = (0, 1, 1)
Cause-effect structures as a DataFrame#
Call .to_pandas() on a cause-effect structure to get one row per distinction,
indexed by mechanism, with the mechanism state, cause and effect purviews, and
\(\varphi\).
analysis = pyphi.analyze(substrate, state)
ces = analysis.ces
df = ces.to_pandas()
df
| phi | mechanism_state | cause_purview | cause_state | effect_purview | effect_state | |
|---|---|---|---|---|---|---|
| mechanism | ||||||
| (A,) | 0.428350 | (0,) | (B,) | (0,) | (B, C) | (0, 0) |
| (B,) | 0.523043 | (1,) | (C,) | (0,) | (A,) | (1,) |
| (A, B) | 0.193358 | (0, 1) | (B,) | (0,) | (A, B) | (1, 0) |
| (A, C) | 0.090205 | (0, 1) | (A,) | (1,) | (B,) | (0,) |
| (B, C) | 0.141602 | (1, 1) | (A,) | (1,) | (B,) | (0,) |
| (A, B, C) | 0.071440 | (0, 1, 1) | (A, B) | (1, 0) | (A, B) | (1, 0) |
The result is an ordinary DataFrame, so the full Pandas API is available for
filtering, aggregation, and export to CSV, Excel, or Parquet.
df["phi"].sum()
np.float64(1.447998863725724)
The Analysis object itself also has a .to_pandas() returning a single-row
summary of the system-level quantities.
analysis.to_pandas()
| phi | normalized_phi | big_phi | n_distinctions | sum_phi_d | sum_phi_r | |
|---|---|---|---|---|---|---|
| 0 | 0.133873 | 0.066937 | 4.365675 | 6 | 1.447999 | 2.917676 |
Transition probabilities as an xarray Dataset#
A substrate’s transition probability matrix exports to a labeled
xarray.Dataset via substrate.tpm.to_xarray(). Each unit becomes a data
variable holding its conditional distribution over the next state, with named
dimensions and integer coordinates drawn from the state space. This requires
the optional xarray dependency (pip install pyphi[xarray]).
ds = substrate.tpm.to_xarray()
ds
<xarray.Dataset> Size: 480B
Dimensions: (u0: 2, u1: 2, u2: 2, u0_next: 2, u1_next: 2, u2_next: 2)
Coordinates:
* u0 (u0) int64 16B 0 1
* u1 (u1) int64 16B 0 1
* u2 (u2) int64 16B 0 1
* u0_next (u0_next) int64 16B 0 1
* u1_next (u1_next) int64 16B 0 1
* u2_next (u2_next) int64 16B 0 1
Data variables:
unit_0 (u0, u1, u2, u0_next) float64 128B 0.8808 0.1192 ... 0.1192 0.8808
unit_1 (u0, u1, u2, u1_next) float64 128B 0.2315 0.7685 ... 0.7685 0.2315
unit_2 (u0, u1, u2, u2_next) float64 128B 0.832 0.168 0.5 ... 0.168 0.832Named dimensions make it straightforward to select a conditional slice by state rather than by raw axis position. Here we read unit 0’s next-state distribution given that its inputs are all off.
ds["unit_0"].sel(u0=0, u1=0, u2=0).values
array([0.88079708, 0.11920292])
Dynamics as a dynamic Bayesian network#
A substrate’s transition dynamics can be exported as a two-timeslice dynamic Bayesian network. Two forms are available.
substrate.to_dbn_dict() returns a plain dictionary with no third-party
dependency. It has three keys: variables maps each node label to its alphabet
size, edges lists the inter-slice parent-to-child pairs (implicitly from time
\(t\) to \(t+1\)), and cpds maps each node to its parents and its conditional
probability table.
dbn = substrate.to_dbn_dict()
print("variables:", dbn["variables"])
print("edges: ", dbn["edges"])
print("A parents:", dbn["cpds"]["A"]["parents"])
variables: {'A': 2, 'B': 2, 'C': 2}
edges: [('A', 'A'), ('B', 'A'), ('A', 'B'), ('B', 'B'), ('C', 'B'), ('A', 'C'), ('C', 'C')]
A parents: ['A', 'B']
substrate.to_dbn() returns the same structure as a networkx.DiGraph, ready
for graph tooling. Each node label X becomes two variables, (X, 0) at time
\(t\) and (X, 1) at time \(t+1\), connected only by inter-slice edges. The graph
is therefore acyclic even when the substrate has feedback. This requires the
visualize extra (pip install pyphi[visualize]), which provides networkx.
g = substrate.to_dbn()
print("nodes:", sorted(g.nodes, key=str))
print("edges:", sorted(g.edges, key=str))
nodes: [('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]
edges: [(('A', 0), ('A', 1)), (('A', 0), ('B', 1)), (('A', 0), ('C', 1)), (('B', 0), ('A', 1)), (('B', 0), ('B', 1)), (('C', 0), ('B', 1)), (('C', 0), ('C', 1))]
Each next-timeslice node stores its conditional probability table and its ordered parent labels as node attributes.
g.nodes[("A", 1)]["parents"]
['A', 'B']
Where to go next#
Save and load results writes any PyPhi result to disk and reads it back.
Sweep states and subsystems produces the DataFrames these exports come from.
Visualize results renders a structure instead of exporting it.