In [1]:
import io
import os
import urllib.request

import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
%matplotlib inline
import numpy as np
import pandas as pd

In [2]:
from supersight import Dashboard, Plots_gatherer

1. Create a collection of plots

The main goal of supersight is to gather your plots and organize these plots within a mini web site. Actually, you can embed any svg plots but this tool was designed to work with MatPlotlib.

We start by creating a plots gatherer object.
It will contain our plots for easy retrieval:


In [3]:
plots = Plots_gatherer()

Let's generate a simple plot :


In [4]:
graph = plt.figure()
x = np.linspace(0, 10, 30)
y = np.sin(x)
plt.plot(x, y, 'o', color='black');

buf = io.BytesIO()
plt.savefig(buf, format='svg')
plots.add_plot("first graph", buf)


Once your are happy with it, supersight requires you to save your plot in BytesIO objects.
SVG format is advised since it is lightweight and vector-based.
The three last lines are required to save your plot in a way supersight can handle.

We will make a new plot :


In [5]:
graph = plt.figure()
plt.plot(x, y, '-ok')

buf = io.BytesIO()
plt.savefig(buf, format='svg')
plots.add_plot("second graph", buf)


And a third graph for the example :


In [6]:
graph = plt.figure()
plt.scatter(np.random.randn(100), np.random.randn(100));

buf = io.BytesIO()
plt.savefig(buf, format='svg')
plots.add_plot("third graph", buf)


Below is a hack to load external svg in our mini site :


In [7]:
logo = open("logo_small.svg", "rb")
logo_buf = io.BytesIO(logo.read())
plots.add_plot("logo", logo_buf)
logo.close()

Let's add some pandas dataframes as SuperSight can also display data tables within pages :


In [8]:
data=pd.read_csv("https://s3.eu-central-1.amazonaws.com/camo-bucket/misc_to_link/data_sample.csv")

In [9]:
df1 = data.head() # a classic df

In [10]:
# a simple pivot table
df2 = pd.pivot_table(data, index = 'Gender',
                     columns = ['Smoking'],
                     aggfunc = np.average)

2. Create a dashboard

Once your plots are saved in BytesIO objects you can start making a dashboard by initiating a Dashboard object :


In [11]:
ds = Dashboard()

By default, the mini site comes with a home page. We will add two sections. The first section will contain only one page and the second will contain two pages.
As a convention a single-page section is composed of a page named "Page 1".


In [12]:
ds.add_section("Section 1")
# We just created a section named Section 1. Pick a custom name that describe an underlying idea of your plots.
ds.sections["Section 1"].add_page("Page 1")


ds.add_section("Section 2")
# We just created a section named Section 2. We said we wanted two pages in this section :
ds.sections["Section 2"].add_page("Interesting Page")
ds.sections["Section 2"].add_page("Great Page") # name it as you want !

Each page is organised following a classic Bootstrap grid. Users can display elements on this grid. The default grid is [(6, 6), (6, 6), (6, 6)]. It means the page displays three rows with two elements on each row.
Of course, this layout can be overridden. For the Home page we will display the supersight logo.
According to the Bootstrap grid system the layout for a single row and a single element is [(12,)].


In [13]:
ds.sections["Home"].pages["Home"].layout = [(12,)]

In [14]:
ds.sections["Home"].pages["Home"].add_element(name = "logo", plot_object = plots.get_plot("logo"))

In section 1, the default grid can stay (hence six plots). Users can populate the grid with their plots. This process populate the grid from left to right and from top to bottom :


In [15]:
ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 1", plot_object = plots.get_plot("first graph"))
ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 2", plot_object = plots.get_plot("first graph"))

ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 3", plot_object = plots.get_plot("second graph"))
ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 4", plot_object = plots.get_plot("second graph"))

ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 5", plot_object = plots.get_plot("third graph"))
ds.sections["Section 1"].pages["Page 1"].add_element(name = "plot 6", plot_object = plots.get_plot("third graph"))
# add comments :
ds.sections["Section 1"].pages["Page 1"].elements["plot 3"].add_comment_below("This plot will convince you !")

Users can also use MathJax to dsiplay equations in comments :


In [16]:
ds.sections["Section 1"].pages["Page 1"].elements["plot 6"].add_comment_below("""This one is not bad either ! <br>
You can also use MathJax : $x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$""")

Now we can try to vary the layout. For instance on the first row we will display a plot two third the width.
On the second row we can display a unique plot with a heading.
And then three small plots on the last row.


In [17]:
ds.sections["Section 2"].pages["Interesting Page"].layout = [(8, 4), (12,), (4, 4, 4)]

ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 1", plot_object = plots.get_plot("third graph"), heading = None)
ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 2", plot_object = plots.get_plot("first graph"), heading = None)

ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 3", plot_object = plots.get_plot("second graph"), heading = "Interesting Heading")

ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 4", plot_object = plots.get_plot("third graph"), heading = None)
ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 5", plot_object = plots.get_plot("third graph"), heading = None)
ds.sections["Section 2"].pages["Interesting Page"].add_element(name = "plot 6", plot_object = plots.get_plot("third graph"), heading = None)

In [18]:
dummy ="""One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a 
horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, 
slightly domed and divided by arches into stiff sections. The bedding was hardly able to cover it and seemed ready 
to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about 
helplessly as he looked. "What's happened to me?" he thought. It wasn't a dream. His room, a proper human room 
although a little too small, lay peacefully between its four familiar walls. A collection of textile samples 
lay spread out on the table - Samsa was a travelling salesman - 
and above it there hung a picture that he had recently cut out of an illustrated magazine and housed"""

In [19]:
ds.sections["Section 2"].pages["Interesting Page"].elements["plot 1"].add_comment_above(dummy)
ds.sections["Section 2"].pages["Interesting Page"].elements["plot 2"].add_comment_below(dummy[:277])

To finish, we can display text and tables on the "great page" !


In [20]:
ds.sections["Section 2"].pages["Great Page"].layout = [(8, 4), (6,6), (12,), (12,), (12,)]
ds.sections["Section 2"].pages["Great Page"].add_element(name = "info", heading = "Contact Information", 
                                                         comment_below = "camille.moatti@gmail.com <br> +33 (0) X XX XX XX XX", 
                                                         comment_above = "<br>Camille Moatti")
ds.sections["Section 2"].pages["Great Page"].add_element(name = "text", plot_object = plots.get_plot("logo"))

In [21]:
ds.sections["Section 2"].pages["Great Page"].add_element(name = "small table")
ds.sections["Section 2"].pages["Great Page"].elements["small table"].add_table(df1)

In [22]:
ds.sections["Section 2"].pages["Great Page"].add_element(name = "small table 2")
ds.sections["Section 2"].pages["Great Page"].elements["small table 2"].add_table(df1)

In [23]:
ds.sections["Section 2"].pages["Great Page"].add_element(name = "small table 3")
ds.sections["Section 2"].pages["Great Page"].elements["small table 3"].add_table(df1)

In [24]:
ds.sections["Section 2"].pages["Great Page"].add_element(name = "pivot table")
ds.sections["Section 2"].pages["Great Page"].elements["pivot table"].add_table(df2)

In [25]:
df2


Out[25]:
Age BMI Cholesterol SystolicBP
Smoking No Yes No Yes No Yes No Yes
Gender
Female 56.2500 58.910811 26.872642 26.370270 223.929245 227.286486 143.933962 147.845946
Male 58.3125 58.131429 27.043056 26.428571 227.425000 214.548571 146.022222 146.785714

In [26]:
df3 = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
df3


Out[26]:
A B C D
0 -0.423006 -0.513240 1.061477 -0.558365
1 -1.238648 0.846119 -0.644261 -0.552258
2 0.817111 -0.145415 0.217698 1.408786
3 0.259407 0.830762 0.490901 0.548290
4 -0.847908 -1.649884 0.141069 1.303094
5 -0.227206 -0.282176 1.371899 -1.012495
6 1.198979 -0.155085 -0.543779 -0.281877
7 0.567682 -1.526282 -1.067063 -0.632593
8 0.566751 -0.119069 0.253410 0.482566
9 -0.485200 0.671534 -0.339837 0.065606
10 1.556381 0.295662 1.474455 0.047173
11 -1.225802 -0.850209 0.592541 -1.056712
12 -0.580879 0.496014 0.165034 -1.866393
13 0.430223 0.194146 1.639503 0.157637
14 -0.217383 0.271142 -1.848356 -0.738657
15 -2.303244 0.529919 -2.284018 2.224811
16 0.656221 -1.050085 1.266227 0.608433
17 -0.536968 -0.414605 0.181599 0.331550
18 -0.357246 -1.251896 0.754451 -0.175353
19 -0.151432 -0.170119 -0.604615 0.272812
20 -1.194403 -0.605516 -1.428377 -0.273258
21 1.354393 0.538463 -0.797210 -0.887732
22 -0.578657 1.608378 1.213664 2.055374
23 -1.478837 2.313627 0.016663 1.972471
24 -0.792240 0.424837 1.738613 -0.759788
25 -0.471415 -1.236428 0.550395 -1.184716
26 0.775889 1.074326 0.117783 1.789963
27 -1.163204 -0.239207 -2.214845 1.025999
28 1.724699 0.553749 0.249971 0.232381
29 0.090348 -0.924931 -0.584984 -0.256011
30 -0.262006 -0.228765 1.045235 -1.352000
31 -2.508980 -0.106955 -1.558886 -1.114480
32 0.782665 -1.250012 0.611308 0.924013
33 -0.109871 0.445168 0.853253 0.430104
34 1.971616 1.517017 0.480788 -1.321725
35 0.955586 -0.113747 -0.089101 -0.345138
36 -1.049497 1.037004 0.984285 2.088155
37 -0.544250 0.151533 0.798459 1.729618
38 -1.389170 -0.805569 0.313630 0.223347
39 0.125324 -0.376938 1.290942 1.758855
40 0.929767 1.911050 0.392129 1.305046
41 0.350533 0.965447 0.914377 -1.301250
42 0.676759 0.895821 -1.128432 -1.820969
43 -0.599143 0.316410 0.144542 0.875234
44 0.565722 -0.068159 -1.840860 -0.230524
45 0.475021 2.422857 -1.768633 0.759660
46 0.449391 1.183444 2.014046 -0.834593
47 -0.373157 -0.128564 0.067576 0.690839
48 0.914682 1.362880 -0.313492 -1.205855
49 -0.059753 -0.520307 -0.544926 0.519200

In [27]:
d = dict(selector="th",
    props=[('text-align', 'center')])

In [28]:
newdf = df3.style.bar(subset=['A', 'B'], align='mid', color=['#d65f5f', '#5fba7d']).format("{:.2}%").set_properties(**{'text-align': 'center'}).set_table_styles([d])
newdf


Out[28]:
A B C D
0 -0.42% -0.51% 1.1% -0.56%
1 -1.2% 0.85% -0.64% -0.55%
2 0.82% -0.15% 0.22% 1.4%
3 0.26% 0.83% 0.49% 0.55%
4 -0.85% -1.6% 0.14% 1.3%
5 -0.23% -0.28% 1.4% -1.0%
6 1.2% -0.16% -0.54% -0.28%
7 0.57% -1.5% -1.1% -0.63%
8 0.57% -0.12% 0.25% 0.48%
9 -0.49% 0.67% -0.34% 0.066%
10 1.6% 0.3% 1.5% 0.047%
11 -1.2% -0.85% 0.59% -1.1%
12 -0.58% 0.5% 0.17% -1.9%
13 0.43% 0.19% 1.6% 0.16%
14 -0.22% 0.27% -1.8% -0.74%
15 -2.3% 0.53% -2.3% 2.2%
16 0.66% -1.1% 1.3% 0.61%
17 -0.54% -0.41% 0.18% 0.33%
18 -0.36% -1.3% 0.75% -0.18%
19 -0.15% -0.17% -0.6% 0.27%
20 -1.2% -0.61% -1.4% -0.27%
21 1.4% 0.54% -0.8% -0.89%
22 -0.58% 1.6% 1.2% 2.1%
23 -1.5% 2.3% 0.017% 2.0%
24 -0.79% 0.42% 1.7% -0.76%
25 -0.47% -1.2% 0.55% -1.2%
26 0.78% 1.1% 0.12% 1.8%
27 -1.2% -0.24% -2.2% 1.0%
28 1.7% 0.55% 0.25% 0.23%
29 0.09% -0.92% -0.58% -0.26%
30 -0.26% -0.23% 1.0% -1.4%
31 -2.5% -0.11% -1.6% -1.1%
32 0.78% -1.3% 0.61% 0.92%
33 -0.11% 0.45% 0.85% 0.43%
34 2.0% 1.5% 0.48% -1.3%
35 0.96% -0.11% -0.089% -0.35%
36 -1.0% 1.0% 0.98% 2.1%
37 -0.54% 0.15% 0.8% 1.7%
38 -1.4% -0.81% 0.31% 0.22%
39 0.13% -0.38% 1.3% 1.8%
40 0.93% 1.9% 0.39% 1.3%
41 0.35% 0.97% 0.91% -1.3%
42 0.68% 0.9% -1.1% -1.8%
43 -0.6% 0.32% 0.14% 0.88%
44 0.57% -0.068% -1.8% -0.23%
45 0.48% 2.4% -1.8% 0.76%
46 0.45% 1.2% 2.0% -0.83%
47 -0.37% -0.13% 0.068% 0.69%
48 0.91% 1.4% -0.31% -1.2%
49 -0.06% -0.52% -0.54% 0.52%

In [29]:
ds.sections["Section 2"].pages["Great Page"].add_element(name = "styled table")
ds.sections["Section 2"].pages["Great Page"].elements["styled table"].add_table(newdf, isStyled = True)

In [30]:
ds.render()


bootstrap-grid.css
bootstrap-grid.css.map
bootstrap-grid.min.css
bootstrap-grid.min.css.map
bootstrap-reboot.css
bootstrap-reboot.css.map
bootstrap-reboot.min.css
bootstrap-reboot.min.css.map
bootstrap.css
bootstrap.css.map
bootstrap.min.css
bootstrap.min.css.map
jumbotron.css
.DS_Store
bootstrap.js
bootstrap.min.js
Home Home logo
Starting individual page : Section 1 Page 1
Section 1 Page 1 plot 1
Section 1 Page 1 plot 2
Section 1 Page 1 plot 3
Section 1 Page 1 plot 4
Section 1 Page 1 plot 5
Section 1 Page 1 plot 6
Starting individual page : Section 2 Interesting Page
Section 2 Interesting Page plot 1
Section 2 Interesting Page plot 2
Section 2 Interesting Page plot 3
Section 2 Interesting Page plot 4
Section 2 Interesting Page plot 5
Section 2 Interesting Page plot 6
Starting individual page : Section 2 Great Page
Section 2 Great Page info
Section 2 Great Page text
Section 2 Great Page small table
Section 2 Great Page small table 2
Section 2 Great Page small table 3
Section 2 Great Page pivot table
Section 2 Great Page styled table

3. Customize the default template

SuperSight comes with the possibility to easily hack the default template. I let a plain vanilla Bootstrap 4 template because a lot are already familiar with this framework.
The easier way to get started is to copy the default template from the module.

Here the complete folder 'customized_template' is in the current directory :

ds.render(template='customized_template')

In [ ]: