← Back to list

How I Standardized and Normalized 10 Million+ Rows of Dirty Data Using Python (Without Losing My…

My practical, no-fluff approach to cleaning and transforming massive datasets with pandas, NumPy, and custom logic.

Maximilian Oliver in The Pythoneers · 2025-08-02 02:53 · 55 claps · 2.6 min read paywalled
#handle-large-data #data-standardization #python-pandas #python-numpy #data-cleaning
Open on Medium ↗

How I Standardized and Normalized 10 Million+ Rows of Dirty Data Using Python (Without Losing My Mind)

My practical, no-fluff approach to cleaning and transforming massive datasets with pandas, NumPy, and custom logic.

Data is never clean. If you’ve ever opened a 2GB CSV from a client that swears their data is “pretty clean,” you know exactly what I mean.

In this deep dive, I’ll walk you through how I handled a real-world dataset with over 10 million messy records. The goal? Standardize and normalize everything for a machine learning pipeline that wouldn’t choke on inconsistencies.

1. Understanding the Dataset (and the Mess)

Before touching any code, I spent time identifying:

  • What formats were inconsistent?
  • Were there nulls? Duplicates?
  • Were there logical inconsistencies?

Here’s the initial inspection step:

import pandas as pd

df = pd.read_csv("raw_data.csv")

# Quick look at the shape and types
print(df.shape)
print(df.dtypes)

# Preview
print(df.head())

# Check for missing values
print(df.isnull().sum())

Key Insight: About 15% of the fields were missing, and the data types were all over the place.

2. Converting Everything to the Right Data Types

Data types were a nightmare: dates stored as strings, numeric fields stored as objects, and inconsistent encodings.

# Convert date column
df['created_at'] = pd.to_datetime(df['created_at'], errors='coerce')

# Convert price to float
df['price'] = pd.to_numeric(df['price'], errors='coerce')

# Convert ID to string
df['user_id'] = df['user_id'].astype(str)

3. Handling Nulls — Smartly

You can’t just .dropna() a million rows and call it a day.

# Fill missing prices with median
df['price'].fillna(df['price'].median(), inplace=True)

# Drop rows where critical fields are null
df.dropna(subset=['user_id', 'created_at'], inplace=True)

# Fill missing city with mode
df['city'].fillna(df['city'].mode()[0], inplace=True)

4. Standardizing Text Fields (Case, Spaces, Encoding)

This is where the invisible bugs hide.

def clean_text(x):
    if isinstance(x, str):
        return x.strip().lower().replace('\xa0', ' ')
    return x

df['city'] = df['city'].apply(clean_text)
df['country'] = df['country'].apply(clean_text)

Also, remove duplicates after standardization:

df.drop_duplicates(inplace=True)

5. Normalizing Categorical Data (Mapping Variants to One Form)

People write “USA”, “U.S.A.”, “United States”, “us”, and “US”. They’re all the same country.

country_map = {
    'usa': 'united states',
    'u.s.a.': 'united states',
    'us': 'united states',
    'united states': 'united states',
    'uk': 'united kingdom',
    'england': 'united kingdom',
}

df['country'] = df['country'].map(lambda x: country_map.get(x, x))

This step reduced the number of unique countries from 42 → 14. That’s the power of normalization.

6. Binning Numerical Data (For Downstream ML)

Some models don’t like raw numerical values — especially with skewed distributions.

import numpy as np

# Binning price into 5 quantile-based buckets
df['price_bin'] = pd.qcut(df['price'], q=5, labels=False)

# Binning user activity
df['activity_score'] = df['actions_last_30d'].fillna(0)
df['activity_level'] = pd.cut(df['activity_score'], bins=[-1, 5, 20, 100, np.inf], labels=['low', 'medium', 'high', 'superuser'])

7. Scaling Numerical Features

To normalize the data (important for algorithms like KNN, SVM, etc.)

from sklearn.preprocessing import MinMaxScaler, StandardScaler

scaler = MinMaxScaler()
df[['price_scaled', 'actions_scaled']] = scaler.fit_transform(df[['price', 'actions_last_30d']])

For a normal distribution:

standard_scaler = StandardScaler()
df[['price_standard']] = standard_scaler.fit_transform(df[['price']])

8. Final Sanity Checks and Export

I never trust data until I re-inspect it after processing.

# Check again
print(df.info())
print(df.describe(include='all'))

# Final null check
print(df.isnull().sum())

# Export cleaned data
df.to_csv("cleaned_data.csv", index=False)

Also, run basic profiling:

# Optional but useful
import pandas_profiling

profile = pandas_profiling.ProfileReport(df)
profile.to_file("data_profile.html")

9. Lessons Learned & What I’d Do Differently

  • Always inspect data types and NULLs first.
  • Don’t rush standardization. A single dirty entry can break your ML model.
  • Automate repetitive tasks with functions.
  • Profile early. Profile often.

This process took me a couple of days, and I reused most of this logic across multiple projects. Once you get this flow down, you’ll be able to clean millions of rows in minutes — confidently.


메타데이터
post_id
bb81b9ea6f1e
slug
how-i-standardized-and-normalized-10-million-rows-of-dirty-data-using-python-without-losing-my-bb81b9ea6f1e
url
https://medium.com/pythoneers/how-i-standardized-and-normalized-10-million-rows-of-dirty-data-using-python-without-losing-my-bb81b9ea6f1e
canonical_url
https://medium.com/pythoneers/how-i-standardized-and-normalized-10-million-rows-of-dirty-data-using-python-without-losing-my-bb81b9ea6f1e
author_url
https://medium.com/@maximilianoliver25
status
ok
fetched_at
2026-07-15 02:09:20