CertSafari
    Databricks Certified Machine Learning Associate· Lessons

    Domain 2 · Lesson 23/48

    Imputing Missing Values: Mean vs Median vs Mode

    Compare and contrast imputing missing values with the mean or median or mode value

    16 min read
    2.08% of exam
    8 sources
    Published 3 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Explain why filling in missing values with a column statistic usually keeps more information than dropping rows
    • Compare mean, median and mode imputation, including which column types each one can handle
    • Show from real numbers how one extreme value moves the mean but leaves the median almost unchanged
    • Apply each strategy with scikit-learn SimpleImputer, PySpark na.fill with sf.median / sf.mode, and the Databricks AutoML imputers parameter

    Key concept

    Univariate (column-statistic) imputation — Each missing value is replaced with one number or category worked out from the values that are present in the same column: its mean, its median or its most frequent value. The choice of statistic decides which value goes in, and so how much the filled column still looks like the original data.

    1.Drop or fill: why imputation exists

    Real datasets have gaps, and most estimators can't work with them. You have two broad options. You can remove the rows (or columns) that have gaps, or you can fill each gap with a value inferred from the data you do have. The Databricks EDA tutorial lists both options in its data-cleaning step: handling missing values 'might involve replacing them with a specific value or removing the affected rows.' Before comparing mean, median and mode, it helps to see what dropping costs.

    Dropping rows with nulls: two-thirds of this DataFrame disappears, including Bob's real age and namepython
    from pyspark.sql import Row
    
    df = spark.createDataFrame([
        Row(age=10, height=80.0, name="Alice"),
        Row(age=5, height=None, name="Bob"),
        Row(age=None, height=None, name="Tom"),
    ])
    
    df.na.drop().show()
    
    +---+------+-----+
    |age|height| name|
    +---+------+-----+
    | 10|  80.0|Alice|
    +---+------+-----+

    The scikit-learn guide makes the same point. Dropping incomplete rows 'comes at the price of losing data which may be valuable (even though incomplete)'. Its recommended alternative is to impute: work out the missing values from the known part of the data. The simplest way is univariate. For each column, compute one statistic from that column's present values and write it into every gap. scikit-learn's SimpleImputer offers three such statistics (mean, median and most frequent) plus a fixed constant. The rest of this lesson is about choosing among them.

    Checkpoint 1 of 8· Check yourself

    According to the scikit-learn guide, what is the main drawback of discarding rows or columns that contain missing values?

    Sources123

    2.Mean vs median: what one extreme value does

    Mean and median imputation both fill a numeric column with one central value, but they measure 'central' differently. The mean adds up all the present values and divides by how many there are. The median is the middle value once the values are sorted. In Databricks you can compute the median directly with the PySpark function sf.median, which 'Returns the median of the values in a group.' Here it is applied to earnings per course:

    sf.median per group. Note the dotNET earnings: 5000, 10000 and one much larger 48000python
    from pyspark.sql import functions as sf
    df = spark.createDataFrame([
        ("Java", 2012, 20000), ("dotNET", 2012, 5000),
        ("Java", 2012, 22000), ("dotNET", 2012, 10000),
        ("dotNET", 2013, 48000), ("Java", 2013, 30000)],
        schema=("course", "year", "earnings"))
    df.groupby("course").agg(sf.median("earnings")).show()
    
    +------+----------------+
    |course|median(earnings)|
    +------+----------------+
    |  Java|         22000.0|
    |dotNET|         10000.0|
    +------+----------------+

    That reveal is the core of the mean-versus-median comparison. The mean uses the size of every value, so one large value moves it. The median depends only on which value is in the middle, so an extreme value at one end barely moves it. When a numeric column's values are bunched together, the two statistics come out close and either one is a reasonable fill. When a few values sit far out on one side, they can be well apart, and the median gives a fill value much nearer to most of the real data. One caveat: the documentation provided for this lesson has no written rule such as 'use the median for skewed data.' That guideline comes from the exam guide's framing. The arithmetic above shows why it holds.

    The statistic is learned once and then reused. In scikit-learn, SimpleImputer computes each column's statistic when you call fit, then writes it into the gaps of whatever data you pass to transform. Fit on a first column of 1, NaN and 7 with strategy='mean', and the learned mean is 4. Every later NaN in that column becomes 4, even in rows that weren't present during fit.

    Checkpoint 2 of 8· Check yourself

    SimpleImputer(strategy='mean') is fit on a first column containing 1, NaN and 7. You then transform a new row whose first value is NaN. What value goes in?

    Checkpoint 3 of 8· Exam question

    A retention analytics table includes `days_since_last_login`, a right-skewed numeric column where most customers log in within a week but a small group of dormant accounts push values into the thousands. About 6% of rows are missing this value, and the team wants to fill the gaps without letting the dormant-account outliers distort the typical customer's imputed value. Which imputation strategy best fits this column?

    Sources43

    3.Mode: the strategy that works on categories

    Mean and median both need numbers you can add or sort. A column of country codes, product types or colours has neither, so the only statistic left is the most frequent value, the mode. This is the main contrast in the objective. Mean and median are choices for numeric columns. Mode is the statistic that also works on categorical ones. scikit-learn's SimpleImputer 'supports categorical data represented as string values or pandas categoricals when using the 'most_frequent' or 'constant' strategy'. Mean and median aren't on that list. Note the name: scikit-learn calls the mode strategy most_frequent, not 'mode'.

    Checkpoint 4 of 8· Fill the gap

    This DataFrame holds string categories with some NaNs. Which strategy value fills each gap with the column's most common category?

    >>> imp = SimpleImputer(strategy=" ? ")
    >>> print(imp.fit_transform(df))

    On the Databricks side, the mode is available as the PySpark function sf.mode and as the SQL mode aggregate. The SQL reference says it 'Returns the most frequent, not NULL, value of expr in a group'. The argument can be 'An expression of any type that can be compared', including strings. The 'not NULL' part matters for imputation. In the query below, NULL is the most common entry (three of seven), but mode ignores it and returns the most common real value. If a group contains only nulls, mode returns NULL, so there is nothing to fill with.

    SQL mode skips NULLs: three NULLs, but the answer is 3sql
    > SELECT mode(col) FROM VALUES (NULL), (1), (NULL), (2), (NULL), (3), (3) AS tab(col);
     3

    Ties are a risk mean and median don't have. If two categories are equally common, the imputed value can differ from one run to the next, and so can a model trained on the result. Both the PySpark and SQL forms take a deterministic flag. In PySpark, deterministic: 'If there are multiple equally-frequent results then return the lowest (defaults to false).' Here it breaks a three-way tie by returning -10. The SQL reference adds one caveat: even with deterministic set to true, results can vary for some string collations, such as UTF8_LCASE.

    sf.mode with deterministic=True: a three-way tie between -10, 0 and 10 returns -10python
    from pyspark.sql import functions as sf
    df = spark.createDataFrame([(-10,), (0,), (10,)], ["col"])
    df.select(sf.mode("col", True)).show()

    Checkpoint 5 of 8· Exam question

    An order table has a `shipping_method` column with categorical values such as "Standard", "Express", and "Overnight". About 8% of rows are missing this value before the team one-hot encodes the feature for a classification model. Which imputation approach is most appropriate for this nominal categorical column?

    Sources563

    4.Applying a strategy: na.fill, constants and AutoML imputers

    The three statistics side by side, plus a constant fill for comparison
    StrategySimpleImputer strategySpark function to compute itString / categorical columns?Behaviour to watch
    Mean'mean'(average of the column)Not supportedEvery value counts towards it, so one extreme value moves it (dotNET: 21000 vs median 10000)
    Median'median'sf.medianNot supportedThe middle value; an extreme value at one end barely moves it
    Mode'most_frequent'sf.modeSupportedTies are non-deterministic unless deterministic is set
    Constant'constant'(a literal passed to fillna)SupportedFills a fixed value of your choice, not one computed from the data

    In PySpark, DataFrame.fillna and DataFrameNaFunctions.fill (df.na.fill) are aliases. Both take a literal replacement value: 'The replacement value must be an int, float, boolean, or string.' So a median or mode fill in Spark takes two steps. First compute the statistic with sf.median or sf.mode, then pass the result to na.fill. To give different columns different strategies (say a median for a numeric column and a mode for a string column), pass a dict: 'If the value is a dict, then subset is ignored and value must be a mapping from column name (string) to replacement value.'

    A per-column dict fill: age and name are filled, and height stays NULL because it isn't in the dictpython
    df = spark.createDataFrame([
        (10, 80.5, "Alice"),
        (5, None, "Bob"),
        (None, None, "Tom")],
        schema=["age", "height", "name"])
    
    df.na.fill({'age': 50, 'name': 'unknown'}).show()
    
    +---+------+-------+
    |age|height|   name|
    +---+------+-------+
    | 10|  80.5|  Alice|
    |  5|  NULL|    Bob|
    | 50|  NULL|unknown|
    +---+------+-------+

    A single scalar is easier to write but less careful. In the fillna reference, df.na.fill(50) fills age and height but leaves the string name and boolean bool columns NULL, because 'Columns specified in subset that do not have matching data types are ignored.' A constant fill like this, or the EDA tutorial's df.fillna(0), is a different kind of choice from mean, median or mode. The tutorial describes it as 'A common way to treat NaN or Null values is to replace them with 0 for easier mathematical processing', but the 0 doesn't come from the column's own values.

    Checkpoint 6 of 8· Check yourself

    A DataFrame has columns age (int), height (float), name (string) and bool (boolean). After df.na.fill(50), name and bool still contain NULLs. Why?

    Databricks AutoML exposes the same three choices as a training option. The imputers parameter of databricks.automl.classify and regress maps column names to a strategy, and as a string 'the value must be one of “mean”, “median”, or “most_frequent”.' For a fixed value, use a dictionary with strategy constant and a fill_value. If you leave a column out, 'AutoML selects a default strategy based on column type and content', the same type-driven choice this lesson has been making by hand. Specifying a non-default method has a side effect: AutoML then skips semantic type detection.

    Checkpoint 7 of 8· Match them up

    Match each AutoML imputers setting for a column to what happens

    Tap a term, then the definition that fits it.

    Checkpoint 8 of 8· Exam question

    A quality-control dataset tracks `part_weight_grams` for a manufactured component. Histograms show the distribution is approximately symmetric and bell-shaped, with no extreme outliers, and 3% of measurements are missing due to a sensor glitch. Which imputation strategy is the most defensible default for this column?

    Sources781

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Dropping rows with missing values is the safe, lossless default; na.drop() only removes rows that are mostly empty.Why is that wrong?

      By default, na.drop() removes every row that has any null or NaN. That also throws away the real values in those rows, which is the cost imputation avoids.

      Covered in Drop or fill: why imputation exists

    2. 2.Mean or median imputation can be applied to a string or categorical column, and scikit-learn's mode strategy is called 'mode'.Why is that wrong?

      SimpleImputer supports string and categorical data only with most_frequent (the mode) or constant. The strategy name is most_frequent.

      Covered in Mode: the strategy that works on categories

    3. 3.Mode imputation always gives the same fill value for the same data.Why is that wrong?

      When two values are equally frequent, Databricks mode returns either one unless deterministic is set to true. Even then, some string collations can still vary.

      Covered in Mode: the strategy that works on categories

    4. 4.df.na.fill(value) with one scalar fills every column of the DataFrame.Why is that wrong?

      Columns whose type doesn't match the replacement value are silently skipped. To fill numeric and string columns with different statistics, pass a dict of column name to value.

      Covered in Applying a strategy: na.fill, constants and AutoML imputers

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Handling missing values, which might involve replacing them with a specific value or removing the affected rows.”
      ↩︎ Drop or fill: why imputation exists
      “A common way to treat NaN or Null values is to replace them with 0 for easier mathematical processing.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
    2. 2.
      “Returns a new DataFrame omitting rows with null or NaN values.”
      ↩︎ Drop or fill: why imputation exists
      “Returns a new DataFrame omitting rows with null or NaN values.”
      ↩︎ Exam trap 1
    3. 3.
      “this comes at the price of losing data which may be valuable (even though incomplete)”
      ↩︎ Drop or fill: why imputation exists
      “using the statistics (mean, median or most frequent) of each column in which the missing values are located”
      ↩︎ Mean vs median: what one extreme value does
      “supports categorical data represented as string values or pandas categoricals when using the 'most_frequent' or 'constant' strategy”
      ↩︎ Mode: the strategy that works on categories
      “imputes values in the i-th feature dimension using only non-missing values in that feature dimension”
      ↩︎ Key concept
      “supports categorical data represented as string values or pandas categoricals when using the 'most_frequent' or 'constant' strategy”
      ↩︎ Exam trap 2
    4. 5.
      “Returns the most frequent, not NULL, value of expr in a group.”
      ↩︎ Mode: the strategy that works on categories
      “An expression of any type that can be compared.”
      ↩︎ Mode: the strategy that works on categories
      “The result is non-deterministic if there is a tie for the most frequent value.”
      ↩︎ Exam trap 3
      “The function returns either 1 or 2, but not 3”
      ↩︎ Prediction
    5. 6.
      “If there are multiple equally-frequent results then return the lowest (defaults to false).”
      ↩︎ Mode: the strategy that works on categories
    6. 7.
      “If the value is a dict, then subset is ignored and value must be a mapping from column name (string) to replacement value.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
      “The replacement value must be an int, float, boolean, or string.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
      “DataFrame.fillna and DataFrameNaFunctions.fill are aliases of each other.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
      “Columns specified in subset that do not have matching data types are ignored.”
      ↩︎ Exam trap 4
      “Columns specified in subset that do not have matching data types are ignored.”
      ↩︎ Checkpoint
    7. 8.
      “If specified as a string, the value must be one of “mean”, “median”, or “most_frequent”.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
      “If you specify a non-default imputation method, AutoML does not perform semantic type detection.”
      ↩︎ Applying a strategy: na.fill, constants and AutoML imputers
      “If no imputation strategy is provided for a column, AutoML selects a default strategy based on column type and content.”
      ↩︎ Checkpoint

    Spotted a mistake, or was something unclear? Tell us.