Advanced custom CSS#
You can pass your custom raw css to fine control the appearance of your tables. You can take advantage of css classes and ids in order to achieve this.
Show code cell source
import pandas as pd
from IPython.core.display import display, HTML
from pysummaries import pandas_to_report_html, get_styles
# a couple of symbols with html codes or html tags
dagger = "†"
sup = "<sup>1</sup>"
sub = "<sub>2</sub>"
nums = [["91 (67.9%) " + sup, "28 (49.1%)", "7 (50.0%)", "126 (61.5%)"],
["43 (32.1%)", "29 (50.9%)", "7 (50.0%)", "79 (38.5%)"],
["50.0 (15.9)", "55.1 (17.9)", "65.3 (10.9)", "52.5 (16.7)"],
["52.0 [4.00, 84.0] " + sub, "56.0 [14.0, 95.0]", "65.0 [49.0, 86.0]", "54.0 [4.00, 95.0]"],
["91 (67.9%)", "28 (49.1%)", "7 (50.0%)", "126 (61.5%)"],
["43 (32.1%)", "29 (50.9%)", "7 (50.0%)", "79 (38.5%)"],
["50.0 (15.9)", "55.1 (17.9)", "65.3 (10.9)", "52.5 (16.7)"],
["52.0 [4.00, 84.0]", "56.0 [14.0, 95.0]", "65.0 [49.0, 86.0]", "54.0 [4.00, 95.0]"]]
cols = [("Specific","Alive " + dagger, ""),
("Specific","Death", "Melanoma death"),
("Specific","Death", "Non-melanoma death"),
("","Overall", "")]
rows = [("Section 1", "Gender", "Male"), ("Section 1", "Gender", "Female"),
("Section 1", "Age", "Mean (SD)"), ("Section 1", "Age", "Median [min max]"),
("Section 2", "Gender", "Male"), ("Section 2", "Gender", "Female"),
("Section 2", "Age", "Mean (SD)"), ("Section 2", "Age", "Median [min max]")]
df = pd.DataFrame(nums, columns=pd.MultiIndex.from_tuples(cols), index=pd.MultiIndex.from_tuples(rows))
# notice that now strat numbers must make reference to all the hierarchy of multi-indices
strat_numbers = {("Specific","Alive " + dagger, ""):"134",
("Specific", "Death", "Melanoma death"):"57",
("Specific", "Death", "Non-melanoma death"):"14",
("", "Overall", ""):"205"}
# beautify and show
caption = "Table 1: a nice report table with multiple indices and symbols"
footer = [dagger + " This is a foot note", "1. Another foot note", "2. Yet another footnote"]
CSS classes#
Every element of the table has a class, and an id. Using these you can write your own css to achieve full control over the appearance of your table. You can use the argument customcss that is a string with your css to achieve this.
For example let’s change the background color of the sections. Notice the !important in the css, so that it overrides the default white background of the element. Let’s also set the font color of column labels to blue and the N labels to red.
This advantage (or disadvantage depending on what you need) of this approach is that the style will propagate to all tables in the document. Therefore you will see this style automatically applied to all tables in this document.
You can even include this piece of css in a file to be linked to all your html files (jupyterbook allows this by putting this file in the _static folder on your book)
customcss = """
td.rowgrouplabel.level_0 {
background-color: lightgrey !important;
}
span.headerlabel {
color: blue;
}
span.headern {
color: red;
}
"""
table = pandas_to_report_html(df, strat_numbers=strat_numbers,
caption=caption, footer=footer,
customcss=customcss)
table
Specific | |||||
---|---|---|---|---|---|
Alive † | Death | Overall | |||
(N=134) |
Melanoma death
(N=57) |
Non-melanoma death
(N=14) |
(N=205) |
||
Section 1 | |||||
Gender | |||||
Male | 91 (67.9%) 1 | 28 (49.1%) | 7 (50.0%) | 126 (61.5%) | |
Female | 43 (32.1%) | 29 (50.9%) | 7 (50.0%) | 79 (38.5%) | |
Age | |||||
Mean (SD) | 50.0 (15.9) | 55.1 (17.9) | 65.3 (10.9) | 52.5 (16.7) | |
Median [min max] | 52.0 [4.00, 84.0] 2 | 56.0 [14.0, 95.0] | 65.0 [49.0, 86.0] | 54.0 [4.00, 95.0] | |
Section 2 | |||||
Gender | |||||
Male | 91 (67.9%) | 28 (49.1%) | 7 (50.0%) | 126 (61.5%) | |
Female | 43 (32.1%) | 29 (50.9%) | 7 (50.0%) | 79 (38.5%) | |
Age | |||||
Mean (SD) | 50.0 (15.9) | 55.1 (17.9) | 65.3 (10.9) | 52.5 (16.7) | |
Median [min max] | 52.0 [4.00, 84.0] | 56.0 [14.0, 95.0] | 65.0 [49.0, 86.0] | 54.0 [4.00, 95.0] | |
† This is a foot note 1. Another foot note 2. Yet another footnote |
CSS classes map#
Here you have a map of the classes you can use in your css. The table itself has the class Pytable1. You can set all classes as children of this to achieve more specificity. Observe that multi-index column and row labels have a level second class starting with 0. Observe also that column headers are in the class th.header which contains two separate spans, one for the column name and one for the column N, so that you can control them togheter (with th.header) separately (with span.headerlabel and span.headern)
th.superheader.level_0 | th.superheader.level_0 | th.superheader.level_0 | |||
---|---|---|---|---|---|
th.superheader.level_1 | th.superheader.level_1 | th.superheader.level_1 | th.superheader.level_1 | ||
th.header
span.headerlabel |
th.header
span.headerlabel span.headern |
th.header
span.headerlabel span.headern |
th.header
span.headerlabel span.headern |
th.header
span.headerlabel span.headern |
|
td.rowgrouplabel.level_0 | |||||
td.rowgrouplabel.level_1 | |||||
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowgrouplabel.level_1 | |||||
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowgrouplabel.level_0 | |||||
td.rowgrouplabel.level_1 | |||||
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowgrouplabel.level_1 | |||||
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.rowlabel | td.rowvalue | td.rowvalue | td.rowvalue | td.rowvalue | |
td.footnote |
CSS ids#
As mentioned before, each element has an unique id. You can target these to gain control over every single element in the table when writing your css. Ids are a combination of the table id (random has), the classes, the levels and a number ordering them. For values, we number them by row and then column
In this circupstances, you can pass the argument table_id so that the id is known to you instead of it being a random hash.
With this approach changes will apply only to the particular table you are trying to manipulate and not to other tables in the document.
As an example, let’s change the background color of “Dead”, something you could not achieve using classes alone.
table_id = "mytable1"
customcss = f"""
#{table_id}_superheader_level_1_2 {{
background-color: lightgrey !important;
}}
"""
table = pandas_to_report_html(df, strat_numbers=strat_numbers,
caption=caption, footer=footer,
customcss=customcss,
table_id=table_id)
table
Specific | |||||
---|---|---|---|---|---|
Alive † | Death | Overall | |||
(N=134) |
Melanoma death
(N=57) |
Non-melanoma death
(N=14) |
(N=205) |
||
Section 1 | |||||
Gender | |||||
Male | 91 (67.9%) 1 | 28 (49.1%) | 7 (50.0%) | 126 (61.5%) | |
Female | 43 (32.1%) | 29 (50.9%) | 7 (50.0%) | 79 (38.5%) | |
Age | |||||
Mean (SD) | 50.0 (15.9) | 55.1 (17.9) | 65.3 (10.9) | 52.5 (16.7) | |
Median [min max] | 52.0 [4.00, 84.0] 2 | 56.0 [14.0, 95.0] | 65.0 [49.0, 86.0] | 54.0 [4.00, 95.0] | |
Section 2 | |||||
Gender | |||||
Male | 91 (67.9%) | 28 (49.1%) | 7 (50.0%) | 126 (61.5%) | |
Female | 43 (32.1%) | 29 (50.9%) | 7 (50.0%) | 79 (38.5%) | |
Age | |||||
Mean (SD) | 50.0 (15.9) | 55.1 (17.9) | 65.3 (10.9) | 52.5 (16.7) | |
Median [min max] | 52.0 [4.00, 84.0] | 56.0 [14.0, 95.0] | 65.0 [49.0, 86.0] | 54.0 [4.00, 95.0] | |
† This is a foot note 1. Another foot note 2. Yet another footnote |
CSS Ids map#
Here you have a map of the ids you can use in your css. The table itself has the as id either a random hash. or the id you set with table_id. We will call it id here. For all elements the id is the concatenation of the table id, with the class with the order of appearance.
id_superheader_level_0_0 | id_superheader_level_0_1 | id_superheader_level_0_2 | |||
---|---|---|---|---|---|
id_superheader_level_1_0 | id_superheader_level_1_1 | id_superheader_level_1_2 | id_superheader_level_1_3 | ||
id_header_0
id_headerlabel_0 |
id_header_1
id_headerlabel_1 id_headern_1 |
id_header_2
id_headerlabel_2 id_headern_2 |
id_header_3
id_headerlabel_3 id_headern_3 |
id_header_4
id_headerlabel_4 id_headern_4 |
|
id_rowgrouplabel_level_0_0 | |||||
id_rowgrouplabel_level_1_0 | |||||
id_rowlabel_0 | id_rowvalue_row_0_col_0 | id_rowvalue_row_0_col_1 | id_rowvalue_row_0_col_2 | id_rowvalue_row_0_col_3 | |
id_rowlabel_1 | id_rowvalue_row_1_col_0 | id_rowvalue_row_1_col_1 | id_rowvalue_row_1_col_2 | id_rowvalue_row_1_col_3 | |
id_rowgrouplabel_level_1_1 | |||||
id_rowlabel_3 | id_rowvalue_row_2_col_0 | id_rowvalue_row_2_col_1 | id_rowvalue_row_2_col_2 | id_rowvalue_row_2_col_3 | |
id_rowlabel_3 | id_rowvalue_row_3_col_0 | id_rowvalue_row_3_col_1 | id_rowvalue_row_3_col_2 | id_rowvalue_row_3_col_3 | |
id_rowgrouplabel_level_0_1 | |||||
id_rowgrouplabel_level_1_2 | |||||
id_rowlabel_4 | id_rowvalue_row_4_col_0 | id_rowvalue_row_4_col_1 | id_rowvalue_row_4_col_2 | id_rowvalue_row_4_col_3 | |
id_rowlabel_5 | id_rowvalue_row_5_col_0 | id_rowvalue_row_5_col_1 | id_rowvalue_row_5_col_2 | id_rowvalue_row_5_col_3 | |
id_rowgrouplabel_level_1_3 | |||||
id_rowlabel_6 | id_rowvalue_row_6_col_0 | id_rowvalue_row_6_col_1 | id_rowvalue_row_6_col_2 | id_rowvalue_row_6_col_3 | |
id_rowlabel_7 | id_rowvalue_row_7_col_0 | id_rowvalue_row_7_col_1 | id_rowvalue_row_7_col_2 | id_rowvalue_row_7_col_3 | |
id_footnote |