Usage

Note

See the Getting started page for a more gentle introduction.

In the following we will use the example IUCN dataset. It is a parquet file with the Red List category (in 2022) of more than 84000 species.

import polars as pl
from pylifemap import Lifemap

iucn = pl.read_parquet(
    "https://raw.githubusercontent.com/Lifemap-ToL/pylifemap/main/data/iucn.parquet"
)

Installation

Install locally

You can add the package to a project with uv or to a virtual environment with pip:

# Install pylifemap, preferably in a virtual environment
pip install pylifemap
# Add pylifemap to a python project
uv add pylifemap

Run directly with uv

You can run pylifemap directly with uv run using a --with argument:

# Run a Jupyter session
uv run --with pylifemap --with jupyter jupyter-lab

# Run a marimo session
uv run --with pylifemap --with marimo marimo edit

# Run a custom script
uv run --with pylifemap my_script.py

Run with juv

juv is a toolkit for reproducible Jupyter notebooks, powered by uv.

You can easily create and run a notebook with pylifemap with:

juv init notebook.ipynb
juv add notebook.ipynb pylifemap
juv run notebook.ipynb

Run with Docker

Another way to use the package without having to install it is to use our Docker image.

Run in Jupyter

To use pylifemap in a Jupyter environment, open a terminal in the directory containing your data and notebook files, and run:

docker run -it -p 127.0.0.1:8899:8899 -v $PWD:/local/ ghcr.io/lifemap-tol/pylifemap:latest

Open the following URL in your browser: http://127.0.0.1:8899/lab, and you will have access to a Jupyter notebook environment with pylifemap and its dependencies preinstalled.

Run a script

If you just want to run a Python script generating a pylifemap visualization, you can open a terminal in the script directory and run the following command:

docker run -v $PWD:/local/ ghcr.io/lifemap-tol/pylifemap:latest myscript.py

In this case, if you use show() in your script the result will not be opened in your browser but will instead be saved in a lifemap.html file in your working directory.

Base map customization

Several arguments can be passed to Lifemap() when initializing the visualization.

Note

For a complete list of Lifemap arguments, see its documentation.

Widget size

The width and height arguments allow to modify the widget size. They can be given in any CSS unit.

For example, the following initialization would make the visualization take the full available width, and an height of 800 pixels.

Lifemap(iucn, taxid_col="taxid", width="100%", height=800)

Map theme

By default Lifemap displays its base map in a dark theme with a black background, but you can switch to another theme by adding a theme argument:

Lifemap(iucn, theme="light").layer_points().show()
Warning: 779 taxids have not been found in Lifemap database.
Warning: 152 duplicated taxids have been found in the data.

Available themes are currently dark, light, lightgrey, lightblue and lightgreen.

Map controls

By default, the Lifemap widget displays several controls allowing to interact with the map. It is possible to customize which controls are shown by passing a controls argument to Lifemap:

Lifemap(iucn, taxid_col="taxid", controls=("zoom", "full_screen"))

The following values are available:

  • "zoom": zoom in and zoom out buttons
  • "reset_zoom": zoom reset button
  • "png_export": button to export the current view to a PNG file
  • "search": taxa search button
  • "full_screen": full screen toggle button

Default view (center and zoom)

The center and zoom arguments allow to specify the initial map view at widget creation.

There are three different possible values for center:

  • "default": show the entire tree of life (default value)
  • "auto": center the view on the displayed data
  • taxid (integer value): center the view on this taxid coordinates

If you set zoom to an integer value between 4 and 42, the initial zoom will be set to this level. If you leave zoom at its default None value, the zoom level will be computed according to the center value.

Note

For technical reason, the center="auto" argument doesn’t take into account data passed to layer_screengrid or layer_heatmap_deck.

Visualization layers

Here are the currently available layers:

Layer Description
layer_points Displays each observation with a point. Radius and color can be dependent of an attribute in the DataFrame.
layer_lines Using aggregated data, highlights branches of the tree with lines of varying width and color.
layer_donuts Displays aggregated categorical data as donut charts.
layer_heatmap Displays a heatmap of the observations distribution in the tree.
layer_heatmap_deck Displays a deck.gl heatmap of the observations distribution in the tree.
layer_screengrid Displays the observations distribution with a colored grid with fixed-size cells..
layer_text Displays text labels by taxid
layer_icons Displays icons associated to taxids

Several layers can be added to the same map by chaining several methods:

Lifemap(iucn, taxid_col="taxid").layer_heatmap().layer_points().show()

Layers customization

Each layer accepts a certain number of arguments to customize its appearance. For example we can change the radius, color and opacity of our points:

(
    Lifemap(iucn)
    .layer_points(fill="#EFB118", radius=5, opacity=0.5)
    .show()
)

Instead of providing fixed values to fill or radius, we can also pass data column names to make size or color depending on the column values. For example, we can make the color of the points depending on their status:

(
    Lifemap(iucn)
    .layer_points(fill="status", radius=4, opacity=0.9)
    .show()
)
Note

See each layer documentation page for a complete list of its arguments.

Layers data

By default, layers use the data provided when calling Lifemap, but it is possible to give a specific dataset to a layer. To do this, just pass a data and optionally a taxid_col argument to the layer function.

icons_data = pl.DataFrame({
    'taxid': ['1005039','9544','41956','35128','74311'],
    'imageurl': ['https://images.phylopic.org/images/6767b093-d375-48c2-b4f2-9215b76ea08d/thumbnail/64x64.png',
                 'https://images.phylopic.org/images/4e9c5666-79cc-4766-a7d3-d547514b0d77/thumbnail/64x64.png',
                 'https://images.phylopic.org/images/f722ba34-03d4-4b90-8409-e02829a3c5d4/thumbnail/64x64.png',
                 'https://images.phylopic.org/images/ebff2e9f-3091-4ad6-a7b2-f39a77d3a960/thumbnail/64x64.png',
                 'https://images.phylopic.org/images/8fbb3dd3-24ff-4e96-b41c-92b334ff4560/thumbnail/64x64.png']
})

(
    Lifemap(iucn, theme="lightgrey")
    .layer_points(radius=4, opacity=0.9)
    .layer_icons(data=icons_data, icon="imageurl", popup=True)
    .show()
)

Popups customization

For layers like layer_points, layer_lines or layer_icons that support popups, it is possible to customize the popup content by adding a popup_col argument. This argument must be the name of a data column containing the popup content for each data point.

icons_data = icons_data.with_columns(popup="The taxid of this node is <strong>" + pl.col("taxid") + "</strong>")

(
    Lifemap(icons_data, theme="light")
    .layer_icons(icon="imageurl", popup=True, popup_col="popup")
    .show()
)

Lazy loading

By default the entire user data points are loaded and displayed on the map. This can be slow when the dataset is big.

Lazy loading means that data isn’t loaded entirely when the widget is created, but incrementally depending on the current map view: only the visible points are loaded and displayed.

Lazy loading is available for the points, lines, text, donuts and icons layers. It is enabled by default for the donuts layer.

To manually enable or disable lazy loading, you can set the lazy argument to True or False when using one these layers creation function.

Another useful argument you can give is lazy_zoom, which is the maximum zoom depth to display. In other words, if the map view is currently at zoom level z, then only data points above zoom level z + lazy_zoom. You can set lazy_zoom to -1 to display all zoom levels.

Warning

In certain cases, lazy loading can make certain map area appear empty when zoomed out, whereas some data points become visible when zooming in.

Widget display and export

Lifemap and layer methods create a widget object but don’t display it. For it to appear, we have to call the show() method:

Lifemap(iucn, taxid_col="taxid").layer_points().show()
Note

When in a notebook environment, calling show() will display the visualisation as a widget. When called from a Python script or a Python REPL, the visualization will be saved to a temporary file and, if possible, displayed in the user’s browser. When called from a Python script running inside our Docker container, it will be saved to a file in the working directory.

The widget can also be saved to an HTML file by using the save() method:

Lifemap(iucn, taxid_col="taxid").layer_points().save("lifemap.html")

You can also use Jupyter HTML export or Quarto document conversion to create HTML documents embedding interactive pylifemap widgets.

Important

Lifemap is updated weekly from the latest NCBI data. During these updates, the precise coordinates of each taxon can change slightly.

If you export your widget to HTML and your data includes less than 100 000 data points, the up to date coordinates are retrieved from our API each time the widget is displayed, so the coordinates of the base map and of your dataset should still match. But if you have more than 100 000 data points, they may not correspond anymore if the base map has been updated since the widget creation.

You can also use the Export to PNG button on the widget to export the currently displayed view to an image file in PNG format.

Data aggregation

pylifemap provides several functions that allow to aggregate data along the branches of the tree:

Function Description
aggregate_count Aggregates the number of children of each tree node.
aggregate_num Aggregates a numerical variable along the tree branches with a given function (sum , mean, max…).
aggregate_freq Aggregates the frequencies of the levels of a categorical variable.

For example, if we filter our dataset to only keep the species with an “extinct” status:

iucn_extinct = iucn.filter(pl.col("status") == "Extinct")
iucn_extinct = iucn[iucn["status"] == "Extinct"]

We can then aggregate their count along the branches with aggregate_count:

from pylifemap import aggregate_count
iucn_extinct_agg = aggregate_count(iucn_extinct)
iucn_extinct_agg
shape: (774, 2)
taxidn
i32u32
null2
0196
2759196
319321
32681
30762441
307811476
31360231
34101191
34101211

Finally, we can represent this new dataset with a lines layer, making the lines width and color depending on the aggregated count values.

(
    Lifemap(iucn_extinct_agg)
    .layer_lines(color="n", width="n", label="Extinct species")
    .show()
)
Warning: 3 taxids have not been found in Lifemap database: [None, 1914395, 3041918].