Welcome to PyBloqs’s documentation!

Installation

For installation steps, please refer to the project README.

General use

The following section is available as a Jupyter notebook in docs/source/examples.ipynb.

Composable Blocks & Interactive Charts

This notebook is a simple illustration of the python API for Blocks and Highcharts interactive charts.

What are Blocks? Blocks are composable layout elements for easily building HTML, PDF, PNG and JPG based reports. At the same time, all block constructs can be rendered in-line in IPython Notebooks (as will be shown later). This has practical benefits like a single backtest function that can be used for quick analysis during research work in a notebook, but also directly injected into more formal reports with having to fumble around with intermediate formats.

Practically all the functionality is based on HTML rendering. HTML is a declarative, tree based language that is easy to work with and fungible. Blocks are also declarative, composable into a tree and are meant to be dead simple. The match was thus quite natural.

The blocks do not try to match the power and precision of latex. Such an undertaking would be not only out of the scope of a simple library, but would mean the reinvention of latex with all the gnarliness that comes with it.

This notebook aims to showcase the functionality and offers some examples to help people get started.

Imports & Data

[1]:
%%capture
import numpy as np
import pandas as pd
import pandas.util.testing as pt
from datetime import datetime

import pybloqs as p
[2]:
df = pd.DataFrame((np.random.rand(200, 4)-0.5)/10,
                  columns=list("ABCD"),
                  index=pd.date_range(datetime(2000,1,1), periods=200))

df_cr = (df + 1).cumprod()

a = df_cr.A
b = df_cr.B
c = df_cr.C
c.name = "C"

Using Blocks

Obligatory “Hello World!”

[3]:
p.Block("Hello World!")
[3]:
Hello World!

Play around with alignment

[4]:
p.Block("Hello World!", h_align="left")
[4]:
Hello World!

Adding a title

[5]:
p.Block("Hello World!", title="Announcement", h_align="left")
[5]:

Announcement

Hello World!

Writing out dataframes

[6]:
p.Block(df.head())
[6]:
A B C D
2000-01-01 00:00:00 0.03 -0.02 -0.01 -0.04
2000-01-02 00:00:00 0.01 0.02 0.02 -0.05
2000-01-03 00:00:00 0.00 -0.04 -0.02 -0.03
2000-01-04 00:00:00 -0.02 0.04 0.01 -0.03
2000-01-05 00:00:00 -0.05 0.04 -0.04 -0.02

Writing out matplotlib plots

[7]:
p.Block(df.A.plot())
[7]:

Raw HTML output

[8]:
p.Block("<b>this text is bold</b>")
[8]:
this text is bold

Composing blocks

[9]:
p.VStack([p.Block("Hello World!", title="Announcement"), p.Block("<b>this text is bold</b>")])
[9]:

Announcement

Hello World!
this text is bold

In most cases, one does not need to explicitly wrap elements in blocks

[10]:
p.Block(["Block %s" % i for i in range(8)])
[10]:
Block 0
Block 1
Block 2
Block 3
Block 4
Block 5
Block 6
Block 7

Splitting composite blocks into columns

[11]:
p.Block(["Block %s" % i for i in range(8)], cols=4)
[11]:
Block 0
Block 1
Block 2
Block 3
Block 4
Block 5
Block 6
Block 7

Layout styling is cascading - styles will cascade from parent blocks to child blocks by default. This behavior can be disabled by setting inherit_cfg to false on the child blocks, or simply specifying the desired settings explicitly.

[12]:
p.Block(["Block %s" % i for i in range(8)], cols=4, text_align="right")
[12]:
Block 0
Block 1
Block 2
Block 3
Block 4
Block 5
Block 6
Block 7

Using specific block types is simple as well. As an example - the Paragraph block:

[13]:
p.Block([p.Paragraph("First paragraph."),
         p.Paragraph("Second paragraph."),
         p.Paragraph("Third paragraph.")], text_align="right")
[13]:

First paragraph.

Second paragraph.

Third paragraph.

The Pre block preserves whitespace formatting and is rendered using a fixed width font. Useful for rendering code-like text.

[14]:
p.Pre("""
some:
  example:
    yaml: [1,2,3]
  data: "text"
""")
[14]:
some:
  example:
    yaml: [1,2,3]
  data: "text"

Creating custom blocks is trivial. For the majority of the cases, one can just inherit from the Container block, which has most of the plumbing already in place:

[15]:
class Capitalize(p.Raw):
    def __init__(self, contents, **kwargs):
        # Stringify and capitalize
        contents = str(contents).upper()

        super(Capitalize, self).__init__(contents, **kwargs)

Capitalize("this here text should look like shouting!")
[15]:
THIS HERE TEXT SHOULD LOOK LIKE SHOUTING!
[16]:
# Emails a block (or a report consisting of many blocks). The emailing is independent of previous reports being saved (e.g. there is no need to call save
# before emailing).
from smtplib import SMTPServerDisconnected

try:
    p.Block('').email()
except SMTPServerDisconnected:
    print("Please create ~/.pybloqs.cfg with entry for 'smtp_server'. See README.md and pybloqs/config.py for details.")
Please create ~/.pybloqs.cfg with entry for 'smtp_server'. See README.md and pybloqs/config.py for details.

Page break

[17]:
blocks = [p.Block("First page", styles={"page-break-after": "always"}),
          p.Block("Second page")]
r = p.VStack(blocks)
r.save("two_page_report.pdf")
['wkhtmltopdf', '--no-stop-slow-scripts', '--debug-javascript', '--javascript-delay', '200', '--page-size', 'A4', '--orientation', 'Portrait', '--enable-smart-shrinking', '/tmp/69e57e72c8.html', 'two_page_report.pdf'] returned:

None
[17]:
'two_page_report.pdf'

Table formatting

The following section is available as a Jupyter notebook in docs/source/table_formatting.ipynb.

HTML Tables and Table Formatting

Creating a HTML Table from pandas.DataFrame

The following is hopefully sufficient for most applications (feedback welcome!):

from pybloqs import Blockb = Block(df)

When called only with DataFrame as parameter, a set of default formatters is applied:

table_block = Block(df, formatters=None, use_default_formatters=True)
[1]:
%%capture
import pybloqs as abl
from pybloqs import Block
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(4,4), index=['a','b','c','d'], columns = ['aa','bb','cc','dd'])
df.index.name = 'ABC'
Block(df)

NB: The grid between cells is from jupyter default CSS. It will not show if the block is displayed with b.show() .

Formatting Tables with Table Formatters

Formatters are functions which add a single specific formatting aspect (e.g. bold, font-size, alignment, multi-index display). Formatters can be stacked together as a list to produce desired layout. The list is then passed to HTMLJinjaTableBlock.

Use of default formatters can be disabled completely. Additional formatters can be used on top or instead of default formatters. Formatters change appearance by modifying cell values and adding CSS styles.

‘Exotic’ formatters, which are used only in a single context, can be defined locally. A set of general use formatters can be found in pybloqs.block.table_formatters.

All formatters take the following parameters:

:rows List of rows, where formatter should be applied
:columns List of columns, where formatter should be applied
:apply_to_header_and_index True/False, if set to True, formatter will be applied to all index and header cells

If rows and columns are both None, formatter is applied to all cells in table.

An example:

[2]:
import pybloqs.block.table_formatters as tf
table_block = Block(df)
table_block_raw = Block(df, use_default_formatters=False)

fmt_pct = tf.FmtPercent(1, columns=['bb','cc'], apply_to_header_and_index=False)
fmt_totals = tf.FmtAppendTotalsRow(total_columns=['aa','dd'])
fmt_highlight = tf.FmtHighlightText(columns=['bb'], rows=['d'], apply_to_header_and_index=False)
formatters=[fmt_pct,fmt_totals, fmt_highlight]
table_block_additional_formatters = Block(df, formatters=formatters)

fmt_mult = tf.FmtMultiplyCellValue(1e6, '')
fmt_sep = tf.FmtThousandSeparator()
formatters=[fmt_mult, fmt_sep]
table_block_new_formatters = Block(df, formatters=formatters, use_default_formatters=False)

row1 = abl.HStack([table_block, table_block_raw])
row2 = abl.HStack([table_block_additional_formatters, table_block_new_formatters])

abl.VStack([row1,row2])
[2]:
ABC aa bb cc dd
a 0.69 0.41 0.26 0.13
b 0.50 0.68 0.94 0.67
c 0.60 0.71 0.07 0.92
d 0.35 0.48 0.22 0.88
ABC aa bb cc dd
a 0.6852474251934583 0.41120411129050705 0.26466502351221766 0.13038676806147742
b 0.5009290828732462 0.6756706339375467 0.9411918781685857 0.6719157166512993
c 0.6010710732450969 0.7148296329613193 0.07250899622251417 0.9247174989893737
d 0.35060978076583094 0.4773133603344051 0.22375109653340475 0.878994223447428
ABC aa bb cc dd
a 0.69 41.1% 26.5% 0.13
b 0.50 67.6% 94.1% 0.67
c 0.60 71.5% 7.3% 0.92
d 0.35 47.7% 22.4% 0.88
Total 2.14 2.61
ABC aa bb cc dd
a 685,247 411,204 264,665 130,387
b 500,929 675,671 941,192 671,916
c 601,071 714,830 72,509 924,717
d 350,610 477,313 223,751 878,994
General formatters

The following formatters handle miscalleneous general tasks #### Replace NaN

FmtReplaceNaN(value='')

Replaces all np.nan values in specified range with provided value. By default uses empty string.

FmtAlignCellContents
FmtAlignCellContents(alignment='center')

Aligns content inside cells within specified range. Valid values for alignment are left|right|center|justify|initial|inherit (anything that the CSS tag text-align understands).

FmtAlignTable
FmtAlignTable(alignment)

Aligns the entire table relative to its entironment. Valid values for alignment are center, right, left.

FmtHeader
FmtHeader(fixed_width='100%', index_width=None, column_width=None, rotate_deg=0,top_padding=None, no_wrap=True)

Creates a table with fixed-width columns.

:fixed_width Total width of table
:index_width Fixed width of index column
:column_width Fixed width of all other columns
:rotate_deg Value in degrees by which to rotate table header cells
:top_padding: additional vertical space above table, may be necessary when using rotated headers
:no_wrap True/False, disables line-breaks within header cell when set to True

An example (NB, jypiter ignores top-padding, which otherwise works in direkt browser display and PDF output):

[3]:
fmt_header = tf.FmtHeader(fixed_width='20cm',index_width='30%', top_padding='3cm', rotate_deg=30)
Block(df, formatters=[fmt_header])
[3]:
ABC aa bb cc dd
a 0.69 0.41 0.26 0.13
b 0.50 0.68 0.94 0.67
c 0.60 0.71 0.07 0.92
d 0.35 0.48 0.22 0.88
FmtStripeBackground
FmtStripeBackground(first_color=colors.LIGHT_GREY, second_color=colors.WHITE, header_color=colors.WHITE,

Creates a repeating color patters in the background of the specified cells.

:first_color CSS color, for odd row numbers
:second_color CSS color, for even row numbers
:header_color CSS color applied to header row
FmtAddCellPadding
FmtAddCellPadding(left=None, right=None, top=None, bottom=None, length_unit='px')

Add space on sides of selected cells. #### FmtAppendTotalsRow

FmtAppendTotalsRow(row_name='Total', operator=OP_SUM, bold=True, background_color=colors.LIGHT_GREY, font_color=None, total_columns=None)

Adds a line at the end of the table filled with values computed columnwise. For an example, see section [Formatting Tables with Table Formatters]

:row_name Label for additional row shown in index
:operator Computational operation to perform on columns, e.g. tf.OP_SUM, tf.OP_MEAN, tf.OP_NONE
:total_columns Names of columns to apply operator to. If None, operator is applied to all columns.
:bold True/False, applied bold font-style to cells in added row
:font_color CSS color for cell text in added row
:background_color CSS color for cell background in added row
FmtHideRows
FmtHideCells(rows=None, columns=None)

Hides cells in the intersection of rows and columns list. If only rows or columns is specified and the other is left None, the entire row or column is hidden. #### FmtPageBreak

FmtPageBreak(no_break=True, repeat_header=True)

Defines table behaviour around page-breaks. Please note that Webkit-based browsers (Chrome, Safari and wkhtmltopdf as well) do not handle the repeat-header property properly, especially when headers are rotated. This bug is reported and not resolved since 6 years. Functionality in Firefox is fine, including rotated headers.

:no_break True/False, avoids splitting table on page break
:repeat_header True/False, when table is split accross page, repeat header on the next page
Displaying text

The following formatters modify the display of both text and numbers. #### FmtFontsize

FmtFontsize(fontsize, format='px')

Sets the font-size property of specified cells. A nice property is vw, which gives the font-size as percent of viewport-width. This will scale the font with the witdh of the page and is thus suited for wide tables which should still look fine (but small) when output as PDF.

FmtHighlightText
FmtHighlightText(bold=True, italic=True, font_color=colors.BLUE)

Sets various properties of character display.

:bold True/False, sets bold font-style
:italic True/False, sets italic font-style
:font_color CSS color, sets text color
FmtHighlightBackground
FmtHighlightBackground(color=colors.RED)

Sets the background color of specified cells.

FmtBold
FmtBold()

Sets font-style bold in specified cells.

Displaying numbers

The following formatters only apply to numbers. Please note that some convert the number to a string when applied.

FmtNumbers, FmtDecimals, FmtThousandSeparator, FmtPercent

E.g.

FmtPercent(n_decimals)

If cell content is a number, it is changed to a string with approriate formatting, e.g. number of decimals (FmtDecimals), with a comma as thousands separator (FmtThousandSeparator), or as percent with a trailing ‘%’ sign(FmtPercent).

FmtNumbers(fmt_string)

is the general purpose formatting class, which accets any formatting string. For more information about formatting strings, see https://pyformat.info/

FmtMultiplyCellValue, FmtValueToMillion, FmtValueToPercent, FmtValueToBps

E.g.

FmtValueToPercent(suffix='%')

Multiplies number in cell by a given factor, thus keeping is a number. A suffix an be added to the columns header. This is useful for tables, which all contain percentage values and where a ‘%’ sign after each value is undesireable.

FmtMultiplyCellValue(d, suffix)

is the general purpose function, multiplying by any given factor.

Heatmaps

The table formatting has a very flexible heatmap formatter. #### FmtHeatmap

FmtHeatmap(min_color=colors.HEATMAP_RED, max_color=colors.HEATMAP_GREEN, threshold=0.,axis=None)

Creates a heatmap in the intersection of specified columns and rows.

:min_color CSS color, which is the color applied as background-color at the minimum negative value
:max_color CSS color, which is the color applied as background-color at the maximum positive value
:threshold specifies an interval around 0, in which no heatmapping is applied
:axis Number, either 0 (horizontal), or 1 (vertical) or None. If None, heatmap is applied over all selected cells. If set to a number, heatmap is applied column-wise or row-wise reprectively.
[4]:
import string
# Create DataFrame
df_size = 15
index = [c for c in string.ascii_lowercase[:df_size]]
columns = [c+c for c in string.ascii_lowercase[:df_size]]
df = pd.DataFrame(np.random.rand(df_size,df_size), index=index, columns=columns)

# Specify heatmap formatters
# All values in range
fmt_heatmap1 = tf.FmtHeatmap(rows=index[10:16],columns=columns[:5])
# By row
fmt_heatmap2 = tf.FmtHeatmap(rows=index[:3], axis=0, max_color=(255,0,255))
# By column
fmt_heatmap3 = tf.FmtHeatmap(rows=index[5:],columns=columns[10:], axis=1, max_color=(255,255,0))

formatters =[fmt_heatmap1, fmt_heatmap2, fmt_heatmap3]

Block(df, formatters=formatters)
[4]:
aa bb cc dd ee ff gg hh ii jj kk ll mm nn oo
a 0.89 0.95 0.52 0.00 0.76 0.23 0.97 0.23 0.02 0.29 0.78 0.38 0.23 0.57 0.05
b 0.17 0.93 0.70 0.71 0.48 0.77 0.08 0.06 0.22 0.99 0.73 0.36 0.60 0.60 0.63
c 0.77 0.46 0.25 0.97 0.08 0.15 0.40 0.49 0.36 0.64 0.20 0.94 0.03 0.54 0.49
d 0.93 0.28 0.28 0.47 0.07 0.17 0.28 0.25 0.74 0.65 0.20 0.79 0.81 0.82 0.32
e 0.34 0.09 0.84 0.92 0.95 0.50 0.50 0.32 0.36 0.16 0.92 0.01 0.34 0.49 0.56
f 0.44 0.10 0.63 0.61 0.86 0.09 0.31 0.97 0.71 0.87 0.09 0.23 0.03 0.73 0.71
g 0.55 0.74 0.67 0.49 0.89 0.56 0.25 0.10 0.94 0.78 0.57 0.73 0.61 0.92 0.86
h 0.21 0.15 0.95 0.53 0.47 0.48 0.27 0.94 0.02 0.09 0.29 0.65 0.55 0.28 0.56
i 0.10 0.50 0.44 0.58 0.54 0.54 0.11 0.90 0.00 0.21 0.09 0.95 0.70 0.18 0.19
j 0.55 0.86 0.17 0.73 0.79 0.51 0.47 0.17 0.67 0.77 1.00 0.29 0.90 0.45 0.91
k 0.85 0.93 0.69 0.54 0.40 0.29 0.74 0.64 0.34 0.85 0.23 0.72 0.25 0.35 0.14
l 0.14 0.83 0.89 0.46 0.36 0.98 0.68 0.57 0.53 0.10 0.55 0.67 0.96 0.24 0.43
m 0.16 0.65 0.78 0.04 0.03 0.16 0.57 0.40 0.48 0.02 0.30 0.95 0.82 0.71 0.99
n 0.05 0.79 0.58 0.84 0.58 0.76 0.61 0.06 0.32 0.47 0.03 0.37 0.75 0.73 0.24
o 0.55 0.66 0.17 0.81 0.49 0.78 0.85 0.41 0.94 0.02 0.21 0.41 0.40 0.98 0.49
Multi-index tables

Multi-index dataframes can be expanded to sinlesimple index dataframes with special formatting applied. #### FmtExpandMultiIndex

FmtExpandMultiIndex(total_columns=None, operator=OP_SUM, bold=True, indent_px=20, hline_color=colors.DARK_BLUE, level_background_colors=None, level_text_colors=None)

See example below. Can handle non-unique indices.

:total_columns List of columns on which to apply operator at higher index levels
:operator Computational operation to perform on columns, e.g. tf.OP_SUM, tf.OP_MEAN, tf.OP_NONE
:bold True/False, changes higher-level font-style to bold
:index_px Indentation space per level in pixels
:hline_color CSS color, sets the color of the horizontal line separating higher-level rows
:level_background_colors List of CSS colors with size equal to number of index-levels, background_color applied in each index-level row
:level_text_colors List of CSS colors with size equal to number of index-levels, text color applied in each index-level row
[5]:
def make_multiindex_table():
    fmt = tf.FmtExpandMultiIndex()
    idx = np.array([['a', 'a', 'b', 'b'], ['aa', 'ab', 'ba', 'bb']])
    idx_tuples = list(zip(*idx))
    multi_index = pd.MultiIndex.from_tuples(idx_tuples, names=['a-level', 'aa-level'])
    columns = ['column0', 'column1', 'column2']
    data = pd.DataFrame(np.arange(12, dtype=float).reshape(4, 3), index=multi_index, columns=columns)
    return data

mi_df = make_multiindex_table()
print(mi_df)

fmt_multiindex = tf.FmtExpandMultiIndex(operator=tf.OP_SUM)
Block(mi_df, formatters=[fmt_multiindex], use_default_formatters=False)
                  column0  column1  column2
a-level aa-level
a       aa            0.0      1.0      2.0
        ab            3.0      4.0      5.0
b       ba            6.0      7.0      8.0
        bb            9.0     10.0     11.0
[5]:
column0 column1 column2
a 3.0 5.0 7.0
aa 0.0 1.0 2.0
ab 3.0 4.0 5.0
b 15.0 17.0 19.0
ba 6.0 7.0 8.0
bb 9.0 10.0 11.0
Writing custom formatters

Custom formatters can either be either added to pybloqs or included with user-space code. The latter is useful for very specific formatters, which have little chance of being reused and thus do not need to sit in the code base. In general, formatters are classes that inherit from TableFormatter base class. The base class provides the following function hooks, which do not need to be all implemented by the new formatter. In fact, most formatters only make use of one or two function hooks. The available hooks are: * _insert_additional_html: Can be used to put HTML or JavaScript code in front of table. * _modify_dataframe: Access and potentially modify DataFrame before any other hook functions become active. * _modify_cell_content: Access and potentially modify cell value. Called for each cell separately. * create_table_level_css: Insert CSS inline style on <table> level * _create_thead_level_css: Insert CSS inline style on <thead> level * _create_row_level_css: Insert CSS inline style on <th> and <tr>level * _create_cell_level_css: Insert CSS inline style on <td> level

Interactive plots

PyBloqs supports embedding of interactive plotting libraries such as Bokeh and Plotly

Interactive Plots Integration (Plotly, Bokeh, Highcharts)

PyBloqs also supports plotting with interactive plotting libraries such as Plotly(offline), Bokeh and Highcharts.

The plots will be rendered in HTML and will rely on your browser’s CSS and JS capabilities to provide interactivity.

Plotly Example

[1]:
%%capture
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.graph_objs as go
# The lines below are only needed for notebook output
from plotly.offline import init_notebook_mode
init_notebook_mode()
[2]:
%%capture
trace1 = go.Scatter(x=[1,2,3], y=[4,5,6], marker={'color': 'red', 'symbol': 104, 'size': 10},
                    mode="markers+lines",  text=["one","two","three"], name='1st Trace')

data=go.Data([trace1])
layout=go.Layout(title="First Plot", xaxis={'title':'x1'}, yaxis={'title':'x2'})
plotly_fig=go.Figure(data=data,layout=layout)
[3]:
import pybloqs as p
p.Block(plotly_fig)
[3]:

Bokeh Example

[4]:
import numpy as np
from six.moves import zip
from bokeh.plotting import figure as b_fig
# The lines below are only needed for notebook output
from bokeh.io.output import output_notebook
from bokeh.resources import INLINE
output_notebook(resources=INLINE)
Loading BokehJS ...
[5]:
N = 4000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors2 = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)]
bokeh_fig = b_fig(width=300, height=300)
bokeh_fig.scatter(x,y, radius=radii, fill_color=colors2, fill_alpha=0.6, line_color=None)
[5]:
GlyphRenderer(
id = '70b52809-b7ff-469a-ba98-be4685fe9d76', …)
[6]:
p.Block(bokeh_fig)
[6]:

Combining Bokeh and Plotly plots with HStack

[7]:
p.HStack([p.Block(bokeh_fig), p.Block(plotly_fig)])
[7]:

Highcharts examples

Please note: Highcharts has a proprietary license

Simple line chart

When evaluated as the last expression in a Notebook Cell, the plot is automatically displayed inline. Note how the plot name (hover over the line to see the little popup) is taken from the input data (if available).

[8]:
import pandas as pd
import numpy as np
from datetime import datetime

df = pd.DataFrame((np.random.rand(200, 4)-0.5)/10,
                  columns=list("ABCD"),
                  index=pd.date_range(datetime(2000,1,1), periods=200))
df_cr = (df + 1).cumprod()
a = df_cr['A']
b = df_cr['B']
c = df_cr['C']
[9]:
import pybloqs.plot as pp
pp.interactive()
pp.Plot(a)
Interactive mode initialized successfully
[9]:

Saving as interactive HTML

[10]:
pp.Plot(df).save("chart_sample.html")
[10]:
'chart_sample.html'
Scatter Plot

Regular scatter plot, with zooming on both the x and y axes.

[11]:
pp.Plot(df.values[:,:2], pp.Scatter(pp.Marker(enabled=True)), pp.Chart(zoom_type="xy"))
[11]:
Bar Charts

Notice how when viewing all the data at once, the chart shows monthly data, yet zooming in reveals additional detail at up to daily resolution. This is accomplished by using a custom data grouping.

[12]:
bar_grouping = pp.DataGrouping(approximation="open", enabled=True, group_pixel_width=100)
[13]:
# Bar chart from a dataframe
pp.Plot(df, pp.Column(bar_grouping))
[13]:
[14]:
# Stacked bar chart
pp.Plot(df, pp.Column(bar_grouping, stacking="normal"))
[14]:
[15]:
# Composite bar chart from two separate plots.
s2 = pp.Plot([pp.Plot(a, pp.Column(bar_grouping)),
              pp.Plot(b, pp.Column(bar_grouping))])
s2
[15]:
Comparing series in a dataframe

Plot the cumulative percentage difference between input series (or columns of a dataframe). The cumulative difference is always calculated from the start of the observation window. This results in intuitively correct behavior when zooming in or sliding the window, as the chart will dynamically recalculate the values. Incredibly useful for comparing model performance over time for example as one doesn’t need to manually normalize money curves for different periods.

[16]:
s3 = pp.Plot(df_cr,
             pp.PlotOptions(pp.Series(compare="percent")),
             pp.TooltipPct(),
             pp.YAxisPct())
s3
[16]:
Three series on separate side-by-side Y axes
[17]:
s4 = pp.Plot([pp.Plot(a),
            pp.Plot(b, pp.YAxis(pp.Title(text="B Axis"), opposite=True)),
            pp.Plot(c, pp.YAxis(pp.Title(text="C Axis"), opposite=True, offset=40))])
s4
[17]:
Two series on separate subplots
[18]:
s5 = pp.Plot([pp.Plot(a, pp.Line(), pp.YAxis(pp.Title(text="a only"), height=150)),
               pp.Plot(b, pp.Column(), pp.YAxis(pp.Title(text="b only"),
                                                   top=200, height=100, offset=0))],
              pp.Tooltip(value_decimals=2), height="400px")
s5
[18]:
Creating a report from multiple charts and saving as HTML or PDF. Or sending it as email.
[19]:
import pandas.util.testing as pt
b = p.Block([p.Block(pt.makeTimeDataFrame().tail(10), title="A table", title_level=1),
           p.Block([s2, s3], title="Side-by-side Plots", cols=2),
           p.Block(title="Full Width Plots", styles={"page-break-before": "always"}),
           p.Block(s4, title="Side by Side Axes"),
           p.Block(s5, title="Composite Plots")], title="Dynamic Reports")
[20]:
b.save("charts_test.pdf")
b.save("charts_test.html")
['wkhtmltopdf', '--no-stop-slow-scripts', '--debug-javascript', '--javascript-delay', '200', '--page-size', 'A4', '--orientation', 'Portrait', '--enable-smart-shrinking', '/tmp/65a1f98236.html', 'charts_test.pdf'] returned:

None
[20]:
'charts_test.html'

Configuration

Local configuration of features like email sending and PDF backend.

Configuration

PyBloqs has a few elements that can be configured locally:

  • Email sending
  • Public html directory (where Block.publish() will save output)
  • Tmp html directory (where Block.show() will save output)
  • PDF conversion backend
  • Image conversion backend

The defaults are set in pybloqs/config.py . Any entry in the dict “user_config” in config.py can be overwritten with a local file in the user’s home directory at ~/.pybloqs.cfg . The format of .pybloqs.cfg is YAML, so changing the PDF conversion backend to Chrome-headless would be done with the following line:

pdf_converter: chrome_headless

Indices and tables