Better Output for 2D Arrays

Preamble

In [2]:
extern crate ndarray;
extern crate darn;

use std::fmt::Debug;
use ndarray::prelude::*;

Introduction

In earlier sections, we progressively developed a workaround to get nice looking Plotly visualisations embedded into our notebooks. This will support our literate programming approach whereby we intertwine our narrative, code, and informative output to present a single and coherent document.

In this section, we will continue this effort, but for the better embedding of our arrays in our notebook. Currently, if we output an array such as:

In [18]:
let data_2D = array![[1.,2.,3.,4.,5.,6.,7.,8.,9.,10.],
                     [11.,12.,13.,14.,15.,16.,17.,18.,19.,20.]];

to a notebook we will see the following.

In [4]:
data_2D
Out[4]:
[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
 [11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]], shape=[2, 10], strides=[10, 1], layout=C (0x1), const ndim=2

We can consider this to be a small array along both axes, however, it is already at the point where it may be inconvenient to interpret the output in its current format.

Customising How Arrays are Displayed

If you're familiar with the DataFrame from the Python package pandas, then you may remember the notebook output of a DataFrame is nicely formatted in HTML. Let's write a function in Rust to achieve something similar for our 2D arrays, we'll name it show_array(). This function will take an ndarray::Array as a parameter and then loop through the first axis per table row, and the second axis per table column. As we loop through these elements we will appropriately surround them in HTML tags to construct a table with rows and columns. This table will then be printed and rendered in the output cell as HTML.

In [5]:
pub fn show_array<T: Debug>(values: &Array2<T>) {
    let mut html = String::new();
    html.push_str("<table>");
    for r in 0..(values.shape()[0]) {
        html.push_str("<tr>");
        for c in 0..values.shape()[1] {
            html.push_str("<td>");
            html.push_str(&format!("{:?}", values[[r, c]]));
            html.push_str("</td>");
        }
        html.push_str("</tr>");            
    }
    html.push_str("</table>");
    println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT", html);
}

Now let's invoke this function and pass in the data array from the previous example.

In [6]:
show_array(&data_2D);

We can see that the presentation of our array has been improved significantly.

The DARN Crate

To make things even easier throughout this book, we will add this function to the crate associated with this book, darn (Data Analysis with Rust Notebooks), which is published the Rust Package Registry. All we need to do now is use extern crate darn; to get this function, which we may extend in future to support column headers. Let's use the same example code, but this time we will use the function provided by darn.

In [7]:
darn::show_array(&data_2D);
Out[7]:
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0

Conclusion

In this section, we improved the presentation of the cell output for our arrays. This will generally improve the presentation of our notebooks, and the function is now available and ready to use as part of the darn crate in the following sections.

Support this work

You can access this notebook and more by getting the e-book on Data Analysis with Rust Notebooks.