Skip to content

plots

champalimaud.plots

Figures drawn from tables.

Each function returns a figure object and writes no file. Colors come from champalimaud.look.

jaccard_heatmaps(panels, types)

Draw four titled Jaccard matrices, shaded from 0 to 1.

Parameters:

Name Type Description Default
panels dict of str to numpy.ndarray

Four square matrices by title, as overlap.jaccard_matrix returns them, in reading order.

required
types sequence of str

The names of the rows and columns of every matrix.

required

Returns:

Type Description
Figure

A 2 x 2 grid with the value written in each cell.

See Also

champalimaud.overlap.jaccard_matrix : Builds each panel.

Examples:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> def panel():
...     m = rng.uniform(0, 1, (4, 4))
...     m = (m + m.T) / 2
...     np.fill_diagonal(m, 1)
...     return m
>>> panels = {
...     f"{side}, by {level}": panel()
...     for level in ("type", "cell")
...     for side in ("output", "input")
... }
>>> figure = jaccard_heatmaps(panels, ["T1", "T2", "T3", "T4"])

jaccard_heatmaps

Source code in champalimaud/plots.py
def jaccard_heatmaps(
    panels: dict[str, np.ndarray], types: Sequence[str]
) -> Figure:
    """Draw four titled Jaccard matrices, shaded from 0 to 1.

    Parameters
    ----------
    panels : dict of str to numpy.ndarray
        Four square matrices by title, as
        `overlap.jaccard_matrix` returns them, in reading order.
    types : sequence of str
        The names of the rows and columns of every matrix.

    Returns
    -------
    matplotlib.figure.Figure
        A 2 x 2 grid with the value written in each cell.

    See Also
    --------
    champalimaud.overlap.jaccard_matrix : Builds each panel.

    Examples
    --------
    >>> import numpy as np
    >>> rng = np.random.default_rng(0)
    >>> def panel():
    ...     m = rng.uniform(0, 1, (4, 4))
    ...     m = (m + m.T) / 2
    ...     np.fill_diagonal(m, 1)
    ...     return m
    >>> panels = {
    ...     f"{side}, by {level}": panel()
    ...     for level in ("type", "cell")
    ...     for side in ("output", "input")
    ... }
    >>> figure = jaccard_heatmaps(panels, ["T1", "T2", "T3", "T4"])
    """
    fig = Figure(figsize=(9, 8))
    axes = fig.subplots(2, 2)
    for ax, (title, matrix) in zip(axes.ravel(), panels.items(), strict=True):
        image = ax.imshow(matrix, vmin=0, vmax=1, cmap="Greys")
        ax.set_xticks(range(len(types)), types, rotation=45, ha="right")
        ax.set_yticks(range(len(types)), types)
        ax.set_title(title, fontsize=11)
        for i in range(len(types)):
            for j in range(len(types)):
                # Dark text on light cells and white on dark ones;
                # 0.55 is where the shading crosses mid-gray.
                ink = "white" if matrix[i, j] >= 0.55 else "black"
                ax.text(
                    j,
                    i,
                    f"{matrix[i, j]:.2f}",
                    ha="center",
                    va="center",
                    color=ink,
                    fontsize=9,
                )
        fig.colorbar(image, ax=ax, fraction=0.046)
    return fig

network_figure(edges, types, *, height='640px')

Draw a network of types and their partner types.

Parameters:

Name Type Description Default
edges DataFrame

Columns src, dst, synapses, and kind, which is "out", "in", or "lateral", as between_types.network_edges returns them.

required
types sequence of str

The types to draw large, each in its color; every other node is a partner type, in gray and larger the more edges it has.

required
height str

Height of the canvas, as a CSS length.

"640px"

Returns:

Type Description
Network

An interactive network. An edge is as wide as its synapse count allows, in the color of its kind.

See Also

champalimaud.between_types.network_edges : Picks the edges.

Examples:

>>> import polars as pl
>>> edges = pl.DataFrame(
...     {
...         "src": ["LC16", "X"],
...         "dst": ["X", "LC16"],
...         "synapses": [900, 40],
...         "kind": ["out", "in"],
...     }
... )
>>> network = network_figure(edges, ["LC16"])
>>> sorted(network.get_nodes())
['LC16', 'X']
Source code in champalimaud/plots.py
def network_figure(
    edges: pl.DataFrame, types: Sequence[str], *, height: str = "640px"
) -> Network:
    """Draw a network of types and their partner types.

    Parameters
    ----------
    edges : polars.DataFrame
        Columns ``src``, ``dst``, ``synapses``, and ``kind``, which is
        ``"out"``, ``"in"``, or ``"lateral"``, as
        `between_types.network_edges` returns them.
    types : sequence of str
        The types to draw large, each in its color; every other node is
        a partner type, in gray and larger the more edges it has.
    height : str, default "640px"
        Height of the canvas, as a CSS length.

    Returns
    -------
    pyvis.network.Network
        An interactive network.
        An edge is as wide as its synapse count allows, in the color of
        its kind.

    See Also
    --------
    champalimaud.between_types.network_edges : Picks the edges.

    Examples
    --------
    >>> import polars as pl
    >>> edges = pl.DataFrame(
    ...     {
    ...         "src": ["LC16", "X"],
    ...         "dst": ["X", "LC16"],
    ...         "synapses": [900, 40],
    ...         "kind": ["out", "in"],
    ...     }
    ... )
    >>> network = network_figure(edges, ["LC16"])
    >>> sorted(network.get_nodes())
    ['LC16', 'X']
    """
    net = Network(
        height=height, width="100%", directed=True, cdn_resources="in_line"
    )
    net.barnes_hut(gravity=-12000, spring_length=220, central_gravity=0.25)
    degree: dict[str, int] = {}
    for src, dst in zip(edges["src"], edges["dst"], strict=True):
        degree[src] = degree.get(src, 0) + 1
        degree[dst] = degree.get(dst, 0) + 1
    for name in types:
        net.add_node(
            name, label=name, color=TYPE_COLORS[name], size=40, borderWidth=2
        )
    for name in sorted(set(degree) - set(types)):
        # Larger for a partner with more edges, up to a cap, so the
        # hubs stand out without hiding the rest.
        net.add_node(
            name,
            label=name,
            color=CONTEXT_COLOR,
            size=12 + min(degree[name], 20),
        )
    colors = {
        "out": DIRECTION_COLORS["out"],
        "in": DIRECTION_COLORS["in"],
        "lateral": CONTEXT_COLOR,
    }
    for row in edges.iter_rows(named=True):
        # The width in pixels grows by one for every 300 synapses and is
        # clamped, so weak edges stay visible and strong ones do not
        # swamp the plot.
        net.add_edge(
            row["src"],
            row["dst"],
            width=max(1.5, min(8.0, row["synapses"] / 300)),
            color=colors[row["kind"]],
            title=f"{row['kind']} {row['synapses']} synapses",
        )
    return net

plot_neurons(neurons, colors=TYPE_COLORS)

Plot skeletons in 3D, colored by cell type.

Parameters:

Name Type Description Default
neurons sequence of navis.TreeNeuron

Named <type>_<root_id>, as load.load_skeletons returns them.

required
colors dict of str to str

Color of each type; look.TYPE_COLORS by default.

TYPE_COLORS

Returns:

Type Description
Figure

Drag to rotate, scroll to zoom.

Raises:

Type Description
RuntimeError

If navis returns no figure.

Examples:

>>> import pandas as pd
>>> nodes = pd.DataFrame(
...     {
...         "node_id": [1, 2],
...         "parent_id": [-1, 1],
...         "x": [0.0, 1.0],
...         "y": [0.0, 0.0],
...         "z": [0.0, 0.0],
...         "radius": [1.0, 1.0],
...     }
... )
>>> neuron = navis.TreeNeuron(nodes, name="LC16_1", units="nm")
>>> figure = plot_neurons([neuron])
>>> type(figure).__name__
'Figure'
Source code in champalimaud/plots.py
def plot_neurons(
    neurons: Sequence[navis.TreeNeuron],
    colors: dict[str, str] = TYPE_COLORS,
) -> PlotlyFigure:
    """Plot skeletons in 3D, colored by cell type.

    Parameters
    ----------
    neurons : sequence of navis.TreeNeuron
        Named ``<type>_<root_id>``, as `load.load_skeletons` returns
        them.
    colors : dict of str to str, optional
        Color of each type; `look.TYPE_COLORS` by default.

    Returns
    -------
    plotly.graph_objects.Figure
        Drag to rotate, scroll to zoom.

    Raises
    ------
    RuntimeError
        If navis returns no figure.

    Examples
    --------
    >>> import pandas as pd
    >>> nodes = pd.DataFrame(
    ...     {
    ...         "node_id": [1, 2],
    ...         "parent_id": [-1, 1],
    ...         "x": [0.0, 1.0],
    ...         "y": [0.0, 0.0],
    ...         "z": [0.0, 0.0],
    ...         "radius": [1.0, 1.0],
    ...     }
    ... )
    >>> neuron = navis.TreeNeuron(nodes, name="LC16_1", units="nm")
    >>> figure = plot_neurons([neuron])
    >>> type(figure).__name__
    'Figure'
    """
    fill = [colors[n.name.split("_")[0]] for n in neurons]
    figure = navis.plot3d(
        list(neurons), color=fill, backend="plotly", inline=False
    )
    # plot3d's return type allows None for inline backends;
    # plotly with inline=False always returns a figure.
    if figure is None:
        msg = "navis.plot3d returned no figure"
        raise RuntimeError(msg)
    return figure

top_partners_figure(out_mass, in_mass, types, *, shown=15)

Draw the heaviest partner types of each type as bars.

Parameters:

Name Type Description Default
out_mass DataFrame

Columns type, partner_type, and synapses, as strength.type_mass returns them for outputs and for inputs.

required
in_mass DataFrame

Columns type, partner_type, and synapses, as strength.type_mass returns them for outputs and for inputs.

required
types sequence of str

The types, one column of panels each, in their colors.

required
shown int

Bars per panel.

15

Returns:

Type Description
Figure

Outputs in the top row, inputs in the bottom row.

Examples:

>>> import polars as pl
>>> mass = pl.DataFrame(
...     {
...         "type": ["LC16", "LC16"],
...         "partner_type": ["X", "Y"],
...         "synapses": [9, 4],
...     }
... )
>>> figure = top_partners_figure(mass, mass, ["LC16"])
>>> len(figure.axes)
2

top_partners_figure

Source code in champalimaud/plots.py
def top_partners_figure(
    out_mass: pl.DataFrame,
    in_mass: pl.DataFrame,
    types: Sequence[str],
    *,
    shown: int = 15,
) -> Figure:
    """Draw the heaviest partner types of each type as bars.

    Parameters
    ----------
    out_mass, in_mass : polars.DataFrame
        Columns ``type``, ``partner_type``, and ``synapses``, as
        `strength.type_mass` returns them for outputs and for inputs.
    types : sequence of str
        The types, one column of panels each, in their colors.
    shown : int, default 15
        Bars per panel.

    Returns
    -------
    matplotlib.figure.Figure
        Outputs in the top row, inputs in the bottom row.

    Examples
    --------
    >>> import polars as pl
    >>> mass = pl.DataFrame(
    ...     {
    ...         "type": ["LC16", "LC16"],
    ...         "partner_type": ["X", "Y"],
    ...         "synapses": [9, 4],
    ...     }
    ... )
    >>> figure = top_partners_figure(mass, mass, ["LC16"])
    >>> len(figure.axes)
    2
    """
    fig = Figure(figsize=(14, 8))
    axes = fig.subplots(2, len(types), squeeze=False)
    for col, t in enumerate(types):
        for row, (mass, direction) in enumerate(
            [(out_mass, "out"), (in_mass, "in")]
        ):
            ax = axes[row][col]
            top = (
                mass.filter(pl.col("type") == t)
                .sort("synapses", descending=True)
                .head(shown)
                .sort("synapses")
            )
            ax.barh(top["partner_type"], top["synapses"], color=TYPE_COLORS[t])
            ax.set_title(f"{t} {direction}", color=TYPE_COLORS[t])
            ax.tick_params(labelsize=8)
            ax.set_xlabel("synapses")
    return fig