Reshaping
Crosstab Counts
Count co-occurrences of two categorical lists into a nested dict-of-dicts
(a contingency table). With pandas, pd.crosstab builds this frequency table
in one call, with sorted row and column labels.
By hand
With pandas
pd.crosstab(df['region'], df['status']) returns a DataFrame where each cell
is the count of rows with that (region, status) combination. Row and column
labels are sorted automatically.
naive.py
region = ['N', 'S', 'N', 'N', 'S', 'S']
status = ['A', 'B', 'B', 'A', 'A', 'B']
counts = {}
for i in range(len(region)):
r = region[i]
s = status[i]
if r not in counts:
counts[r] = {}
if s not in counts[r]:
counts[r][s] = 0
counts[r][s] += 1
result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}
print('RESULT:', result)
library.py
import pandas as pd
from dalib.display import set_display
set_display()
region = ['N', 'S', 'N', 'N', 'S', 'S']
status = ['A', 'B', 'B', 'A', 'A', 'B']
df = pd.DataFrame({'region': region, 'status': status})
ct = pd.crosstab(df['region'], df['status'])
result = {r: {c: int(ct.loc[r, c]) for c in ct.columns} for r in ct.index}
print('index:', ct.index.tolist())
print('columns:', ct.columns.tolist())
print('A:', [int(ct.loc[r, 'A']) for r in ct.index])
print('B:', [int(ct.loc[r, 'B']) for r in ct.index])
print('RESULT:', result)
index: ['N', 'S']
columns: ['A', 'B']
A: [2, 1]
B: [1, 2]
RESULT: {'N': {'A': 2, 'B': 1}, 'S': {'A': 1, 'B': 2}}
Implementation notes
pd.crosstabis a frequency-count pivot — it counts rows, not aggregates values. For value aggregation, usepivot_tablewithaggfunc='sum'etc.- Pass
normalize='index'to get row-fraction proportions (each row sums to 1),normalize='columns'for column fractions, ornormalize='all'(same asnormalize=True) to normalize over all cells. - Cross-reference:
groupby-count(ch04) for single-column frequency counts;pivot-table-simple(this chapter) for value aggregation into a grid.