NaN, None and NaT
What it is
Three different absences: NaN is a float, NaT is its datetime equivalent, None is Python's null. Pandas mostly normalises them, but their types differ.
Why it matters
NaN is not equal to anything, including itself, so df[df.col == np.nan] silently returns nothing. .isna() is the only reliable test.
In the real world: A customer with no cancellation date hasn't cancelled. Treating that blank as “missing data” rather than “still active” would wreck a churn report.
np.nan == np.nan # False — this is why == never works
df["col"].isna().sum() # the only reliable count
pd.Series([1, None]).dtype # float64: None became NaN and upcast the columnNaN being a float means one missing value upcasts an integer column to float64. Ids become 1.0, and joining them against a clean integer column matches nothing.