Skip to content

Plot backends

Base

Abstract base class for plot backends.

AbstractSomPlot

Bases: ABC

SomPlot.

Source code in src/somap/plot_backends/base.py
class AbstractSomPlot(ABC):
    """SomPlot."""

    @abstractmethod
    def __init__(self, som, *, show_prototypes=True, show_activity=False, **kwargs):
        """Creates a SomPlot.

        Args:
            som: Self-Organizing Map as a Som object.
            args: Positional arguments passed to the `SomPlot` backend.
            show_prototypes: Show prototypes of each node as an image.
            show_activity: Show activity value of each node as a color.
            kwargs: Keyword arguments passed to the `SomPlot` backend.
        """
        raise NotImplementedError

    @abstractmethod
    def plot(self):
        """Returns the plot chart."""
        raise NotImplementedError

    @abstractmethod
    def save(self):
        """Saves the plot as an image."""
        raise NotImplementedError

__init__(som, *, show_prototypes=True, show_activity=False, **kwargs) abstractmethod

Creates a SomPlot.

Parameters:

Name Type Description Default
som

Self-Organizing Map as a Som object.

required
args

Positional arguments passed to the SomPlot backend.

required
show_prototypes

Show prototypes of each node as an image.

True
show_activity

Show activity value of each node as a color.

False
kwargs

Keyword arguments passed to the SomPlot backend.

{}
Source code in src/somap/plot_backends/base.py
@abstractmethod
def __init__(self, som, *, show_prototypes=True, show_activity=False, **kwargs):
    """Creates a SomPlot.

    Args:
        som: Self-Organizing Map as a Som object.
        args: Positional arguments passed to the `SomPlot` backend.
        show_prototypes: Show prototypes of each node as an image.
        show_activity: Show activity value of each node as a color.
        kwargs: Keyword arguments passed to the `SomPlot` backend.
    """
    raise NotImplementedError

plot() abstractmethod

Returns the plot chart.

Source code in src/somap/plot_backends/base.py
@abstractmethod
def plot(self):
    """Returns the plot chart."""
    raise NotImplementedError

save() abstractmethod

Saves the plot as an image.

Source code in src/somap/plot_backends/base.py
@abstractmethod
def save(self):
    """Saves the plot as an image."""
    raise NotImplementedError

Altair

Altair plot backends.

SomPlot

Bases: AbstractSomPlot

SomPlot.

Source code in src/somap/plot_backends/altair.py
class SomPlot(AbstractSomPlot):
    """SomPlot."""

    def __init__(
        self,
        som: AbstractSom,
        *,
        show_prototypes=True,
        show_activity=False,
        **kwargs,
    ):
        """Creates a SomPlot.

        Args:
            som: Self-Organizing Map as a Som object.
            args: Positional arguments passed to the `SomPlot` backend.r.ravel()
            show_prototypes: Show prototypes of each node as an image.
            show_activity: Show activity value of each node as a color.
            kwargs: Keyword arguments passed to the `SomPlot` backend.
        """
        _images = som.w_bu.reshape(som.shape + som.input_shape)
        _values = som.i_act_nb

        kwargs1 = {k[4:]: v for k, v in kwargs.items() if k.startswith("img_")}
        kwargs2 = {k[4:]: v for k, v in kwargs.items() if k.startswith("plt_")}

        chart_data = _encode_plot_data(images=_images, values=_values, **kwargs1)
        self.chart = plot(
            chart_data,
            som.shape,
            som.topography,
            images=show_prototypes,
            values=show_activity,
            **kwargs2,
        )

    def plot(self):
        """Returns the plot chart."""
        return self.chart

    def save(self, filename: str | IO, *args, **kwargs):
        """Saves the plot as an image."""
        self.chart.save(filename)

__init__(som, *, show_prototypes=True, show_activity=False, **kwargs)

Creates a SomPlot.

Parameters:

Name Type Description Default
som AbstractSom

Self-Organizing Map as a Som object.

required
args

Positional arguments passed to the SomPlot backend.r.ravel()

required
show_prototypes

Show prototypes of each node as an image.

True
show_activity

Show activity value of each node as a color.

False
kwargs

Keyword arguments passed to the SomPlot backend.

{}
Source code in src/somap/plot_backends/altair.py
def __init__(
    self,
    som: AbstractSom,
    *,
    show_prototypes=True,
    show_activity=False,
    **kwargs,
):
    """Creates a SomPlot.

    Args:
        som: Self-Organizing Map as a Som object.
        args: Positional arguments passed to the `SomPlot` backend.r.ravel()
        show_prototypes: Show prototypes of each node as an image.
        show_activity: Show activity value of each node as a color.
        kwargs: Keyword arguments passed to the `SomPlot` backend.
    """
    _images = som.w_bu.reshape(som.shape + som.input_shape)
    _values = som.i_act_nb

    kwargs1 = {k[4:]: v for k, v in kwargs.items() if k.startswith("img_")}
    kwargs2 = {k[4:]: v for k, v in kwargs.items() if k.startswith("plt_")}

    chart_data = _encode_plot_data(images=_images, values=_values, **kwargs1)
    self.chart = plot(
        chart_data,
        som.shape,
        som.topography,
        images=show_prototypes,
        values=show_activity,
        **kwargs2,
    )

plot()

Returns the plot chart.

Source code in src/somap/plot_backends/altair.py
def plot(self):
    """Returns the plot chart."""
    return self.chart

save(filename, *args, **kwargs)

Saves the plot as an image.

Source code in src/somap/plot_backends/altair.py
def save(self, filename: str | IO, *args, **kwargs):
    """Saves the plot as an image."""
    self.chart.save(filename)

plot(data, shape, topography, *, bin_size_scale=1.0, w_scale=1.0, h_scale=1.0, values=True, images=True, tooltip=True, color_scale=alt.Scale(scheme='lighttealblue'), color_legend=None, title=None)

Return an Altair chart containing shape[0] * shape[1] hexagons.

Parameters:

Name Type Description Default
data DataFrame

Pandas dataframe with the following columns: x: Horizontal coordinate in the hex grid. y: Vertical coordonates in the hex grid. values: one scalar value per hexagon (optional). images: one encoded image per hexagon (optional). image: one encoded image per hexagon (for tooltip) (optional). infos: one text value per hexagon(optional).

required
shape tuple[int, int]

column and line numbers of the hex grid.

required
topography str

Topography of the 2D map, either 'square' or 'hex'.

required
bin_size_scale float

size of the hexagons.

1.0
w_scale float

horizontal scaling factor (sometimes needed to go around rendering bugs of external libs).

1.0
h_scale float

vertical scaling factor (sometimes needed to go around rendering bugs of external libs).

1.0
values bool

if True, fill the hexagons with a color corresponding to data['values'].

True
images bool

if True, show the image of all hexagon with an image corresponding to data['images'].

True
tooltip bool

if True, show an interactive tooltip given detailed values of the selected hexagon

True
color_scale Scale

color scale used if color is True. Continous scale from light blue to dark blue by default.

Scale(scheme='lighttealblue')
color_legend Legend | None

arr color legend used if color is True. Dynamic legend by default. (set to 'None' to hide the legend).

None
title str | None

Title of the plot.

None

Returns:

Type Description
LayerChart

The altair chart.

Source code in src/somap/plot_backends/altair.py
def plot(
    data: pd.DataFrame,
    shape: tuple[int, int],
    topography: str,
    *,
    bin_size_scale: float = 1.0,
    w_scale: float = 1.0,
    h_scale: float = 1.0,
    values: bool = True,
    images: bool = True,
    tooltip: bool = True,
    color_scale: alt.Scale = alt.Scale(scheme="lighttealblue"),
    color_legend: alt.Legend | None = None,
    title: str | None = None,
) -> alt.LayerChart:
    """Return an Altair chart containing shape[0] * shape[1] hexagons.

    Args:
        data: Pandas dataframe with the following columns:
            x: Horizontal coordinate in the hex grid.
            y: Vertical coordonates in the hex grid.
            values: one scalar value per hexagon (optional).
            images: one encoded image per hexagon (optional).
            image: one encoded image per hexagon (for tooltip) (optional).
            infos: one text value per hexagon(optional).
        shape: column and line numbers of the hex grid.
        topography: Topography of the 2D map, either 'square' or 'hex'.
        bin_size_scale: size of the hexagons.
        w_scale: horizontal scaling factor (sometimes needed to go around rendering
            bugs of external libs).
        h_scale: vertical scaling factor (sometimes needed to go around rendering bugs
            of external libs).
        values: if True, fill the hexagons with a color corresponding to data['values'].
        images: if True, show the image of all hexagon with an image corresponding to
            data['images'].
        tooltip: if True, show an interactive tooltip given detailed values of the
            selected hexagon
        color_scale: color scale used if color is True. Continous scale from light blue
            to dark blue by default.
        color_legend:arr color legend used if color is True. Dynamic legend by default.
            (set to 'None' to hide the legend).
        title: Title of the plot.

    Returns:
        The altair chart.
    """
    if values and "value" not in data.columns:
        raise ValueError(
            "Chart data must contain the 'values' field to fill colors. \
            If you don't want to fill colors, use the `color=False` argument"
        )

    if images and "image" not in data.columns:
        raise ValueError(
            "Chart data must contain the 'images' field to show images. \
            If you don't want to show images, use the `images=False` argument"
        )

    x_nb, y_nb = shape

    if topography == "hex":
        marker_shape = "M0,-2.3094L2,-1.1547 2,1.1547 0,2.3094 -2,1.1547 -2,-1.1547Z"
        bin_size = int(22 * bin_size_scale)
        w_scale = 2 * w_scale
        h_scale = 1.6 * h_scale
    elif topography == "square":
        marker_shape = "M 1 -1 L 1 1 L -1 1 L -1 -1 Z"
        bin_size = int(40 * bin_size_scale)
        w_scale = 0.9 * w_scale
        h_scale = 0.9 * h_scale
    else:
        raise ValueError(
            f"Topography must be either 'square' or 'hex' (not {topography})"
        )

    _axis = alt.Axis(title="", labels=False, grid=False, tickOpacity=0, domainOpacity=0)

    chart = (
        alt.Chart(data)
        .encode(
            x=alt.X("x:Q", axis=_axis),
            y=alt.Y("y:Q", axis=_axis),
            stroke=alt.value("black"),
            strokeWidth=alt.value(0.2),
        )
        .properties(width=w_scale * bin_size * x_nb, height=h_scale * bin_size * y_nb)
    )

    if values:
        chart = chart.encode(
            fill=alt.Color("value:Q", scale=color_scale, legend=color_legend)
        )

    if images:
        chart = chart.encode(url="image")

    if tooltip:
        chart = chart.encode(
            tooltip=["x", "y"]
            + ["image"] * ("image" in data.columns)
            + ["value:Q"] * ("value" in data.columns)
        )

    if topography == "hex":
        chart = chart.encode(x=alt.X("xPos:Q", axis=_axis))
        chart = chart.transform_calculate(
            xPos="(datum.x + 1 / 2 * ((datum.y + 1) % 2))"
        )

    if title is not None:
        chart = chart.properties(title=title)

    c = alt.layer(
        chart.mark_point(size=bin_size**2, shape=marker_shape),
        chart.mark_image(width=25, height=25),
    ).configure_view(strokeWidth=0)

    return c

Array2image

Array2image plot backends.

SomPlot

Bases: AbstractSomPlot

SomPlot.

Source code in src/somap/plot_backends/array2image.py
class SomPlot(AbstractSomPlot):
    """SomPlot."""

    def __init__(
        self,
        som: AbstractSom,
        *,
        show_prototypes=True,
        show_activity=False,
        **kwargs,
    ):
        """Creates a SomPlot.

        Args:
            som: Self-Organizing Map as a Som object.
            args: Positional arguments passed to the `SomPlot` backend.r.ravel()
            show_prototypes: Show prototypes of each node as an image.
            show_activity: Show activity value of each node as a color.
            kwargs: Keyword arguments passed to the `SomPlot` backend.
        """
        if som.topography != "square":
            raise ValueError(
                f"The 'array2image' plot backend only support the "
                f"'square' topography, not the '{som.topography}'."
            )

        data = som.w_bu.reshape(som.shape + som.input_shape)
        self.image = array_to_image(data, **kwargs)
        self.image = add_text_border(self.image, f"{som.params}")

    def plot(self):
        """Returns the plot chart."""
        return self.image

    def save(self, filename: str, *args, **kwargs):
        """Saves the plot as an image."""
        self.image.save(filename, *args, **kwargs)

__init__(som, *, show_prototypes=True, show_activity=False, **kwargs)

Creates a SomPlot.

Parameters:

Name Type Description Default
som AbstractSom

Self-Organizing Map as a Som object.

required
args

Positional arguments passed to the SomPlot backend.r.ravel()

required
show_prototypes

Show prototypes of each node as an image.

True
show_activity

Show activity value of each node as a color.

False
kwargs

Keyword arguments passed to the SomPlot backend.

{}
Source code in src/somap/plot_backends/array2image.py
def __init__(
    self,
    som: AbstractSom,
    *,
    show_prototypes=True,
    show_activity=False,
    **kwargs,
):
    """Creates a SomPlot.

    Args:
        som: Self-Organizing Map as a Som object.
        args: Positional arguments passed to the `SomPlot` backend.r.ravel()
        show_prototypes: Show prototypes of each node as an image.
        show_activity: Show activity value of each node as a color.
        kwargs: Keyword arguments passed to the `SomPlot` backend.
    """
    if som.topography != "square":
        raise ValueError(
            f"The 'array2image' plot backend only support the "
            f"'square' topography, not the '{som.topography}'."
        )

    data = som.w_bu.reshape(som.shape + som.input_shape)
    self.image = array_to_image(data, **kwargs)
    self.image = add_text_border(self.image, f"{som.params}")

plot()

Returns the plot chart.

Source code in src/somap/plot_backends/array2image.py
def plot(self):
    """Returns the plot chart."""
    return self.image

save(filename, *args, **kwargs)

Saves the plot as an image.

Source code in src/somap/plot_backends/array2image.py
def save(self, filename: str, *args, **kwargs):
    """Saves the plot as an image."""
    self.image.save(filename, *args, **kwargs)

add_text_border(image, text, border_height=50, font_size=20, font_path=FONT_PATH)

Adds a header with a text to an image.

Source code in src/somap/plot_backends/array2image.py
def add_text_border(image, text, border_height=50, font_size=20, font_path=FONT_PATH):
    """Adds a header with a text to an image."""
    # Original image size
    width, height = image.size

    # New image size (with border)
    new_height = height + border_height
    new_image = Image.new("RGB", (width, new_height), "white")

    # Paste the original image onto the new image
    new_image.paste(image, (0, border_height))

    # Draw the text
    draw = ImageDraw.Draw(new_image)
    font = ImageFont.truetype(str(font_path), 14)

    draw.text((0, 0), text, font=font, fill="black")

    return new_image