Skip to content

Share axes between subplots #821

Closed

Description

@asmunder

I'm using fastplotlib to visualize a set of timeseries that has a few hundred sensors measuring two distinct variables with different units. This works fine with two scatter subplots stacked vertically, one for each variable.

Since the x-axis has identical range between my subplots (but the y-axis is very different), it would be really nice to have a linked pan/zoom/etc. between them. This corresponds exactly to the "sharex" (or "sharey") arguments that can be passed to plt.subplots() in matplotlib.

Is there a way to achieve this in the current version of fastplotlib?

I'm also interested to know if there is a way to get something resembling timestamps on the x-axis (but I would guess this is future work).

Activity

  1. clewis7 commented on May 16, 2025

    @clewis7
    Member

    Hi @asmunder

    Thanks for posting. I recommend looking at our examples gallery, where some examples show you how you can sync the controllers of different subplots. See here under "Controller examples".

    I believe this should be what you are looking for. If not, please let us know and we can provide more help :D

  2. clewis7 commented on May 16, 2025

    @clewis7
    Member

    And yes, timestamps (or in general, more detailed/flexible axes) are something we hope to add in the future.

  3. kushalkolar commented on May 16, 2025

    @kushalkolar
    Member

    If by timestamps you just mean adding an "s" to the tick value, you can set a function to map tick values to any output string, just note that the exact API for axes things might change in the future:

    fig[0, 0].axes.x.tick_format = lambda v, min_v, max_v: f"{v} sec"
  4. asmunder commented on May 20, 2025

    @asmunder
    Author

    Hi @clewis7 , @kushalkolar , thanks for the quick responses.

    I followed the suggestion to try the "sync subplots" example. This approach would be fine if I wanted to sync both the x- and y-axis. But the scale of my data on the y-axis is different between the subplots, in my case something like [0 .. 1] and [-30 .. 50]. With the "sync subplots" option of sharing the controller, the y-axis range is forced to be identical on both subplots, so I can't really see both datasets at the same time.

    What I was able to do was to first normalize my data such that everything is [0 .. 1] and then pass functions to tick_format which undo the normalization in the tick label formatting. It's a hack for sure, and the zoom is still linked on both axes (which I don't want), but at least I can see all the data with sensible ranges.

    I had a look in the source for fastplotlib and in pygfx at the panzoom controller. In order to get equivalent functionality to the sharex = True, sharey = False setting in matplotlib, would this require a new PanZoomSharexController (and similar for Sharey), so that you'd get multiple controllers with one linked axis that you pass to the controller_ids argument of the Figure constructor? Or could it be possible to do something like this using several of the existing PanZoomController and synchronizing them using bidirectional events?

  5. kushalkolar commented on May 20, 2025

    @kushalkolar
    Member

    Ah I see, this is a WIP, relevant issue: #783 . This should allow customizable linking of specific axes.

    also somewhat related, #772 and #809

  6. asmunder commented on May 20, 2025

    @asmunder
    Author

    Thanks for clarifying! Will follow the developments on this.

    As for the timestamp formatting on the axis, below is a minimal example that works quite nicely for a Pandas dataframe with a DatetimeIndex, in case it is useful for others. Here I'm using the date2num and num2date functions from matplotlib.dates.

    The only gripe here is that sometimes the label texts overlap at some zoom levels. Is there any way to adjust that?

    import numpy as np
    import pandas as pd
    import fastplotlib as fpl
    import matplotlib.dates as mdates
    
    # Generate random dataframe with DatetimeIndex
    N_periods = 10000
    df = pd.DataFrame(
        data={"values": np.random.uniform(high=42, size=N_periods)},
        index=pd.date_range(start="2025-01-01", periods=N_periods, freq="h"),
    )
    print(df)
    
    # Convert to numpy array that fastplotlib can use
    data = np.vstack(
        [
            mdates.date2num(df.index),
            df["values"].to_numpy(),
        ]
    ).T
    
    # Create the plot
    fig = fpl.Figure()
    fig[0, 0].add_scatter(data=data)
    
    # Define datetime formatter
    def axis_datetime(v, min_v, max_v):
        w = mdates.num2date(v)
        label = w.strftime("%Y-%m-%d")
        return label
    
    # Apply datetime formatter and set free aspect ratio on the axes
    fig[0, 0].axes.x.tick_format = axis_datetime
    fig[0, 0].auto_scale(maintain_aspect=False)
    
    # Show the fig
    fig.show()
    fpl.loop.run()
  7. kushalkolar commented on May 20, 2025

    @kushalkolar
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions