Operators: +, >>, |, /

Plotnine overloads the + and >> operators to make it easier to compose and pass data to plots. It also overloads the | and / operators to compose multiple plots together. You might also see &, and * used for adding components to composed plots.

This page covers the basics of the + and >> operators, along with an explanation for why they exist.

from plotnine import *
from plotnine.data import mpg

The + operator

Use + to add geoms, scales, and themes to a plot.

(
    ggplot(mpg, aes("displ", "hwy"))
    + geom_point(aes(fill="class"))  # first geom
    + theme_minimal()  # theme
)

The >> operator

Use the >> operator to pipe data into a plot.

(
    mpg
    # piped into plot
    >> ggplot(aes("displ", "hwy", fill="class")) + geom_point()
)

Why operators?

Composable and extendable

Operators enable plot actions to use syntax that’s composable and extendable. This means two things:

  • composable: you can add additional components like geoms to a plot.
  • extendable: components defined in other libraries—like new kinds of geoms—can be added to a plot using the same + syntax.

For example, suppose you defined your own Plotnine theme, as shown below:

def my_theme(base_size=11):
    return (
        theme_grey(base_size=base_size)  # start with grey theme
        + theme(axis_text_x=element_text(angle=90))  # customize x-axis
    )

You can use this theme with the + operator, just like the built-in Plotnine themes:

(
    ggplot(mpg, aes("displ", "hwy", fill="class"))
    + geom_point()
    + my_theme(base_size=16)  # apply custom theme
)

Notice that the custom my_theme() component was added to the plot, in the same way anything else is (e.g. geom_point()).

Keep method chains going

The >> operator allows you to keep method chains going, without needing to create intermediate variables. This is especially useful when using Polars DataFrames, where you often want to do small bits of data wrangling before piping into the plot.

import polars as pl

pl_mpg = pl.from_pandas(mpg)

(
    pl_mpg
    #
    # label high mpg cars
    .with_columns(good_mpg=pl.col("hwy") > 30)
    #
    # remove compact cars
    .filter(pl.col("class") != "compact")
    #
    # pipe into plot
    >> ggplot(aes("displ", "hwy", fill="good_mpg")) + geom_point()
)

Is overloading operators “Pythonic”?

Sometimes people ask whether overloading the + and >> operators is “Pythonic” – meaning that it follows standard conventions for Python code. This is a tricky question to answer, but there are two cases that point to “yes”:

  • The builtin pathlib library overloads / to good effect.
  • Tools like Polars use operators to compose selectors.

pathlib example

from pathlib import Path

Path("a/b") / "c"
PosixPath('a/b/c')

In this case, the / operator makes it quick to add to a path (by returning a new Path object). Similarly, the Plotnine >> operator makes it easy to go from DataFrame method chaining into a plot.

Polars example

import polars as pl
import polars.selectors as cs

df = pl.DataFrame({"ttl_a": [1], "ttl_b": [2], "ttly": [3]})

# get cols starting with "ttl" but not "ttly"
selector = cs.starts_with("ttl") - cs.by_name("ttly")

df.select(selector)
shape: (1, 2)
ttl_a ttl_b
i64 i64
1 2

Each selector (like starts_with) specifies a way to select columns, while operators like - provide ways to combine them. Similarly, each geom in Plotnine specifies a piece to include in a plot, while the + operator makes it easy to assemble pieces.