Code review of a scikit-learn-style transformer, its tests and a shell test script

Read the full interview experience this question came from →

Quick Overview

A data science code-review exercise built around a scikit-learn-style transformer class with fit and transform methods and the shell script that runs its tests. It tests reviewing the class for correctness, explaining why class-based design is preferred in data science code, writing pytest tests with real coverage, and explaining how the script reports failure.

Code review of a scikit-learn-style transformer, its tests and a shell test script

Company: Capital One

Role: Data Scientist

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

In a code-review and testing round for a data science role, the interviewer shows you data-preparation code written as scikit-learn-style classes with `fit` and `transform` methods, together with the shell script that runs the tests. You are asked to review the code, explain why it is organized as classes, write tests for it, and explain how the `.sh` script works. The report describes the round only as code review and testing of functions that abstract scikit-learn-style classes, plus a `.sh` file; it does not reproduce the code. This version supplies a small representative class and script so the review can be practiced. The class is meant to be fitted once on training data and then applied to validation, test and production data. **`prep.py`** ```python import pandas as pd class MeanImputeScaler: """Fill missing values with the training mean, then standardize each column.""" def __init__(self, columns): self.columns = columns def fit(self, df): self.means = df[self.columns].mean() self.stds = df[self.columns].std() def transform(self, df): for col in self.columns: df[col] = df[col].fillna(df[col].mean()) df[col] = (df[col] - self.means[col]) / self.stds[col] return df def fit_transform(self, df): self.fit(df) return self.transform(df) ``` **`run_tests.sh`** ```bash #!/bin/bash pip install -r requirements.txt python -m pytest tests/ echo "All tests passed" ``` ### Clarifying Questions - Will the class run inside a scikit-learn `Pipeline`, cross-validation or `GridSearchCV`, or is it only called by hand? - How should a column that is constant in the training data be handled: mapped to 0, dropped, or rejected with an error? - Should `transform` fail when a fitted column is missing from its input, and should extra columns pass through untouched? - Where does `run_tests.sh` run (in CI, on laptops, or both), and in which Python environment? ### Part 1 — Review the class Review `MeanImputeScaler` as you would a pull request. For each problem you find, explain what goes wrong when a model is trained and then served with this class, rank the problems by severity, and propose a fix. Then write the corrected class. ```hint Follow the data Trace a training DataFrame through `fit`, then a one-row scoring DataFrame with a missing value through `transform`. Which statistics does each line use, and where were they computed? ``` ```hint What the caller gets back Look at what each method returns, and at the state of the DataFrame the caller passed in once the call finishes. ``` #### What This Part Should Cover - Defects that make training, validation and serving see different inputs, and why they rank highest - Breaks in the fit/transform contract that stop chaining and pipeline tools from working - Numerical and input edge cases, and the errors a user would see - A corrected class that fixes each defect ### Part 2 — Why classes A teammate asks why this is a class at all, since a single function `clean(df)` would be shorter. Explain why the class-based fit/transform design is preferred in data science code, and when a plain function is still the right choice. ```hint Two moments in a model's life Think about what has to carry over from the moment the model is trained to the moment it scores new data, and what holds it in between. ``` #### What This Part Should Cover - Where learned statistics live, and what that means for leakage and for serving - How a shared interface lets steps compose with validation and tuning tools - Persistence and testability - When a stateless function is enough ### Part 3 — Write the tests Write pytest tests for your corrected class. List the behaviors the suite should cover, show at least four tests in code, and explain how you would measure coverage and what a coverage number does not tell you. ```hint Fit on one frame, score another Several of the defects from Part 1 cannot show up in a test that fits and transforms the same DataFrame. ``` #### What This Part Should Cover - Exact expected values on small hand-computed inputs - Tests aimed at each defect from Part 1, including the production scoring case - Edge cases and error paths - How coverage is measured and its limits ### Part 4 — The shell script Explain what a `.sh` file is and the ways `run_tests.sh` can be run. Then say whether the script does its job when CI runs it, and rewrite it. ```hint One number decides CI decides pass or fail from a single number the script hands back when it finishes. Which command produces that number here? ``` #### What This Part Should Cover - What a shell script is, its first line, and how execute permission affects the way it is run - Exit status and how CI uses it - The defect in this script and a corrected version - Environment and reproducibility concerns ### What a Strong Answer Covers - Prioritizes defects that silently change model inputs over style comments - Ties each review comment to a concrete consequence for training, validation or serving - Tests organized around behaviors and defects rather than around line coverage - Working knowledge of the scikit-learn estimator conventions and the reasons behind them - A constructive review tone that explains why each change matters and proposes the fix ### Follow-up Questions - How would you make the class work with `GridSearchCV`, which copies estimators by reading their constructor parameters? - The model scores data both in a nightly batch job and in a real-time service. How would you guarantee both apply exactly the same transformation? - Your tests reach 100% line coverage, yet a defect like the one in `transform` could still ship. How? - How would you test the full pipeline (preprocessing plus model) without making the suite slow or flaky?

Overview: A data science code-review exercise built around a scikit-learn-style transformer class with fit and transform methods and the shell script that runs its tests. It tests reviewing the class for correctness, explaining why class-based design is preferred in data science code, writing pytest tests with real coverage, and explaining how the script reports failure.

Read the full Capital One Data Scientist interview experience this question came from

Capital One logo
Capital One
May 11, 2026
mediumData ScientistOnsiteSoftware Engineering Fundamentals
0
0

In a code-review and testing round for a data science role, the interviewer shows you data-preparation code written as scikit-learn-style classes with fit and transform methods, together with the shell script that runs the tests. You are asked to review the code, explain why it is organized as classes, write tests for it, and explain how the .sh script works.

The report describes the round only as code review and testing of functions that abstract scikit-learn-style classes, plus a .sh file; it does not reproduce the code. This version supplies a small representative class and script so the review can be practiced. The class is meant to be fitted once on training data and then applied to validation, test and production data.

prep.py

import pandas as pd


class MeanImputeScaler:
    """Fill missing values with the training mean, then standardize each column."""

    def __init__(self, columns):
        self.columns = columns

    def fit(self, df):
        self.means = df[self.columns].mean()
        self.stds = df[self.columns].std()

    def transform(self, df):
        for col in self.columns:
            df[col] = df[col].fillna(df[col].mean())
            df[col] = (df[col] - self.means[col]) / self.stds[col]
        return df

    def fit_transform(self, df):
        self.fit(df)
        return self.transform(df)

run_tests.sh

#!/bin/bash
pip install -r requirements.txt
python -m pytest tests/
echo "All tests passed"

Clarifying Questions Guidance

  • Will the class run inside a scikit-learn Pipeline , cross-validation or GridSearchCV , or is it only called by hand?
  • How should a column that is constant in the training data be handled: mapped to 0, dropped, or rejected with an error?
  • Should transform fail when a fitted column is missing from its input, and should extra columns pass through untouched?
  • Where does run_tests.sh run (in CI, on laptops, or both), and in which Python environment?

Part 1 — Review the class

Review MeanImputeScaler as you would a pull request. For each problem you find, explain what goes wrong when a model is trained and then served with this class, rank the problems by severity, and propose a fix. Then write the corrected class.

What This Part Should Cover Guidance

  • Defects that make training, validation and serving see different inputs, and why they rank highest
  • Breaks in the fit/transform contract that stop chaining and pipeline tools from working
  • Numerical and input edge cases, and the errors a user would see
  • A corrected class that fixes each defect

Part 2 — Why classes

A teammate asks why this is a class at all, since a single function clean(df) would be shorter. Explain why the class-based fit/transform design is preferred in data science code, and when a plain function is still the right choice.

What This Part Should Cover Guidance

  • Where learned statistics live, and what that means for leakage and for serving
  • How a shared interface lets steps compose with validation and tuning tools
  • Persistence and testability
  • When a stateless function is enough

Part 3 — Write the tests

Write pytest tests for your corrected class. List the behaviors the suite should cover, show at least four tests in code, and explain how you would measure coverage and what a coverage number does not tell you.

What This Part Should Cover Guidance

  • Exact expected values on small hand-computed inputs
  • Tests aimed at each defect from Part 1, including the production scoring case
  • Edge cases and error paths
  • How coverage is measured and its limits

Part 4 — The shell script

Explain what a .sh file is and the ways run_tests.sh can be run. Then say whether the script does its job when CI runs it, and rewrite it.

What This Part Should Cover Guidance

  • What a shell script is, its first line, and how execute permission affects the way it is run
  • Exit status and how CI uses it
  • The defect in this script and a corrected version
  • Environment and reproducibility concerns

What a Strong Answer Covers Guidance

  • Prioritizes defects that silently change model inputs over style comments
  • Ties each review comment to a concrete consequence for training, validation or serving
  • Tests organized around behaviors and defects rather than around line coverage
  • Working knowledge of the scikit-learn estimator conventions and the reasons behind them
  • A constructive review tone that explains why each change matters and proposes the fix

Follow-up Questions Guidance

  • How would you make the class work with GridSearchCV , which copies estimators by reading their constructor parameters?
  • The model scores data both in a nightly batch job and in a real-time service. How would you guarantee both apply exactly the same transformation?
  • Your tests reach 100% line coverage, yet a defect like the one in transform could still ship. How?
  • How would you test the full pipeline (preprocessing plus model) without making the suite slow or flaky?