SQL + Python Data Engineering Lab

From raw data to reliable insights

An interactive SQL, Python, Pandas, ETL and data quality lab demonstrating how disorganized datasets become reliable analytical assets.

The simulated dataset carries duplicates, missing values, inconsistent formats and invalid dates. The lab uses SQL, Python and Pandas to investigate, clean and validate them. It then exports an analysis-ready dataset.

All data is fictional and deterministic. No sensitive fiscal, personal or institutional information is used.

The queries and pipelines shown are safe technical demonstrations. Results are computed from a simulated dataset using deterministic functions equivalent to the SQL and Pandas operations displayed.

  • SQL
  • Python
  • Pandas
  • ETL
  • Data Quality
  • CSV
  • Data Modeling
  • Power BI
  • TypeScript
  • Next.js
Raw records
86
Issues detected
18
Cleaned records
77
Score before
65.1
Score after
98.7

The raw dataset

This is how data arrives: fields as text, mixed formats and real extraction defects. The first rows below already contain some of the problems the audit detects.

First rows of the simulated raw dataset.
idcompetence_daterevenue_namecategoryestimated_valuecollected_value
R-2025-1101-012025-01-01ISStributaria37803667
R-2025-1101-022025-02-01ISStributaria36363636
R-2025-1101-032025-03-01ISStributaria34923597
R-2025-1101-032025-04-01ISStributaria33483549
R-2025-1101-052025-05-01ISStributaria38163587
R-2025-1101-062025-13-45ISStributaria36723562

Investigating with SQL

Ten preset queries cover the investigation cycle: trend, gap, duplicate/missing/validity audits, ranking, running total, outliers and the final BI-ready dataset.

Basicruns over the cleaned dataset

Purpose: Group the simulated revenue by competence month and spot the trend.

SELECT
  strftime('%Y-%m', competence_date) AS competence_month,
  SUM(collected_value) AS collected_revenue
FROM revenue_clean
GROUP BY competence_month
ORDER BY competence_month;

Pandas pipeline equivalent

df.groupby(df["competence_date"].dt.to_period("M"))["collected_value"].sum()
Result of "Collected revenue by month" — 12 row(s) total.
competence_monthcollected_revenue
2025-017,211
2025-0212,211
2025-0311,157
2025-047,754.56
2025-0511,495
2025-067,189
2025-0710,569
2025-0850,529

Executive reading: The monthly view is the base of executive tracking: consecutive drops raise alerts.

The queries and pipelines shown are safe technical demonstrations. Results are computed from a simulated dataset using deterministic functions equivalent to the SQL and Pandas operations displayed.

Python + Pandas pipeline

Each step below shows the problem, the conceptual Pandas code and — on execution — how many records are actually affected in the simulated dataset.

01Read and standardize columns

Problem: Columns with spaces, mixed casing and divergent names break downstream scripts.

import pandas as pd

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

df.columns = (
    df.columns
    .str.strip()
    .str.lower()
    .str.replace(" ", "_")
)

02Tolerant type conversion

Problem: Mixed date formats and text-encoded numbers (1.234,56) prevent aggregation.

df["competence_date"] = pd.to_datetime(
    df["competence_date"],
    errors="coerce",
    dayfirst=True,
)

df["collected_value"] = pd.to_numeric(
    df["collected_value"]
    .str.replace(".", "", regex=False)
    .str.replace(",", ".", regex=False),
    errors="coerce",
)

03Text normalization

Problem: " iptu ", "Iss" and "TRIBUTARIA" are the same thing written three ways.

df["revenue_name"] = df["revenue_name"].str.strip().str.upper()
df["category"] = (
    df["category"]
    .str.strip()
    .str.lower()
    .str.normalize("NFKD")
)

04Missing values and duplicates

Problem: Missing values and repeated rows inflate sums and distort metrics.

df = df.drop_duplicates()
df = df.drop_duplicates(subset="id", keep="first")

df["category"] = df["category"].fillna(
    df["revenue_code"].map(code_to_category)
)
df = df.dropna(subset=["competence_date", "collected_value"])

05Rule validation and anomalies

Problem: Negative values, orphan codes and outliers need explicit decisions, not silence.

valid = df["collected_value"] >= 0
known = df["revenue_code"].isin(dim_revenue["code"])

rejected = df[~(valid & known)]
df = df[valid & known]

df["is_outlier"] = (
    df["collected_value"] > df["estimated_value"] * 5
)

06Aggregation and export to BI

Problem: The end goal is a stable dataset that Power BI consumes without surprises.

monthly = (
    df.groupby([df["competence_date"].dt.to_period("M"), "revenue_name"])
    .agg(collected=("collected_value", "sum"))
    .reset_index()
)

df.to_csv("revenue_clean.csv", index=False)

Pipeline execution

Run the pipeline to audit the dataset, apply the deterministic transformations, compare before/after and unlock the cleaned dataset export.

Run the pipeline to see the log, the metrics and the before/after comparison.

Flow architecture

From extraction to decision: the same data goes through investigation, cleansing, auditing and publication — ending in the Power BI case's executive dashboard.

  1. Raw CSV
  2. SQL investigation
  3. Python / Pandas
  4. Data quality
  5. Clean dataset
  6. Power BI
  7. Executive decision

Next: BI and decision-making

The dataset cleaned in this lab shares the same domain as the Power BI case — fiscal KPIs, targets and municipal revenue. See the data applied in the executive dashboard.

Lab stack

SQLPythonPandasETLPower BINext.jsTypeScriptVercel