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
Loop over both parallel lists at once by index. For each (region, status) pair, create the inner dict when a region is first seen, initialize the count to zero when a status is first seen for that region, then increment.
naive.py
Replay: real traced execution (multi-file project)
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)
region ← ['N', 'S', 'N', 'N', 'S', 'S']
1region = ['N', 'S', 'N', 'N', 'S', 'S']2status = ['A', 'B', 'B', 'A', 'A', 'B']values this step['N', 'S', 'N', 'N', 'S', 'S']regionstatus ← ['A', 'B', 'B', 'A', 'A', 'B']
1region = ['N', 'S', 'N', 'N', 'S', 'S']2status = ['A', 'B', 'B', 'A', 'A', 'B']3counts = {}values this step['A', 'B', 'B', 'A', 'A', 'B']statuscounts ← {}
2status = ['A', 'B', 'B', 'A', 'A', 'B']3counts = {}4for i in range(len(region)):values this step{}countsi ← 0
3counts = {}4for i in range(len(region)):5 r = region[i]values this step0ir ← 'N'
4for i in range(len(region)):5 r = region[i]6 s = status[i]values this step'N'rs ← 'A'
5r = region[i]6s = status[i]7if r not in counts:values this step'A'sif r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}counts ← {'N': {}}
7if r not in counts:8 counts[r] = {}9if s not in counts[r]:values this step{} → {'N': {}}countsif s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 0}}
9if s not in counts[r]:10 counts[r][s] = 011counts[r][s] += 1values this step{'N': {}} → {'N': {'A': 0}}countscounts ← {'N': {'A': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 0}} → {'N': {'A': 1}}countsi ← 1
3counts = {}4for i in range(len(region)):5 r = region[i]values this step0 → 1ir ← 'S'
4for i in range(len(region)):5 r = region[i]6 s = status[i]values this step'N' → 'S'rs ← 'B'
5r = region[i]6s = status[i]7if r not in counts:values this step'A' → 'B'sif r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}counts ← {'N': {'A': 1}, 'S': {}}
7if r not in counts:8 counts[r] = {}9if s not in counts[r]:values this step{'N': {'A': 1}} → {'N': {'A': 1}, 'S': {}}countsif s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 1}, 'S': {'B': 0}}
9if s not in counts[r]:10 counts[r][s] = 011counts[r][s] += 1values this step{'N': {'A': 1}, 'S': {}} → {'N': {'A': 1}, 'S': {'B': 0}}countscounts ← {'N': {'A': 1}, 'S': {'B': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 1}, 'S': {'B': 0}} → {'N': {'A': 1}, 'S': {'B': 1}}countsi ← 2
3counts = {}4for i in range(len(region)):5 r = region[i]values this step1 → 2ir ← 'N'
4for i in range(len(region)):5 r = region[i]6 s = status[i]values this step'S' → 'N'rs = status[i]
5r = region[i]6s = status[i]7if r not in counts:if r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}if s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 1, 'B': 0}, 'S': {'B': 1}}
9if s not in counts[r]:10 counts[r][s] = 011counts[r][s] += 1values this step{'N': {'A': 1}, 'S': {'B': 1}} → {'N': {'A': 1, 'B': 0}, 'S': {'B': 1}}countscounts ← {'N': {'A': 1, 'B': 1}, 'S': {'B': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 1, 'B': 0}, 'S': {'B': 1}} → {'N': {'A': 1, 'B': 1}, 'S': {'B': 1}}countsi ← 3
3counts = {}4for i in range(len(region)):5 r = region[i]values this step2 → 3ir = region[i]
4for i in range(len(region)):5 r = region[i]6 s = status[i]s ← 'A'
5r = region[i]6s = status[i]7if r not in counts:values this step'B' → 'A'sif r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}if s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 2, 'B': 1}, 'S': {'B': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 1, 'B': 1}, 'S': {'B': 1}} → {'N': {'A': 2, 'B': 1}, 'S': {'B': 1}}countsi ← 4
3counts = {}4for i in range(len(region)):5 r = region[i]values this step3 → 4ir ← 'S'
4for i in range(len(region)):5 r = region[i]6 s = status[i]values this step'N' → 'S'rs = status[i]
5r = region[i]6s = status[i]7if r not in counts:if r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}if s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 0}}
9if s not in counts[r]:10 counts[r][s] = 011counts[r][s] += 1values this step{'N': {'A': 2, 'B': 1}, 'S': {'B': 1}} → {'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 0}}countscounts ← {'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 0}} → {'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 1}}countsi ← 5
3counts = {}4for i in range(len(region)):5 r = region[i]values this step4 → 5ir = region[i]
4for i in range(len(region)):5 r = region[i]6 s = status[i]s ← 'B'
5r = region[i]6s = status[i]7if r not in counts:values this step'A' → 'B'sif r not in counts:
6s = status[i]7if r not in counts:8 counts[r] = {}if s not in counts[r]:
8 counts[r] = {}9if s not in counts[r]:10 counts[r][s] = 0counts ← {'N': {'A': 2, 'B': 1}, 'S': {'B': 2, 'A': 1}}
10 counts[r][s] = 011 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}values this step{'N': {'A': 2, 'B': 1}, 'S': {'B': 1, 'A': 1}} → {'N': {'A': 2, 'B': 1}, 'S': {'B': 2, 'A': 1}}countsfor i in range(len(region)):
3counts = {}4for i in range(len(region)):5 r = region[i]result ← {'N': {'A': 2, 'B': 1}, 'S': {'A': 1, 'B': 2}}
11 counts[r][s] += 112result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}13print('RESULT:', result)values this step{'N': {'A': 2, 'B': 1}, 'S': {'A': 1, 'B': 2}}resultstdout ← RESULT: {'N': {'A': 2, 'B': 1}, 'S': {'A': 1, 'B': 2}}
12result = {k: dict(sorted(counts[k].items())) for k in sorted(counts)}13print('RESULT:', result)values this stepRESULT: {'N': {'A': 2, 'B': 1}, 'S': {'A': 1, 'B': 2}}stdout
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.
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.