Data Analysis with Rust Notebooks

A practical book on Data Analysis with Rust Notebooks that teaches you the concepts and how they’re implemented in practice.

Get the book
Finishing Touches for Visualisation

Preamble

:dep darn = {version = "0.3.3"}
:dep plotly = {version = "0.4.0"}
:dep nanoid = {version = "0.3.0"}
extern crate darn;
extern crate plotly;
extern crate nanoid;

use plotly::{Plot, Scatter, Layout};
use plotly::common::{Mode, Anchor, Orientation, Title};
use plotly::layout::{Legend, Margin, Axis};
use nanoid::nanoid;
use std::fs;

Plotly Workaround Function

In the last section, we improved upon our Plotly workaround to get our plots to appear within our notebooks. All that's left now is to separate our workaround into a function so that we can re-use it throughout the rest of this book. We'll name this function show_plot()

fn show_plot(plot: Plot) { 
    let plot_id = Box::leak(nanoid!().into_boxed_str());
    let plot_handle = format!("{}{}", "show_plot_", plot_id).replace("-","_");
    let plotly_file = "temp_plot.html";
    plot.to_html(plotly_file);
    
    let plotly_contents = fs::read_to_string(plotly_file).unwrap();
    fs::remove_file(plotly_file);

    let start_bytes = plotly_contents
        .find("<div id=\"plotly-html-element\" class=\"plotly-graph-div\"")
        .unwrap_or(0);
    
    let end_bytes = plotly_contents
        .find("\n</div>\n</body>\n</html>")
        .unwrap_or(plotly_contents.len());
    
    println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT",
    format!("<div>{}</div>",
        &plotly_contents[start_bytes..end_bytes]
            .replace("plotly-html-element", plot_id)
            .replace("height:100%; width:100%;", "")
            .replace(",height:0,width:0", "")
            .replace("height:0,width:0", "")
            .replace("window.PLOTLYENV=",
                     "function show_plot() { window.PLOTLYENV=")
            .replace("};\n\n\n    </script>",
                     "};\n\n\n}; if (typeof Plotly === \"undefined\"){
                var script = document.createElement(\"script\");
                script.type = \"text/javascript\";
                script.src = \"https://cdn.plot.ly/plotly-1.58.1.min.js\";
                script.onload = function () { show_plot() };
                document.body.appendChild(script);} else { show_plot() }</script>")
            .replace("show_plot", &plot_handle)));
}

Now let's use the same example code from the previous sections and use our function above to display the plot. This time, we're going to use the Layout struct and specify some customisations. This will reduce the padding on our plot, change the positioning of the legend, and label the axes. This is not a necessary step, however, the output should look much nicer.

let layout = Layout::new()
    .xaxis(Axis::new().title(Title::new("x axis")))
    .yaxis(Axis::new().title(Title::new("y axis")))
    .margin(Margin::new().top(0).bottom(40).left(40).right(10))
    .legend(Legend::new().x(0.5).y(1.1)
    .orientation(Orientation::Horizontal).x_anchor(Anchor::Center));

let trace1 = Scatter::new(vec![1, 2, 3, 4], vec![10, 15, 13, 17])
    .name("trace1")
    .mode(Mode::Markers);
let trace2 = Scatter::new(vec![2, 3, 4, 5], vec![16, 5, 11, 9])
    .name("trace2")
    .mode(Mode::Lines);
let trace3 = Scatter::new(vec![1, 2, 3, 4], vec![12, 9, 15, 12])
    .name("trace3");

let mut plot = Plot::new();

plot.set_layout(layout);
plot.add_trace(trace1);
plot.add_trace(trace2);
plot.add_trace(trace3);

To display our plot, we can pass the plot variable to our show_plot() function.

show_plot(plot);

The DARN Crate

To make things even easier throughout this book, I've created a crate, darn (Data Analysis with Rust Notebooks), and published it on the Rust Package Registry. All we need to do now is use extern crate darn; to get this function, which has a little extra to make the plots look even nicer with some layout defaults. Let's use the same example code, this time noting that we are specifying less layout information.

let layout = Layout::new()
    .xaxis(Axis::new().title(Title::new("x axis")))
    .yaxis(Axis::new().title(Title::new("y axis")));

let trace1 = Scatter::new(vec![1, 2, 3, 4], vec![10, 15, 13, 17])
    .name("trace1")
    .mode(Mode::Markers);

let trace2 = Scatter::new(vec![2, 3, 4, 5], vec![16, 5, 11, 9])
    .name("trace2")
    .mode(Mode::Lines);

let trace3 = Scatter::new(vec![1, 2, 3, 4], vec![12, 9, 15, 12])
    .name("trace3");

let mut plot = Plot::new();

plot.set_layout(layout);
plot.add_trace(trace1);
plot.add_trace(trace2);
plot.add_trace(trace3);

This time, we'll use the function provided by our crate, darn::show_plot(), to display our plot.

darn::show_plot(plot);

Conclusion

In this section, we took our Plotly workaround a step further to its final destination. Our Plotly workaround is now available as part of the darn crate and ready to use in the following sections.

Comments

From the collection

Data Analysis with Rust Notebooks

A practical book on Data Analysis with Rust Notebooks that teaches you the concepts and how they’re implemented in practice.

Get the book

ISBN

978-1-915907-10-3

Cite

Rostami, S. (2020). Data Analysis with Rust Notebooks. Polyra Publishing.