Embedding Tables

Directive: items-table – embed sequences and mappings as tables

Added value includes powerful facilities for creating tabular presentations of single- and multi-dimensional data structures, such as lists of lists and lists of dictionaries. We’ll start with a few basic examples of the items-table:: directive, before explaining all various configuration options.

Basic Examples

Table from a list

Python sequences, such as a lists, can be represented as tables. Consider the following Python data structure, which is a list of strings containing the names of the days of the week:

days_of_the_week = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

This is how we would refer to that table using an items-table:: directive:

.. items-table:: cal.days_of_the_week

which will be rendered like this:

0 Monday
1 Tuesday
2 Wednesday
3 Thursday
4 Friday
5 Saturday
6 Sunday

Notice that the list indexes are included in the table data.

Table from a dictionary

Python mappings, such as a dictionaries, can be represented as a tables. Consider the following Python data structure, which is a dictionary mapping the names of the months as strings, to the number of days in those months as an integer:

month_lengths = {
    "January": 31,
    "February": 28,
    "March": 31,
    "April": 30,
    "May": 31,
    "June": 30,
    "July": 31,
    "August": 31,
    "September": 30,
    "October": 31,
    "November": 31,
    "December": 31,
}

This is how we would refer to that table using an items-table:: directive:

.. items-table:: cal.month_lengths

which will be rendered like this:

January 31
February 28
March 31
April 30
May 31
June 30
July 31
August 31
September 30
October 31
November 31
December 31

Notice that the dictionary keys are included in the table data.

Table from list of lists

Arbitrarily nested combinations of sequences and mappings (e.g. lists and dictionaries).

elements = {
    "H": {
        "name": "Hydrogen",
        "weight": 1.008,
        "number": 1,
        "boiling point": 20.271,

    },
    "He": {
        "name": "Helium",
        "weight": 4.003,
        "number": 2,
        "boiling point": 4.222,
    },
    "Li": {
        "name": "Lithium",
        "weight": 6.94,
        "number": 3,
        "boiling point": 1603.0,
    },
    "Be": {
        "name": "Beryllium",
        "weight": 9.012,
        "number": 4,
        "boiling point": 2742.0,
    },
}

This is how we would refer to that table using an items-table:: directive:

.. items-table:: elements.elements

which will be rendered like this:

  name weight number boiling point
H Hydrogen 1.008 1 20.271
He Helium 4.003 2 4.222
Li Lithium 6.94 3 1603.0
Be Beryllium 9.012 4 2742.0

Projecting multi-level data structures into tables

Data structures such a list-of-lists or list-of-dictionaries can be considered as being multi-level. For example, a list of integers is a single-level data structure, a list of list of integers is a two-level data structure, and a list of lists of lists is a three- level data structure. Each of these levels can be given a zero-based indexed, called the level index. So in the case of a list of lists of dictionaries, the outer list has indexes at level zero, the inner lists have indexes at level one, and the dictionaries contained within those inner lists have indexes at level two.

One easy way to see this is to think about how many successive applications of Python’s indexing operator are required to access an item.

                                  ┌────────── level 0 (outer list index)
                                  │   ┌────── level 1 (inner list index)
                                  │   │    ┌─ level 2 (dictionary key)
                                  │   │    │
item = list_of_lists_dictionaries[12][32]["red"]

Tables can be no more than two dimensional, so when projecting multi-level data structures into a table some decisions have to be be made. By default, even levels are projected to the vertical axis of the table, and odd levels are projected to the horizontal axis of the table. This works well for the common cases of single level and double-level data structures. For more than two levels, it will likely to necessary to exercise greater control over which levels are projected onto which table axes using options to the items-table:: directive.

Options to control items-table

The items-table:: directive support the following options:

header-rows:The number of leading rows to be formatted as the header.
stub-columns:The number of leading columns to be formatted as header.
header:A comma-separated list of strings to be used as a header row.
widths:A comma-separated list of column widths
title:A title for the table.
v-level-indexes:
 A comma-separated list of level indexes for the vertical axis of the table (row indexes). Defaults to even level indexes. Must be disjoint with h-level-indexes.
h-level-indexes:
 A comma-separated list of level indexes for the horizontal axis of the table (column indexes). Defaults to odd level indexes. Must be disjoint with v-level-indexes.
v-level-visibility:
 A comma-separated list of show or hide values controlling index visibility for the vertical axis of the table. Useful for hiding unwanted sequence indexes.
h-level-visibility:
 A comma-separated list of show or hide values controlling index visibility for the horizontal axis of the table. Useful for hiding unwanted sequence indexes.
h-level-titles:A comma-separated list of names to be associated with each level for the horizontal axis of the table (column indexes).
v-level-titles:A comma-separated list of names to be associated with each level for the vertical axis of the table (column indexes).
v-level-sort-orders:
 A comma-separated list of asc, dec or as-is values, controlling whether the corresponding indexes are sorted in ascending natural order, descending natural order, or taken as-is.
h-level-sort-orders:
 A comma-separated list of asc, dec or as-is values, controlling whether the corresponding indexes are sorted in ascending natural order, descending natural order, or taken as-is.
cell-formats:TODO

Examples

Specify the number of header rows

Use the :header-rows: option to specify the number of leading rows of the table which are to be considered as column headers:

.. items-table:: elements.elements
   :header-rows: 1

which will be rendered like this:

  name weight number boiling point
H Hydrogen 1.008 1 20.271
He Helium 4.003 2 4.222
Li Lithium 6.94 3 1603.0
Be Beryllium 9.012 4 2742.0

The exact style of header rows depends on the theme in use.

Specify the number of stub columns

Use the :stub-columms: option to specify the number of leading columns of the table which are to be considered as row headers:

.. items-table:: elements.elements
   :stub-columns: 1

which will be rendered like this:

  name weight number boiling point
H Hydrogen 1.008 1 20.271
He Helium 4.003 2 4.222
Li Lithium 6.94 3 1603.0
Be Beryllium 9.012 4 2742.0

The exact style of stub columns depends on the theme in use.

Control the assignment of level indexes to tables axes

By default even-numbered levels are projected on to the vertical tables axis, and odd-numbered levels projected to horizontal table axis. To gain more control over which levels of the data structure are projected onto which table axis, use the :v-level-indexes and :h-level-indexes options to the items-table:: directive.

Consider the following three-level economic data stored in a dictionary of dictionaries of dictionaries:

economic_data = {
    "United States": {
        "GDP %": {
            "latest": 3.0,
            "quarter": 3.5,
            "2018": 2.9,
        },
        "Consumer prices %": {
            "latest": 2.3,
            "2018": 2.5,
        },
        "Unemployment rate %": {
            "latest": 3.7,
        },
    },
    "China": {
        "GDP %": {
            "latest": 6.5,
            "quarter": 6.6,
            "2018": 6.6,
        },
        "Consumer prices %": {
            "latest": 2.5,
            "2018": 2.1,
        },
        "Unemployment rate %": {
            "latest": 3.8,
        },
    },
    "Japan": {
        "GDP %": {
            "latest": 1.3,
            "quarter": 3.0,
            "2018": 1.0,
        },
        "Consumer prices %": {
            "latest": 1.2,
            "2018": 0.9,
        },
        "Unemployment rate %": {
            "latest": 2.3,
        },
    },
    "Britain": {
        "GDP %": {
            "latest": 1.2,
            "quarter": 1.6,
            "2018": 1.3,
        },
        "Consumer prices %": {
            "latest": 2.4,
            "2018": 2.4,
        },
        "Unemployment rate %": {
            "latest": 4.0,
        },
    },
}

usa_economic_data = {country: data for country, data in economic_data.items() if country == "United States"}

The default view is equivalent to this, with even-numbered :v-level indexes:, and odd-numbered :h-level-indexes::

.. items-table:: economy.economic_data
   :v-level-indexes: 0, 2
   :h-level-indexes: 1

which will be rendered like this:

    GDP % Consumer prices % Unemployment rate %
United States latest 3.0 2.3 3.7
United States quarter 3.5    
United States 2018 2.9 2.5  
China latest 6.5 2.5 3.8
China quarter 6.6    
China 2018 6.6 2.1  
Japan latest 1.3 1.2 2.3
Japan quarter 3.0    
Japan 2018 1.0 0.9  
Britain latest 1.2 2.4 4.0
Britain quarter 1.6    
Britain 2018 1.3 2.4  

Note

Tables cells for which there is no corresponding data will be left empty.

A more useful presentation would be to put only the country (level zero) on the vertical table axis, and both catgeory and timing information on the horizontal axis:

.. items-table:: economy.economic_data
   :v-level-indexes: 0
   :h-level-indexes: 1, 2

which will be rendered like this:

  GDP % GDP % GDP % Consumer prices % Consumer prices % Unemployment rate %
  latest quarter 2018 latest 2018 latest
United States 3.0 3.5 2.9 2.3 2.5 3.7
China 6.5 6.6 6.6 2.5 2.1 3.8
Japan 1.3 3.0 1.0 1.2 0.9 2.3
Britain 1.2 1.6 1.3 2.4 2.4 4.0

Hide a level of indexes

Particularly when dealing with sequences (e.g. lists) of data, we don’t wish to see the zero-based indexes. Consider this list of lists containing a calendar of days in October 2018:

october_2018 = [
    [1, 2, 3, 4, 5, 6, 7],
    [8, 9, 10, 11, 12, 13, 14],
    [15, 16, 17, 18, 19, 20, 21],
    [22, 23, 24, 25, 26, 27, 28],
    [29, 30, 31, 1, 2, 3, 4],
]

We can display it as a table using this:

.. items-table:: cal.october_2018

which will be rendered like this:

  0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
1 8 9 10 11 12 13 14
2 15 16 17 18 19 20 21
3 22 23 24 25 26 27 28
4 29 30 31 1 2 3 4

The zero-based indexes used as row and column headers are unnecessary, and can be disabled using the :v-level-visibility: and :h-level-visibility: options. Each of these accepts a comma-separated list of show or hide values:

.. items-table:: cal.october_2018
   :v-level-visibility: hide
   :h-level-visibility: hide

which now looks like this:

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 1 2 3 4

Adding a header row

If a suitable header row is not incorporated into the data structure, you can add it using :header: option, which accepts a comma-separated items. Each item can optionally be quoted, which can be useful if it needs to contain a comma.

.. items-table:: cal.october_2018
   :header: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
   :v-level-visibility: hide
   :h-level-visibility: hide

which renders like this:

Monday Tuesday Wednesday Thursday Friday Saturday Sunday
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 1 2 3 4

Adding a title

A title can be added using the :title: option:

.. items-table:: cal.october_2018
   :title: October 2018
   :header: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
   :v-level-visibility: hide
   :h-level-visibility: hide

which renders like this:

October 2018
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 1 2 3 4