2.1 Computing Summary Statistics on a Spark DataFrame
Key Takeaways
- `df.summary()` returns count, mean, stddev, min, 25%, 50%, 75%, and max as a Spark DataFrame, and accepts custom percentiles such as `df.summary("count", "5%", "95%")`.
- `df.describe()` is the narrower method: count, mean, stddev, min, and max only — it computes no percentiles, so it cannot give you a median or an IQR.
- `dbutils.data.summarize(df)` renders an interactive Databricks profile with null percentages, distinct counts, zero counts, and per-column distribution charts.
- Summary output is itself a DataFrame, so it must be displayed or collected; percentile statistics on large data are computed approximately for scalability.
- Reading the null percentage, distinct count, and min/max bounds before modelling is what surfaces sentinel values, constant columns, and impossible ranges.
2.1 Computing Summary Statistics on a Spark DataFrame
Exploratory Data Analysis (EDA) is the indispensable first phase of any production machine learning workflow on the Databricks Lakehouse. In a Lakehouse architecture, data scientists interact directly with massive raw, bronze, or silver Delta Lake tables using Apache Spark. Because Lakehouse datasets frequently scale to millions or billions of rows across distributed clusters, traditional single-node EDA tools (such as native pandas or matplotlib) cannot operate directly without memory bottlenecks or aggressive downsampling. Databricks resolves this challenge by providing distributed data profiling and interactive visualization utilities natively inside Databricks notebooks.
Rigorous EDA achieves several mission-critical objectives before a single algorithm is trained:
- Data Quality & Integrity Verification: Identifying missing values, unexpected null clusters, sentinel values (such as
-999or"N/A"), and data type mismatches. - Target Variable Diagnostics: Assessing class balance for classification problems and evaluating distribution skewness, kurtosis, and multi-modality for regression targets.
- Feature Distribution & Variance Analysis: Uncovering constant or near-zero variance features that contribute no predictive signal, as well as extreme outliers.
- Feature Interactions & Collinearity: Detecting multi-collinearity among predictor variables and uncovering non-linear relationships with the target label.
- Temporal & Spatial Partitioning Patterns: Verifying that temporal distributions are consistent across time to prevent future data leakage.
Automated Data Profiling with dbutils.data.summarize()
Databricks provides a powerful built-in utility specifically engineered for rapid tabular profiling: dbutils.data.summarize(). When executed against an Apache Spark DataFrame, Databricks leverages the distributed Spark Catalyst optimizer to compute aggregate statistics and renders an interactive, visual data profile directly in the notebook output cell.
# Load feature dataset from Unity Catalog Silver table
df = spark.table("ml_catalog.feature_store.customer_churn_features")
# Generate an automatic interactive profile
dbutils.data.summarize(df)
# Comparison with native Spark DataFrame summary methods
# df.summary() returns a Spark DataFrame containing standard statistics
summary_df = df.summary("count", "mean", "stddev", "min", "25%", "50%", "75%", "max")
display(summary_df)
Metrics Computed by dbutils.data.summarize()
The dbutils.data.summarize() command automatically categorizes columns by data type and generates distinct statistical profiles:
| Column Data Type | Computed Summary Statistics & Visualizations |
|---|---|
| Numerical Columns (Integer, Double, Float, Decimal) | Total row count, missing/null value count and percentage, number of zeros, distinct value count, mean, standard deviation, minimum, 25th percentile ($Q1$), median ($Q2$), 75th percentile ($Q3$), maximum, and an interactive distribution histogram with dynamic binning. |
| Categorical / String Columns | Total row count, missing/null count and percentage, empty string count, distinct value cardinality, and a frequency bar chart depicting the top most frequent category values along with long-tail distributions. |
| Boolean Columns | Total row count, missing/null count, true count, false count, and true/false proportion percentage bar. |
| Timestamp / Date Columns | Total row count, missing/null count, distinct count, minimum timestamp, maximum timestamp, and temporal distribution histogram across time intervals. |
dbutils.data.summarize() vs. df.describe() vs. df.summary()
Understanding the operational differences between these profiling approaches is frequently tested on the Databricks Machine Learning Associate exam:
df.describe(*cols): A standard PySpark DataFrame method that calculates basic descriptive statistics:count,mean,stddev,min, andmax. It does not calculate percentiles (such as the median or interquartile range).df.summary(*statistics): An extended PySpark DataFrame method that calculatescount,mean,stddev,min,25%,50%,75%, andmaxby default, or allows custom percentile specifications (e.g.,df.summary("count", "5%", "95%")). It returns a tabular Spark DataFrame.dbutils.data.summarize(df): A Databricks-specific UI command that executes distributed computations across the cluster and renders a comprehensive visual dashboard with interactive charts, null indicators, and distribution shapes without requiring manual charting.
Choosing Between the Three Commands
| Need | Command | Returns |
|---|---|---|
| A quick count/mean/stddev/min/max table in code | df.describe() | Spark DataFrame — no percentiles |
| Quartiles, median, or custom percentiles in code | df.summary() | Spark DataFrame including 25%, 50%, 75% by default |
| A visual profile with nulls, cardinality, and distributions | dbutils.data.summarize(df) | Interactive rendering in the notebook cell |
# describe(): no percentiles at all
display(df.describe("tenure_months", "monthly_charges"))
# summary(): quartiles by default, or name exactly what you want
display(df.summary())
display(df.summary("count", "mean", "5%", "50%", "95%"))
Both describe() and summary() return a DataFrame, not printed output. A cell
that ends in df.summary() shows nothing useful until it is wrapped in display() or
.show() — a detail that appears in exam code snippets.
Behaviour worth remembering
describe()andsummary()operate on numeric and string columns; string columns get count, min, and max (lexicographic) but no mean or stddev.- Percentiles are computed with an approximate algorithm so the work stays
distributed. For an exact quantile use
df.stat.approxQuantile(col, probs, 0.0), which is exact at a relative error of 0 but far more expensive. - Neither method reports null counts directly; the
countrow shows non-null counts, so a column whose count is lower than the DataFrame's row count has nulls. This is the reasondbutils.data.summarizeexists — it reports missingness explicitly.
Reading the Profile for Defects
A summary table is only useful if you know what to look for:
| Signal in the profile | Likely defect | Action |
|---|---|---|
count well below the row count | Missing values | Plan imputation (Section 2.6) |
min is negative on a quantity that cannot be negative | Sentinel encoding (-1, -999) | Convert sentinels to null before imputing |
max far beyond the 75th percentile | Right skew or genuine outliers | Inspect with IQR fences (Section 2.4) |
stddev of exactly 0 | Constant column | Drop it — no predictive signal |
| Distinct count ≈ row count on a string column | Identifier masquerading as a category | Exclude from features |
mean far from the median | Skewed distribution | Prefer median imputation; consider a log transform (Section 2.5) |
A machine learning engineer runs dbutils.data.summarize(df) on a large PySpark DataFrame in a Databricks notebook. Which set of metrics and visual elements will be automatically calculated and displayed for numerical columns?
A data scientist needs the median and interquartile range of annual_income on a 400-million-row Spark DataFrame in order to set outlier fences. Which command produces those values?
In the output of df.summary(), the count row shows 8,400,000 for credit_score while the DataFrame has 10,000,000 rows. What does this indicate?