Styling Tables

Styling DataFrames to make them more readable and skannable with heatmaps, bar charts, categorical colors, and plain text.

source

style_table

 style_table (df, column_types, column_widths=None, title=None,
              precision=1, width=None, height=None, theme='plotly_white',
              font_size=None, title_font_size=None)

Convert a DataFrame to multiple charts (one for each column).

Type Default Details
df pandas.DataFrame Any DataFrame, but typically a small one, to display as a report.
column_types list A list of types, one for each column. Possible values are “bar”, “heatmap”, “category”, and “text”.
column_widths NoneType None A list of fractions that should add up to 1. Each fraction corresponds to a column.
title NoneType None The title of the chart.
precision int 1 How many decimals of precision to display in numeric columns. This affects all numeric columns.
width NoneType None The width of the entire chart in pixels.
height NoneType None The height of the entire chart in pixels.
theme str plotly_white The theme used for styling the entire chart.
font_size NoneType None The size of font of text and number on the chart in points.
title_font_size NoneType None The size of font of the title of the chart in points.

Getting started - a simple table

df = pd.DataFrame({
    'text': ['one', 'two', 'three', 'four', 'five'],
    'category': ['odd', 'even', 'odd', 'even', 'odd'],
    'heatmap': [10, 20, 30, 40, 70],
    'bar': [18, 17, 12, 10, 5]
})

df
text category heatmap bar
0 one odd 10 18
1 two even 20 17
2 three odd 30 12
3 four even 40 10
4 five odd 70 5
style_table(
    df,
    column_types=['text', 'category', 'heatmap', 'bar'])

Modify look and feel

style_table(
    df,
    column_types=['text', 'category', 'heatmap', 'bar'],
    column_widths=[0.15, 0.25, 0.2, 0.4],
    title='Styling tables with <b>adviz',
    theme='plotly_dark',
    precision=0,
    width=600,
    height=400,
    font_size=14,
    title_font_size=25)

Styling multiple columns with multiple chart types

First set the categorical order so we can sort medals by rank: bronze < silver < gold.

medals = px.data.medals_long()[['medal', 'nation', 'count']]
medals['medal'] = (medals['medal']
                   .astype('category')
                   .cat.reorder_categories(['bronze', 'silver', 'gold']))
medals = medals.sort_values(['medal', 'count'], ascending=False)
medals
medal nation count
0 gold South Korea 24
1 gold China 10
2 gold Canada 9
4 silver China 15
3 silver South Korea 13
5 silver Canada 12
8 bronze Canada 12
6 bronze South Korea 11
7 bronze China 8

Since each column contains a different type of data, we want to set the types of chart we want ot use to visualize them:

  • medal: category
  • nation: category
  • count: bar

Note that when you have two categorical columns, different color scales are used to avoid confusion.

style_table(
    medals,
    column_types=['category', 'category', 'bar'],
    title='Olympic Medals',
    theme='none',
    precision=0,
    width=800,
    height=500)

You can also use “heatmap” for numeric columns, and it depends on how you want to display it.

style_table(
    medals,
    column_types=['category', 'category', 'heatmap'],
    title='Olympic Medals',
    theme='none',
    precision=0,
    width=800,
    height=400)

Styling many columns with multiple chart types

election = px.data.election().iloc[:, :-1]
election.head()
district Coderre Bergeron Joly total winner result
0 101-Bois-de-Liesse 2481 1829 3024 7334 Joly plurality
1 102-Cap-Saint-Jacques 2525 1163 2675 6363 Joly plurality
2 11-Sault-au-Récollet 3348 2770 2532 8650 Coderre plurality
3 111-Mile-End 1734 4782 2514 9030 Bergeron majority
4 112-DeLorimier 1770 5933 3044 10747 Bergeron majority

Using column_widths we can set the relative widths of each chart as appropriate. This can be set as any fraction between [0, 1]. The columns that contain a lot of data (like text for example) can be given a larger share of the chart, and you can interactively modify the widths using a list.

style_table(
    election.sort_values('total', ascending=False).head(20),
    column_types=['text', 'heatmap', 'heatmap', 'heatmap', 'bar', 'category', 'category'],
    column_widths=[.3, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12 ],
    precision=0,
    theme='plotly_dark',
    title='Election Results – Top 20',
    height=750,
)

Producing more detailed and readable tables

With large tables and many values, it’s good to filter and display the subset that you want, as we do here with the gapminder dataset. We first sort values by year and gdpPercap, and then get the top five countries for each year. Then we rename the columns to give them a nicer look.

top5_gdp_percap = (px.data
                   .gapminder()
                   .sort_values(['year', 'gdpPercap'],
                                ascending=[True, False])
                   .groupby('year').head().iloc[:, :-2]
                   [['country', 'continent', 'year', 'gdpPercap', 'lifeExp', 'pop']]
                   .rename(columns={
                       'gdpPercap': 'GDP per capita',
                       'lifeExp': 'Life expectancy',
                       'pop': 'population'}))
top5_gdp_percap.head()
country continent year GDP per capita Life expectancy population
852 Kuwait Asia 1952 108382.35290 55.565 160000
1476 Switzerland Europe 1952 14734.23275 69.620 4815000
1608 United States Americas 1952 13990.48208 68.440 157553000
240 Canada Americas 1952 11367.16112 68.750 14785584
1092 New Zealand Oceania 1952 10556.57566 69.390 1994794
top5_gdp_percap
country continent year GDP per capita Life expectancy population
852 Kuwait Asia 1952 108382.35290 55.565 160000
1476 Switzerland Europe 1952 14734.23275 69.620 4815000
1608 United States Americas 1952 13990.48208 68.440 157553000
240 Canada Americas 1952 11367.16112 68.750 14785584
1092 New Zealand Oceania 1952 10556.57566 69.390 1994794
853 Kuwait Asia 1957 113523.13290 58.033 212846
1477 Switzerland Europe 1957 17909.48973 70.560 5126000
1609 United States Americas 1957 14847.12712 69.490 171984000
241 Canada Americas 1957 12489.95006 69.960 17010154
1093 New Zealand Oceania 1957 12247.39532 70.260 2229407
854 Kuwait Asia 1962 95458.11176 60.470 358266
1478 Switzerland Europe 1962 20431.09270 71.320 5666000
1610 United States Americas 1962 16173.14586 70.210 186538000
410 Denmark Europe 1962 13583.31351 72.350 4646899
242 Canada Americas 1962 13462.48555 71.300 18985849
855 Kuwait Asia 1967 80894.88326 64.624 575003
1479 Switzerland Europe 1967 22966.14432 72.770 6063000
1611 United States Americas 1967 19530.36557 70.760 198712000
903 Libya Africa 1967 18772.75169 50.227 1759224
1311 Saudi Arabia Asia 1967 16903.04886 49.901 5618198
856 Kuwait Asia 1972 109347.86700 67.712 841934
1480 Switzerland Europe 1972 27195.11304 73.780 6401400
1312 Saudi Arabia Asia 1972 24837.42865 53.886 6472756
1612 United States Americas 1972 21806.03594 71.340 209896000
904 Libya Africa 1972 21011.49721 52.773 2183877
857 Kuwait Asia 1977 59265.47714 69.343 1140357
1313 Saudi Arabia Asia 1977 34167.76260 58.690 8128505
1481 Switzerland Europe 1977 26982.29052 75.390 6316424
1613 United States Americas 1977 24072.63213 73.380 220239000
1145 Norway Europe 1977 23311.34939 75.370 4043205
1314 Saudi Arabia Asia 1982 33693.17525 63.012 11254672
858 Kuwait Asia 1982 31354.03573 71.309 1497494
1482 Switzerland Europe 1982 28397.71512 76.210 6468126
1146 Norway Europe 1982 26298.63531 75.970 4114787
1614 United States Americas 1982 25009.55914 74.650 232187835
1147 Norway Europe 1987 31540.97480 75.890 4186147
1483 Switzerland Europe 1987 30281.70459 77.410 6649942
1615 United States Americas 1987 29884.35041 75.020 242803533
859 Kuwait Asia 1987 28118.42998 74.174 1891487
691 Iceland Europe 1987 26923.20628 77.230 244676
860 Kuwait Asia 1992 34932.91959 75.190 1418095
1148 Norway Europe 1992 33965.66115 77.320 4286357
1616 United States Americas 1992 32003.93224 76.090 256894189
1484 Switzerland Europe 1992 31871.53030 78.030 6995447
80 Austria Europe 1992 27042.01868 76.040 7914969
1149 Norway Europe 1997 41283.16433 78.320 4405672
861 Kuwait Asia 1997 40300.61996 76.156 1765345
1617 United States Americas 1997 35767.43303 76.810 272911760
1365 Singapore Asia 1997 33519.47660 77.158 3802309
1485 Switzerland Europe 1997 32135.32301 79.370 7193761
1150 Norway Europe 2002 44683.97525 79.050 4535591
1618 United States Americas 2002 39097.09955 77.310 287675526
1366 Singapore Asia 2002 36023.10540 78.770 4197776
862 Kuwait Asia 2002 35110.10566 76.904 2111561
1486 Switzerland Europe 2002 34480.95771 80.620 7361757
1151 Norway Europe 2007 49357.19017 80.196 4627926
863 Kuwait Asia 2007 47306.98978 77.588 2505559
1367 Singapore Asia 2007 47143.17964 79.972 4553009
1619 United States Americas 2007 42951.65309 78.242 301139947
755 Ireland Europe 2007 40675.99635 78.885 4109086
style_table(
    top5_gdp_percap,
    ['text', 'category', 'category', 'bar', 'heatmap', 'heatmap'],
    column_widths=[.1, .07, .05, .12, .12, .12, ],
    theme='seaborn',
    precision=0,
    title="Top 5 countries per year – GDP per capita",
    height=1300)

Visualizing hierarchicacal data

See also: the adviz.status_codes chart.

status_codes = pd.read_csv('data/status_codes.csv')
status_codes['category'] = status_codes['status'].round(-2)
status_codes
status category
0 200 200
1 200 200
2 200 200
3 200 200
4 200 200
... ... ...
414398 200 200
414399 200 200
414400 200 200
414401 200 200
414402 200 200

414403 rows × 2 columns

style_table(
    status_codes[['category', 'status']].value_counts().reset_index().sort_values(['category', 'count'], ascending=[True, False]),
    column_types=['category', 'text', 'bar'],
    column_widths=[.2, .2, .6],
    precision=0,
    theme='none',
    title='Status Codes',
    height=600, width=700)