Unit 02.01: The dtype carries the decision
The variable's type decides the test, and in pandas the dtype is where that decision is recorded.
Six kinds of variable, and the dtype that carries each
Numeric values support means and t-tests. Categorical values support counts, proportions and chi-square. Ordinal values have a real order and no meaningful spacing, so averaging them asserts something the measurement does not support โ pandas records this with ordered=True.
Counts are non-negative integers, often skewed; a symmetric interval around their mean can extend below zero. Rates are counts divided by exposure and are meaningless without the denominator. Binary outcomes are the special case behind proportions and logistic regression.
In pandas the dtype is not decoration. A group column left as object gets an alphabetical reference level in any model you fit.
This block builds one of each and reports its dtype.
import pandas as pd
d = pd.DataFrame({
"minutes": [34.5, 41.0, 29.2], # numeric
"depot": pd.Categorical(["north", "south", "north"]), # categorical
"traffic": pd.Categorical(["light", "heavy", "medium"],
categories=["light", "medium", "heavy"],
ordered=True), # ordinal
"parcels": pd.array([3, 11, 0], dtype="Int64"), # count
"late": [False, True, False], # binary
})
d["late_rate"] = d["parcels"].astype("float") / [12, 40, 5] # rate
for col in d.columns:
s = d[col]
ordered = getattr(s.dtype, "ordered", False)
print(f"{col:11s} dtype={str(s.dtype):12s} ordered={ordered}")
print()
print("Why the type decides the test:")
print(" binary -> proportions, chi-square, logistic regression")
print(f" count -> non-negative integers; mean {d.parcels.mean():.2f} is fine,"
" a symmetric interval round it may not be")
print(f" rate -> carries a denominator: {d.parcels[0]} parcels in 12 stops"
f" is {d.late_rate[0]:.3f},")
print(f" the same {d.parcels[0]} in 120 stops would be"
f" {d.parcels[0] / 120:.3f}")
print(" ordinal -> the order is real, the spacing is not; do not average it")
Six variables, six dtypes โ float64, category, an ordered category, the nullable Int64, bool, and a derived float rate. Only traffic reports ordered=True. The rate line makes the denominator point concrete: 3 parcels in 12 stops is 0.250, while the same 3 in 120 stops is 0.025 โ a tenfold difference from an identical numerator.
The mistake this prevents
The mistake is averaging an ordinal scale. The mean of light, medium and heavy coded 1, 2, 3 assumes the step from light to medium equals the step from medium to heavy, which nothing about the measurement guarantees.
Takeaway
Classify every variable and set its dtype deliberately. Use Categorical with an explicit order for grouping variables, mark ordinal ones ordered=True, and never report a rate without its denominator.
