Skip to content

Commit 87f726f

Browse files
Atreus-TechnologiesMiles Garnseyclaude
authored
Jupyter modernisation (#399)
* refactor: extract evcxr format helpers from display methods Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: assert evcxr display output format and content Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: modernise Jupyter setup guide for JupyterLab 4.x Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: lead with evcxr_display() as the recommended display method The docs showed lab_display() but users reach for evcxr_display(). Correct the example and remove mention of the lower-level variants. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: note JupyterLab version tested against Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Miles Garnsey <miles.garnsey@atreus-technologies.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9ce2f57 commit 87f726f

2 files changed

Lines changed: 80 additions & 78 deletions

File tree

Lines changed: 30 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,67 @@
11
# Jupyter Support
22

3-
As of version `0.7.0`, [Plotly.rs](https://github.com/plotly/plotly.rs) has native support for the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter).
3+
As of version `0.7.0`, [Plotly.rs](https://github.com/plotly/plotly.rs) has native support for the [EvCxR Jupyter Kernel](https://github.com/evcxr/evcxr/tree/main/evcxr_jupyter).
44

55
Once you've installed the required packages you'll be able to run all the examples shown here as well as all [the recipes](../recipes.md) in Jupyter Lab!
66

7+
> Tested against JupyterLab 4.4.7.
78
89
## Installation
9-
It is assumed that an installation of the [Anaconda](https://www.anaconda.com/products/individual) Python distribution is already present in the system. If that is not the case you can follow these [instructions](https://www.anaconda.com/products/individual) to get up and running with `Anaconda`.
1010

11-
```shell script
12-
conda install -c plotly plotly=4.9.0
13-
conda install jupyterlab "ipywidgets=7.5"
11+
Install the plotly package and JupyterLab using pip or conda:
12+
13+
**pip**
14+
```shell
15+
pip install plotly jupyterlab
1416
```
1517

16-
optionally (or instead of `jupyterlab`) you can also install Jupyter Notebook:
17-
```shell script
18-
conda install notebook
18+
**conda**
19+
```shell
20+
conda install -c conda-forge plotly jupyterlab
1921
```
2022

21-
Although there are alternative methods to enable support for the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter), we have elected to keep the requirements consistent with what those of other languages, e.g. Julia, Python and R. This way users know what to expect; and also the folks at [Plotly](https://plotly.com/python/getting-started/#jupyter-notebook-support) have done already most of the heavy lifting to create an extension for Jupyter Lab that works very well.
23+
No separate JupyterLab extension install is required — the plotly renderer is bundled
24+
with the plotly package (5.x+) and JupyterLab picks it up automatically.
2225

23-
Run the following to install the Plotly Jupyter Lab extension:
24-
```shell script
25-
jupyter labextension install jupyterlab-plotly@4.9.0
26-
```
26+
> **Note:** `anywidget` is required for Python's `FigureWidget` interactive features
27+
> but is **not** needed for the Rust `evcxr_display()` path.
2728
28-
Once this step is complete to make sure the installation so far was successful, run the following command:
29-
```shell script
30-
jupyter lab
31-
```
29+
Next, install the EvCxR Jupyter Kernel:
3230

33-
Open a `Python 3` kernel copy/paste the following code in a cell and run it:
34-
```python
35-
import plotly.graph_objects as go
36-
fig = go.Figure(data=go.Bar(x=['a', 'b', 'c'], y=[11, 22, 33]))
37-
fig.show()
38-
```
39-
You should see the following figure:
40-
<div id="jupyter_lab_demo_bar_chart_python" class="plotly-graph-div" style="height:100%; width:100%;"></div>
41-
<script type="text/javascript">
42-
window.PLOTLYENV=window.PLOTLYENV || {};
43-
if (document.getElementById("jupyter_lab_demo_bar_chart_python")) {
44-
var d3 = Plotly.d3;
45-
var image_element= d3.select('#image-export');
46-
var trace_0 = {"x":["a","b","c"],"y":[11,22,33],"type":"bar"};
47-
var data = [trace_0];
48-
var layout = {};
49-
Plotly.newPlot('jupyter_lab_demo_bar_chart_python', data, layout, {"responsive": true});
50-
};
51-
</script>
52-
53-
Next you need to install the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter). Note that EvCxR requires [CMake](https://cmake.org/download/) as it has to compile ZMQ. If [CMake](https://cmake.org/download/) is already installed on your system and is in your path (to test that simply run ```cmake --version``` if that returns a version you're good to go) then continue to the next steps.
54-
55-
In a command line execute the following commands:
56-
```shell script
31+
```shell
5732
cargo install evcxr_jupyter
5833
evcxr_jupyter --install
5934
```
6035

61-
If you're not familiar with the EvCxR kernel it would be good that you at least glance over the [EvCxR Jupyter Tour](https://github.com/google/evcxr/blob/master/evcxr_jupyter/samples/evcxr_jupyter_tour.ipynb).
62-
6336
## Usage
6437

6538
Launch Jupyter Lab:
66-
```shell script
39+
40+
```shell
6741
jupyter lab
6842
```
6943

70-
create a new notebook and select the `Rust` kernel. Then create the following three cells and execute them in order:
44+
Create a new notebook and select the `Rust` kernel. Add the plotly dependency and
45+
display a plot:
7146

72-
```shell script
73-
:dep ndarray = "0.15.6"
74-
:dep plotly = { version = ">=0.7.0" }
47+
**Cell 1**
7548
```
76-
77-
```rust
78-
extern crate ndarray;
79-
extern crate plotly;
80-
extern crate rand_distr;
49+
:dep plotly = "0.14"
8150
```
8251

52+
**Cell 2**
8353
```rust
84-
use ndarray::Array;
85-
use plotly::common::Mode;
86-
use plotly::layout::{Layout};
8754
use plotly::{Plot, Scatter};
88-
use rand_distr::{num_traits::Float, Distribution};
89-
```
9055

91-
Now we're ready to start plotting!
92-
93-
```rust
94-
let x0 = Array::linspace(1.0, 3.0, 200).into_raw_vec();
95-
let y0 = x0.iter().map(|v| *v * (v.powf(2.)).sin() + 1.).collect();
96-
97-
let trace = Scatter::new(x0, y0);
56+
let trace = Scatter::new(vec![1.0, 2.0, 3.0], vec![1.0, 4.0, 9.0]);
9857
let mut plot = Plot::new();
9958
plot.add_trace(trace);
10059

101-
let layout = Layout::new().height(525);
102-
plot.set_layout(layout);
103-
104-
plot.lab_display();
105-
format!("EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n{}\nEVCXR_END_CONTENT", plot.to_json())
60+
plot.evcxr_display();
10661
```
107-
For Jupyter Lab there are two ways to display a plot in the `EvCxR` kernel, either have the plot object be in the last line without a semicolon or directly invoke the `Plot::lab_display` method on it; both have the same result. You can also find an example notebook [here](https://github.com/plotly/plotly.rs/tree/main/examples/jupyter/jupyter_lab.ipynb) that will periodically be updated with examples.
10862

109-
The process for Jupyter Notebook is very much the same with one exception; the `Plot::notebook_display` method must be used to display the plot. You can find an example notebook [here](https://github.com/plotly/plotly.rs/tree/main/examples/jupyter/jupyter_notebook.ipynb)
63+
`evcxr_display()` works in both Jupyter Lab and Notebook. Alternatively you can
64+
leave the plot on the last line of a cell without a semicolon for the same effect.
65+
66+
You can find a full notebook example
67+
[here](https://github.com/plotly/plotly.rs/tree/main/examples/jupyter/jupyter_notebook.ipynb).

plotly/src/plot.rs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,18 +403,28 @@ impl Plot {
403403
tmpl.render().unwrap()
404404
}
405405

406+
fn to_evcxr_notebook_format(&self) -> String {
407+
format!(
408+
"EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT",
409+
self.to_jupyter_notebook_html()
410+
)
411+
}
412+
413+
fn to_evcxr_lab_format(&self) -> String {
414+
format!(
415+
"EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n{}\nEVCXR_END_CONTENT",
416+
self.to_json()
417+
)
418+
}
419+
406420
/// Display plot in Jupyter Notebook.
407421
pub fn notebook_display(&self) {
408-
let plot_data = self.to_jupyter_notebook_html();
409-
println!("EVCXR_BEGIN_CONTENT text/html\n{plot_data}\nEVCXR_END_CONTENT");
422+
println!("{}", self.to_evcxr_notebook_format());
410423
}
411424

412425
/// Display plot in Jupyter Lab.
413426
pub fn lab_display(&self) {
414-
let plot_data = self.to_json();
415-
println!(
416-
"EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n{plot_data}\nEVCXR_END_CONTENT"
417-
);
427+
println!("{}", self.to_evcxr_lab_format());
418428
}
419429

420430
/// Displays the plot in Jupyter Lab; if running a Jupyter Notebook then use
@@ -875,6 +885,40 @@ mod tests {
875885
plot.lab_display();
876886
}
877887

888+
#[test]
889+
fn lab_display_output() {
890+
let plot = create_test_plot();
891+
let output = plot.to_evcxr_lab_format();
892+
893+
assert!(output.starts_with("EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n"));
894+
assert!(output.ends_with("\nEVCXR_END_CONTENT"));
895+
896+
let json_str = output
897+
.strip_prefix("EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n")
898+
.unwrap()
899+
.strip_suffix("\nEVCXR_END_CONTENT")
900+
.unwrap();
901+
let json: serde_json::Value = serde_json::from_str(json_str).unwrap();
902+
assert!(json.get("data").is_some());
903+
assert!(json.get("layout").is_some());
904+
}
905+
906+
#[test]
907+
fn notebook_display_output() {
908+
let plot = create_test_plot();
909+
let output = plot.to_evcxr_notebook_format();
910+
911+
assert!(output.starts_with("EVCXR_BEGIN_CONTENT text/html\n"));
912+
assert!(output.ends_with("\nEVCXR_END_CONTENT"));
913+
914+
let html = output
915+
.strip_prefix("EVCXR_BEGIN_CONTENT text/html\n")
916+
.unwrap()
917+
.strip_suffix("\nEVCXR_END_CONTENT")
918+
.unwrap();
919+
assert!(html.contains("plotly"));
920+
}
921+
878922
#[test]
879923
fn plot_serialize_simple() {
880924
let plot = create_test_plot();

0 commit comments

Comments
 (0)