-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New example and tutorial showing how to create a live scrolling plot (#…
…6314) ### What - Resolves: #5820 Although I would like to update the blueprint_stocks example for completeness, we decided it would be nice to have a tight live-scrolling example and how-to guide since this is one of the more common things people look for. Guide: - https://landing-e9j32r1lf-rerun.vercel.app/docs/howto/fixed-window-plot ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6314?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6314?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/6314) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. --------- Co-authored-by: Clement Rey <[email protected]>
- Loading branch information
Showing
11 changed files
with
2,469 additions
and
2,177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--- | ||
title: Create a fixed-window plot | ||
order: 150 | ||
--- | ||
|
||
As of Rerun 0.16, the [TimeSeriesView](../reference/types/views/time_series_view.md) now supports direct | ||
manipulation of the visible time range. This allows you to create a plot that only shows a fixed window of data. | ||
|
||
## VisibleTimeRange | ||
|
||
To specify the visible time range, you must pass one or more `VisibleTimeRange` objects to the `time_ranges` parameter of the `TimeSeriesView` blueprint type. If your app only uses a single timeline, you can directly pass a single `VisibleTimeRange` object instead of wrapping it in a list. | ||
|
||
The `VisibleTimeRange` object takes three parameters: | ||
- `timeline`: The timeline that the range will apply to. This must match the timeline used to log your data, or if you are only using the rerun-provided timestamps, you can use the strings `"log_time"`, or `"log_tick"`. | ||
- `start`: The start of the visible time range. | ||
- `end`: The end of the visible time range. | ||
|
||
The `start` and `end` parameters are set using a `TimeRangeBoundary`: | ||
- To specify an absolute time, you can use the `TimeRangeBoundary.absolute()` method. | ||
- To specify a cursor-relative time, you can use the `TimeRangeBoundary.cursor_relative()` method. | ||
- You can also specify `TimeRangeBoundary.infinite()` to indicate that the start or end of the time range should be unbounded. | ||
|
||
In order to account for the different types of timeline (temporal or sequence-based), both the | ||
`TimeRangeBoundary.absolute()` and `TimeRangeBoundary.cursor_relative()` methods can be specified using one of | ||
the keyword args: | ||
- `seconds`: Use this if you called `rr.set_time_seconds()` to update the timeline. | ||
- `nanos`: Use this if you called `rr.set_time_nanos()` to update the timeline. | ||
- `seq`: Use this if you called `rr.set_time_seq()` to update the timeline. | ||
|
||
## Example syntax | ||
To create a trailing 5 second window plot, you can specify your `TimeSeriesView` like this: | ||
```python | ||
rrb.TimeSeriesView( | ||
origin="plot_path", | ||
time_ranges=rrb.VisibleTimeRange( | ||
timeline="time", | ||
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-5.0), | ||
end=rrb.TimeRangeBoundary.cursor_relative(), | ||
) | ||
) | ||
``` | ||
|
||
## Full example | ||
For a complete working example, you can run the following code: | ||
|
||
snippet: tutorials/fixed_window_plot | ||
|
||
This should create a plot that only shows the last 5 seconds of data. If you select the view, you should | ||
see that the time range is configured as expected. | ||
|
||
<picture> | ||
<img src="https://static.rerun.io/fixed_window_example/f76228dc2e1212c148064c2193cdf75ef14bb2b9/full.png" alt=""> | ||
<source media="(max-width: 480px)" srcset="https://static.rerun.io/fixed_window_example/f76228dc2e1212c148064c2193cdf75ef14bb2b9/480w.png"> | ||
<source media="(max-width: 768px)" srcset="https://static.rerun.io/fixed_window_example/f76228dc2e1212c148064c2193cdf75ef14bb2b9/768w.png"> | ||
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/fixed_window_example/f76228dc2e1212c148064c2193cdf75ef14bb2b9/1024w.png"> | ||
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/fixed_window_example/f76228dc2e1212c148064c2193cdf75ef14bb2b9/1200w.png"> | ||
</picture> | ||
|
||
Alternatively, you can check out a more full-featured example with multiple plot windows [here](https://github.com/rerun-io/rerun/tree/latest/examples/python/live_scrolling_plot?speculative-link). | ||
|
||
## Additional notes | ||
- Any time you log data, it has two timepoints associated with it: "log_time", and "log_tick". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#!/usr/bin/env python3 | ||
"""A live plot of a random walk using a scrolling fixed window size.""" | ||
|
||
from __future__ import annotations | ||
|
||
import time | ||
|
||
import numpy as np | ||
import rerun as rr # pip install rerun-sdk | ||
import rerun.blueprint as rrb | ||
|
||
rr.init("rerun_example_fixed_window_plot", spawn=True) | ||
|
||
rr.send_blueprint( | ||
rrb.TimeSeriesView( | ||
origin="random_walk", | ||
time_ranges=rrb.VisibleTimeRange( | ||
"time", | ||
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-5.0), | ||
end=rrb.TimeRangeBoundary.cursor_relative(), | ||
), | ||
) | ||
) | ||
|
||
cur_time = time.time() | ||
value = 0.0 | ||
|
||
while True: | ||
cur_time += 0.01 | ||
sleep_for = cur_time - time.time() | ||
if sleep_for > 0: | ||
time.sleep(sleep_for) | ||
|
||
value += np.random.normal() | ||
|
||
rr.set_time_seconds("time", cur_time) | ||
|
||
rr.log("random_walk", rr.Scalar(value)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<!--[metadata] | ||
title = "Live Scrolling Plot" | ||
tags = ["Plots", "Live"] | ||
thumbnail = "https://static.rerun.io/live_scrolling_plot_thumbnail/73c6b11bd074af258b8d30092e15361e358d8069/480w.png" | ||
thumbnail_dimensions = [480, 384] | ||
--> | ||
|
||
Visualize a live stream of several plots, scrolling horizontally to keep a fixed window of data. | ||
|
||
<picture> | ||
<img src="https://static.rerun.io/live_scrolling_plot/9c9a9b3a4dd1d5e858ba58489f686b5d481cfb2e/full.png" alt=""> | ||
<source media="(max-width: 480px)" srcset="https://static.rerun.io/live_scrolling_plot/9c9a9b3a4dd1d5e858ba58489f686b5d481cfb2e/480w.png"> | ||
<source media="(max-width: 768px)" srcset="https://static.rerun.io/live_scrolling_plot/9c9a9b3a4dd1d5e858ba58489f686b5d481cfb2e/768w.png"> | ||
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/live_scrolling_plot/9c9a9b3a4dd1d5e858ba58489f686b5d481cfb2e/1024w.png"> | ||
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/live_scrolling_plot/9c9a9b3a4dd1d5e858ba58489f686b5d481cfb2e/1200w.png"> | ||
</picture> | ||
|
||
## Used Rerun types | ||
[`Scalar`](https://www.rerun.io/docs/reference/types/archetypes/scalar) | ||
|
||
## Setting up the blueprint | ||
|
||
In order to only show a fixed window of data, this example creates a blueprint that uses | ||
the `time_ranges` parameter of the `TimeSeriesView` blueprint type. | ||
|
||
We dynamically create a `TimeSeriesView` for each plot we want to show, so that we can | ||
set the `time_ranges`. The start of the visible time range is set to the current time | ||
minus the window size, and the end is set to the current time. | ||
|
||
```python | ||
rr.send_blueprint( | ||
rrb.Grid( | ||
contents=[ | ||
rrb.TimeSeriesView( | ||
origin=plot_path, | ||
time_ranges=[ | ||
rrb.VisibleTimeRange( | ||
"time", | ||
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-args.window_size), | ||
end=rrb.TimeRangeBoundary.cursor_relative(), | ||
) | ||
], | ||
plot_legend=rrb.PlotLegend(visible=False), | ||
) | ||
for plot_path in plot_paths | ||
] | ||
), | ||
) | ||
|
||
``` | ||
|
||
## Run the code | ||
To run this example, make sure you have the Rerun repository checked out and the latest SDK installed: | ||
```bash | ||
# Setup | ||
pip install --upgrade rerun-sdk # install the latest Rerun SDK | ||
git clone [email protected]:rerun-io/rerun.git # Clone the repository | ||
cd rerun | ||
git checkout latest # Check out the commit matching the latest SDK release | ||
``` | ||
Install the necessary libraries specified in the requirements file: | ||
```bash | ||
pip install -e examples/python/live_scrolling_plot | ||
``` | ||
|
||
Then, simply execute the main Python script: | ||
```bash | ||
python -m live_scrolling_plot | ||
``` |
87 changes: 87 additions & 0 deletions
87
examples/python/live_scrolling_plot/live_scrolling_plot.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#!/usr/bin/env python3 | ||
"""Show several live plots of random walk data using a scrolling fixed window size.""" | ||
|
||
from __future__ import annotations | ||
|
||
import argparse | ||
import time | ||
from typing import Iterator | ||
|
||
import numpy as np | ||
import rerun as rr # pip install rerun-sdk | ||
import rerun.blueprint as rrb | ||
|
||
|
||
def random_walk_generator() -> Iterator[float]: | ||
value = 0.0 | ||
while True: | ||
value += np.random.normal() | ||
yield value | ||
|
||
|
||
def main() -> None: | ||
parser = argparse.ArgumentParser(description="Plot dashboard stress test") | ||
rr.script_add_args(parser) | ||
|
||
parser.add_argument("--num-plots", type=int, default=6, help="How many different plots?") | ||
parser.add_argument("--num-series-per-plot", type=int, default=5, help="How many series in each single plot?") | ||
parser.add_argument("--freq", type=float, default=100, help="Frequency of logging (applies to all series)") | ||
parser.add_argument("--window-size", type=float, default=5.0, help="Size of the window in seconds") | ||
parser.add_argument("--duration", type=float, default=60, help="How long to log for in seconds") | ||
|
||
args = parser.parse_args() | ||
|
||
plot_paths = [f"plot_{i}" for i in range(0, args.num_plots)] | ||
series_paths = [f"series_{i}" for i in range(0, args.num_series_per_plot)] | ||
|
||
rr.script_setup(args, "rerun_example_live_scrolling_plot") | ||
|
||
# Always send the blueprint since it is a function of the data. | ||
rr.send_blueprint( | ||
rrb.Grid( | ||
contents=[ | ||
rrb.TimeSeriesView( | ||
origin=plot_path, | ||
time_ranges=[ | ||
rrb.VisibleTimeRange( | ||
"time", | ||
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-args.window_size), | ||
end=rrb.TimeRangeBoundary.cursor_relative(), | ||
) | ||
], | ||
plot_legend=rrb.PlotLegend(visible=False), | ||
) | ||
for plot_path in plot_paths | ||
] | ||
), | ||
) | ||
|
||
# Generate a list of generators for each series in each plot | ||
values = [[random_walk_generator() for _ in range(args.num_series_per_plot)] for _ in range(args.num_plots)] | ||
|
||
cur_time = time.time() | ||
end_time = cur_time + args.duration | ||
time_per_tick = 1.0 / args.freq | ||
|
||
while cur_time < end_time: | ||
# Advance time and sleep if necessary | ||
cur_time += time_per_tick | ||
sleep_for = cur_time - time.time() | ||
if sleep_for > 0: | ||
time.sleep(sleep_for) | ||
|
||
if sleep_for < -0.1: | ||
print(f"Warning: missed logging window by {-sleep_for:.2f} seconds") | ||
|
||
rr.set_time_seconds("time", cur_time) | ||
|
||
# Output each series based on its generator | ||
for plot_idx, plot_path in enumerate(plot_paths): | ||
for series_idx, series_path in enumerate(series_paths): | ||
rr.log(f"{plot_path}/{series_path}", rr.Scalar(next(values[plot_idx][series_idx]))) | ||
|
||
rr.script_teardown(args) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[project] | ||
name = "live_scrolling_plot" | ||
version = "0.1.0" | ||
readme = "README.md" | ||
dependencies = ["numpy", "rerun-sdk"] | ||
|
||
[project.scripts] | ||
live_scrolling_plot = "live_scrolling_plot:main" | ||
|
||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" |
Oops, something went wrong.