Skip to content

geom_streamline

geom_streamline(
    mapping: FeatureSpec | None = None,
    *,
    data: PlotData | None = None,
    stat: str | None = None,
    position: Position | None = None,
    show_legend: bool | None = None,
    inherit_aes: bool | None = None,
    manual_key: ManualKey | None = None,
    sampling: Sampling | None = None,
    tooltips: Tooltips | None = None,
    arrow: FeatureSpec | _DefaultArrow | None = arrow(
        angle=26.6, length=6, type="closed"
    ),
    density: Number | tuple[Number, Number] | None = None,
    minlength: Number | None = None,
    maxlength: Number | None = None,
    integration_direction: Literal | None = None,
    broken_streamlines: bool | None = None,
    gridded: bool | None = None,
    grid_density: Number | None = None,
    smooth: Number | None = None,
    n_neighbors: int | None = None,
    min_mass: Number | None = None,
    cutoff_percentile: Number | None = None,
    color_by: str | None = None,
    na_rm: bool | None = None,
    **other_args: Any,
) -> LayerSpec

Streamlines of a vector field, with an arrowhead along each line.

As geom_streamline in the R package metR, with matplotlib's algorithm: the lines and arrows are the ones matplotlib.pyplot.streamplot draws. The rows give positions (x, y) and vectors (u, v):

  • rows on a regular grid (every pair of the distinct x and y values exactly once, such as wind or flow measured on a grid) are the field as given;
  • any other rows (scattered points with a direction each, such as cells with RNA velocities on an embedding) are averaged onto a grid first: each grid point takes the Gaussian-weighted mean vector of its nearest rows (scVelo's compute_velocity_on_grid; velocity_grid() returns that grid).

Lines are computed per group and facet panel. Styled as geom_path; a mapped style is constant per group (its mean or first value), so map color to a grouping variable, not to a value that varies along the flow. Rows count as a grid only when their coordinates repeat exactly; others are smoothed.

Parameters:

  • mapping (FeatureSpec, default: None ) –

    Aesthetic mapping from aes(). Needs x, y, u (x component of the vector) and v (y component). color or group gives each group its own field and lines.

  • data (DataFrame or dict, default: None ) –

    Layer data; inherits the plot data when None.

  • stat (str, default: None ) –

    Statistical transformation, by name; "streamline" by default.

  • position (str or FeatureSpec, default: None ) –

    Position adjustment, applied to the line vertices.

  • show_legend (bool, default: None ) –

    False hides this layer from the legend.

  • inherit_aes (bool, default: None ) –

    False ignores the plot's default mapping for this layer.

  • manual_key (str or FeatureSpec, default: None ) –

    This layer's key in a manual legend: a label, or layer_key().

  • sampling ((str, int or FeatureSpec), default: None ) –

    Rows to draw: a sampling_*() spec, an int n (sampling_random(n)) or "none". Default: every row.

  • tooltips (FeatureSpec or list of str, default: None ) –

    Tooltip content: layer_tooltips(), a list of column names, or "none".

  • arrow (FeatureSpec or None, default: arrow(angle=26.6, length=6, type="closed") ) –

    The arrowhead half way along each line, from arrow() (matplotlib's '-|>' head by default). None draws the lines only.

  • density (float or tuple of float, default: 1 ) –

    Closeness of the lines. density=1 divides the field into a 30 x 30 mask, each cell crossed by at most one line. A tuple sets x and y apart.

  • minlength (float, default: 0.1 ) –

    Lines shorter than this (in axes coordinates, the field being 1 x 1) are dropped.

  • maxlength (float, default: 4.0 ) –

    Longest line, in axes coordinates.

  • integration_direction ((both, forward, backward), default: "both" ) –

    Integrate along the field, against it, or both (each half as long).

  • broken_streamlines (bool, default: True ) –

    Stop a line when it comes too close to another. False lets lines run on.

  • gridded (bool, default: None ) –

    Whether the rows are a regular grid. By default, rows that form a complete grid are one, other rows are scattered. True also accepts a grid with missing points (lines stop there); False always averages onto a grid.

  • grid_density (float, default: 1 ) –

    Scattered rows only: the grid has int(50 * grid_density) points per axis.

  • smooth (float, default: 0.5 ) –

    Scattered rows only: width of the Gaussian kernel, in grid steps.

  • n_neighbors (int, default: None ) –

    Scattered rows only: rows averaged into each grid point. By default int(n_rows / 50), so groups need at least 50 rows unless it is given.

  • min_mass (float, default: 1 ) –

    Scattered rows only: grid points with less kernel weight than 10 ** (min_mass - 6) are left empty (from 0, all, to about 5).

  • cutoff_percentile (float, default: 5 ) –

    Scattered rows only: grid points whose neighbours move slower than this percentile of all grid points are left empty.

  • color_by (str, default: None ) –

    Aesthetic the color scale applies to ("color" or "fill").

  • na_rm (bool, default: None ) –

    True removes rows with missing values silently.

  • **other_args (Any) –

    Constant aesthetics (color, size, alpha, linetype).

Returns:

  • LayerSpec –

    The layer.

Raises:

  • SpecError –

    If integration_direction is unknown, or a density, smooth or n_neighbors is not positive.

  • UnsupportedValueError –

    If an option has the wrong type (a string for a number, ...).

Examples:

Wind on a grid, turning around a low:

>>> from ggrust import *
>>> import numpy as np
>>> east, north = np.meshgrid(np.linspace(-3, 3, 31), np.linspace(-2, 2, 21))
>>> wind = {
...     "east": east.ravel(),
...     "north": north.ravel(),
...     "u": (-north - 0.3 * east).ravel(),
...     "v": (east - 0.3 * north).ravel(),
... }
>>> plot = (
...     ggplot(wind, aes("east", "north"))
...     + geom_streamline(aes(u="u", v="v"), density=1.2, color="#2b6cb0")
...     + coord_fixed()
... )

Scattered points, each with a direction:

>>> generator = np.random.default_rng(1)
>>> x, y = generator.normal(size=(2, 2000))
>>> drift = {"x": x, "y": y, "u": 1 - y**2, "v": x * 0.5}
>>> plot = ggplot(drift, aes("x", "y")) + geom_point(alpha=0.2) + geom_streamline(
...     aes(u="u", v="v"), n_neighbors=40
... )