Note on subtotal and Categorical MultiIndex
mastersubtotal function, sidetable converts a Categorical MultiIndex into a plain index. This is done to facilitate the insertion of subtotal labels into the resulting table.repository·master·Indexed 19 days ago
https://github.com/chris1610/sidetableA library that extends pandas DataFrames with a `.stb` accessor to simplify the creation of summary tables. It provides tools for building frequency tables via `.stb.freq()`, analyzing missing values with `.stb.missing()`, adding subtotals and grand totals using `.stb.subtotal()`, flattening MultiIndex columns with `.stb.flatten()`, and formatting numeric results with `.stb.pretty()`. Requires pandas 1.0 or higher.
subtotal function, sidetable converts a Categorical MultiIndex into a plain index. This is done to facilitate the insertion of subtotal labels into the resulting table.sidetable supports grouping on any data type. However, if you attempt to run .stb.freq() on a column with a large number of unique values (high cardinality), the resulting table may be too large to be useful.
To resolve this, you should bin the continuous data into discrete categories using pandas methods like pd.qcut or pd.cut before calling sidetable functions.
# Instead of this (if 'fare' has many unique values):
# df.stb.freq(['fare'])
# Do this:
df['fare_bin'] = pd.qcut(df['fare'], q=4, labels=['low', 'medium', 'high', 'x-high'])
df.stb.freq(['fare_bin'])sidetable uses the pandas DataFrame accessor API to add a .stb accessor to all your DataFrames. Once you import sidetable, the .stb property becomes available on any pandas DataFrame instance, allowing you to call sidetable methods directly on your data.
import pandas as pd
import sidetable
# Now df.stb is available
df = pd.DataFrame({'a': [1, 2, 3]})sidetable extends pandas DataFrames by adding a new .stb accessor. Once you import sidetable, all pandas DataFrame objects will have access to the .stb namespace for performing summary tasks like frequency tables, missing value analysis, and subtotals.
import sidetable
import pandas as pd
df = pd.read_csv('myfile.csv')
# Access sidetable features via the .stb accessor
df.stb.freq(['column1'])By default, null or missing values may cause data to drop out during aggregation in sidetable. This means the cumulative_count in your frequency table might not match the total number of rows in your original DataFrame.
To include missing values in your frequency tables, you should handle them explicitly in your DataFrame (e.g., using .fillna()) before calling sidetable methods. If working with Categorical data, you must first add the new category to the column's categories before filling.
# For standard columns:
# df['col'] = df['col'].fillna('UNK')
# For Categorical columns:
df['deck_fillna'] = df['deck'].cat.add_categories('UNK').fillna('UNK')
df.stb.freq(['deck_fillna'])You can install sidetable using pip or conda. sidetable requires pandas 1.0 or higher and has no additional dependencies.
$ python -m pip install -U sidetableOr using conda:
$ conda install -c conda-forge sidetableWhen working with complex grouping or pivoting (like unstack()), you often end up with a MultiIndex. flatten() converts this into a simple, flat representation.
Options:
sep: Define a custom string to use as a separator in the new column names (e.g., sep='|').reset: Boolean to control whether to reset the index.levels: An integer or a list of levels to reorganize the output display.# Flatten a multiindex DataFrame
complex_df = df.groupby(['embark_town', 'class', 'sex']).agg({'fare': ['sum'], 'age': ['mean']}).unstack()
complex_df.stb.flatten()
# Flatten with custom separator and specific level reorganization
complex_df.stb.flatten(sep='|', reset=False, levels=[0, 2])The freq() method creates a frequency table for one or more columns. It provides counts, percentages, and cumulative totals, which is much simpler than manually combining value_counts() results in pandas.
Key Features:
df.stb.freq(['sex', 'class'])).style=True to format percentages and large numbers for better readability.value argument to sum data from another column instead of just counting occurrences (e.g., df.stb.freq(['class'], value='fare')).thresh to group low-frequency entries into an "other" category. You can customize this label with other_label.# Basic frequency table
df.stb.freq(['class'])
# Styled frequency table
df.stb.freq(['class'], style=True)
# Grouping multiple columns
df.stb.freq(['sex', 'class'])
# Summing a specific column (e.g., fare) by group
df.stb.freq(['class'], value='fare')
# Using a threshold to group small entries into 'All others'
df.stb.freq(['class', 'who'], value='fare', thresh=80, other_label='All others')The subtotal() method allows you to add subtotal rows to a DataFrame, which is particularly useful for grouped pandas data.
Usage Patterns:
df.stb.subtotal() on a simple DataFrame adds a 'grand_total' row.sub_level=[...] to specify exactly which levels should receive a subtotal.grand_label, sub_label, show_sep, and sep to configure labels and separators.# Add a grand total to a simple DataFrame
df.stb.subtotal()
# Add subtotals at specific levels of a grouped DataFrame
summary_table = df.groupby(['sex', 'class', 'embark_town']).agg({'fare': ['sum']})
summary_table.stb.subtotal(sub_level=[1, 2])When performing frequency analysis on multiple columns, sidetable only shows combinations that actually exist in the data. If you want to see all possible combinations—including those with zero occurrences—pass the clip_0=False argument to the .freq() method.
# Shows only existing combinations
# df.stb.freq(['deck', 'class'])
# Shows all combinations, including those with 0 counts
df.stb.freq(['deck', 'class'], clip_0=False)The counts() method provides a high-level summary of each column, including the total count, number of unique values, most frequent value (and its count), and least frequent value (and its count). This is useful for understanding data structure and identifying potential categorical conversions.
You can filter which columns are included using include or exclude parameters, following the same syntax as pandas select_dtypes.
# Summary of all columns
df.stb.counts()
# Exclude numeric columns from the summary
df.stb.counts(exclude='number')The pretty() method interprets the magnitude of numeric results and returns a formatted version (e.g., converting 9975.825 to 9.98k). This is ideal for making aggregated data or large numbers easier to read in reports.
Options:
precision: Control the number of decimal places.rows: Control the number of rows displayed.caption: Add a title/caption to the resulting table.# Format aggregated sums nicely
df.groupby(['pclass', 'sex']).agg({'fare': 'sum'}).stb.pretty()
# Format as percentages with custom precision and caption
df.groupby(['pclass', 'sex']).agg({'fare': 'sum'}).div(df['fare'].sum()).stb.pretty(precision=0, caption="Fare Percentage")