Test and Run a Reproducible Data Science Pipeline
Capital OneData Scientist · Technical ScreenLast updated
Design and implement a robust, testable Python data pipeline class that validates input data schemas, handles feature transformation, logs execution metrics, and produces versioned outputs. Write targeted unit and integration tests ensuring graceful failure on corrupted inputs.
Scenario
In financial services, production models (such as credit risk assessment or fraud detection) must be strictly reproducible, auditable, and resilient to malformed upstream data. You are tasked with implementing the execution engine and testing framework for a batch scoring and model evaluation pipeline.
Task
- Implement a class
BatchPipelineRunnerthat coordinates configuration loading, data validation, feature engineering, and model inference. - Design unit tests and an end-to-end integration test (using standard Python
unittestorpytestpatterns) that verify schema compliance, artifact generation, and error handling.
Pipeline Interface
from typing import Dict, Any, Tuple
import pandas as pd
class DataValidationError(Exception):
"""Raised when input data violates expected schema or bounds."""
pass
class BatchPipelineRunner:
def __init__(self, config: Dict[str, Any]):
"""
config contains:
- 'schema': dict mapping column_name -> expected_type (e.g., {'account_age': int, 'balance': float})
- 'feature_cols': list of str
- 'output_dir': str
- 'model_version': str
"""
self.config = config
def validate_input(self, df: pd.DataFrame) -> bool:
"""
Validates that:
- All required schema columns exist.
- Data types are compatible with config['schema'].
- No negative values in numeric columns specified in config['schema'].
Raises DataValidationError if invalid.
"""
pass
def transform_features(self, df: pd.DataFrame) -> pd.DataFrame:
"""
Applies deterministic transformations:
- Fills missing values in feature_cols with median values derived from the batch.
- Creates a log-transformed feature 'log_balance' if 'balance' exists (using log1p).
Returns a new DataFrame containing transformed features.
"""
pass
def run(self, input_df: pd.DataFrame, model_mock: Any) -> Dict[str, Any]:
"""
Orchestrates:
1. Validation
2. Feature transformation
3. Model prediction (calls model_mock.predict(features))
Returns a metadata dict containing:
- 'status': 'SUCCESS' or 'FAILED'
- 'record_count': int
- 'model_version': str
- 'predictions': list or np.ndarray
"""
pass
Testing Requirements
Provide test cases testing:
- Schema failure handling (missing column or negative balance) raising
DataValidationError. - Feature transformation correctness (deterministic imputation and log transformation).
- An integration test showing end-to-end execution returning the expected metadata structure.
Constraints
- The input DataFrame will have between 0 and 1,000,000 rows.
- Transformations must not mutate the original
input_dfin-place. - Ensure structured logs or return status indicate exact reasons on failure.
Hints
Hint 1
Separate schema checks into presence checks, type compatibility checks, and domain/value boundary checks to make error messages diagnostic.
Hint 2
Avoid mutating input DataFrames in-place by using `df.copy()` at the start of feature engineering, and compute imputation values deterministically.
Hint 3
For testing, use mock model objects with pre-defined `.predict()` returns to test the pipeline runner independently of model weights or external files.
Follow-up questions
- How would you adapt this pipeline to detect feature drift between the training baseline and the incoming batch?
- If the input data is too large to fit into memory (e.g., 50 GB), how would you refactor the pipeline and validation steps?
- How would you integrate schema contract enforcement into a CI/CD pipeline before running automated integration tests?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.