11 Introduction to PySpark and ETL Fundamentals
11.1 1. What is ETL and Why Does It Matter?
ETL (Extract, Transform, Load) is the process of moving data from operational systems (OLTP, APIs, logs, flat files) into a Data Warehouse, transforming it along the way to ensure it is clean, consistent, and ready for analytics.
Distinction with Preprocessing (Part II) ETL is data engineering: it operates on production streams, terabytes of data, and fixed schemas; its output is fully automated and reproducible. Analytical preprocessing (Part II) takes a cleaned dataset view from the DW and transforms it for a specific machine learning experiment. The Data Warehouse serves as the handoff point between both workflows.
11.1.1 1.1 Classic ETL vs. Modern ELT
| Classic ETL | Modern ELT | |
|---|---|---|
| Order | Extract → Transform → Load | Extract → Load → Transform |
| Execution Engine | External ETL Engine | Inside the DW / Lakehouse |
| Tools | Informatica, SSIS, Talend | dbt + BigQuery / Snowflake / DuckDB |
| Advantage | Data is clean upon arrival | Leverages scalable columnar DW compute |
| Disadvantage | Potential bottleneck in ETL engine | Requires scalable and cost-effective DW compute |
11.1.2 1.2 Why PySpark?
When datasets exceed the RAM capacity of a single machine, pandas becomes insufficient. Apache Spark divides work into partitions processed concurrently across a cluster of nodes using an optimized execution plan (DAG — Directed Acyclic Graph).
PySpark is Python’s API for Spark. It enables writing transformations that scale seamlessly from a local machine (local mode) to hundreds of cloud nodes (AWS EMR, Databricks, GCP Dataproc) without altering code.
11.2 2. Practice Environment: Databricks Community Edition
All exercises in this section run on Databricks Community Edition, which supplies a free Spark cluster without requiring local installation.
11.2.1 2.1 Setting Up the Account
- Navigate to community.cloud.databricks.com and create a free account.
- In the sidebar: Compute → Create compute → select runtime Databricks Runtime 14.x LTS (Spark 3.5, Python 3.11).
- Create a new Notebook (Language: Python).
In Databricks, spark is pre-instantiated as a global variable—do not instantiate a manual SparkSession. Simply begin queries with spark.read...
11.2.2 2.2 Uploading Datasets to DBFS
# Databricks File System (DBFS) — cluster file system
# Option A: Upload files via UI (File → Upload data)
# Option B: Download directly inside notebook using dbutils
dbutils.fs.mkdirs("dbfs:/datasets/taxi/")
# Download NYC Taxi dataset (Parquet, ~500 MB per month)
import urllib.request
url = "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet"
urllib.request.urlretrieve(url, "/tmp/yellow_2023_01.parquet")
dbutils.fs.mv("file:/tmp/yellow_2023_01.parquet", "dbfs:/datasets/taxi/yellow_2023_01.parquet")
# Verify download
display(dbutils.fs.ls("dbfs:/datasets/taxi/"))# Read from DBFS in Databricks
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
display(df.limit(5)) # display() is Databricks' interactive alternative to show()11.2.3 2.3 Databricks-Specific Commands
| Command | Description |
|---|---|
display(df) |
Renders interactive tables with built-in charting |
dbutils.fs.ls("dbfs:/path/") |
Lists files in DBFS |
dbutils.fs.mkdirs("dbfs:/path/") |
Creates a directory in DBFS |
dbutils.fs.rm("dbfs:/path/", True) |
Deletes a file or directory |
%sql SELECT ... |
Executes SQL cells against registered tables |
spark.catalog.listTables() |
Lists available tables in the metastore |
Delta Lake comes pre-installed in Databricks. There is no need for pip install delta-spark—simply pass .format("delta") directly.
11.2.4 2.4 Datasets for Exercises
| Dataset | Size | How to obtain in Databricks |
|---|---|---|
| NYC Taxi (1 month) | ~500 MB | Download via urllib (see §2.2) |
| NYC Taxi (Full 2023) | ~6 GB | Loop across all 12 monthly URLs |
| Synthetic TPC-H | Configurable | %pip install tpch + python -m tpch generate |
| Databricks Sample Data | Various | dbfs:/databricks-datasets/ (pre-installed) |
# Explore pre-installed Databricks sample datasets
display(dbutils.fs.ls("dbfs:/databricks-datasets/"))
# Includes: nyctaxi, airlines, wine-quality, iot-stream, retail-org, etc.
# Read sample taxi dataset directly
df_sample = spark.read.format("delta")\
.load("dbfs:/databricks-datasets/nyctaxi/tables/nyctaxi_yellow")
display(df_sample.limit(10))11.3 3. PySpark: Core Fundamentals
11.3.1 3.1 SparkSession
In Databricks, the SparkSession is automatically exposed as spark. In local environments:
from pyspark.sql import SparkSession
# Only required outside of Databricks
spark = (SparkSession.builder
.appName("ETL-Data-Mining")
.master("local[*]")
.config("spark.sql.shuffle.partitions", "8")
.getOrCreate())11.3.2 3.2 Spark DataFrames: Lazy vs. Eager Execution
# Transformations (lazy) — build logical execution graph without computing results
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
df2 = df.filter(df.trip_distance > 1.0)
df3 = df2.select("tpep_pickup_datetime", "trip_distance", "total_amount")
# Actions — trigger execution of the full DAG
df3.show(5) # or display(df3) in Databricks
df3.count()| Concept | Meaning |
|---|---|
| Transformation | filter, select, groupBy, join — construct the DAG |
| Action | show, count, collect, write — execute the DAG |
| Partition | Parallel unit of work; typically corresponds to one file output per partition |
| RDD | Low-level execution abstraction; use DataFrames unless handling raw custom objects |
11.3.3 3.3 Explicit Schemas (Best Practice)
from pyspark.sql.types import *
schema = StructType([
StructField("tpep_pickup_datetime", TimestampType(), True),
StructField("tpep_dropoff_datetime", TimestampType(), True),
StructField("passenger_count", IntegerType(), True),
StructField("trip_distance", DoubleType(), True),
StructField("fare_amount", DoubleType(), True),
StructField("tip_amount", DoubleType(), True),
StructField("total_amount", DoubleType(), True),
StructField("payment_type", IntegerType(), True),
])
df = spark.read.parquet("dbfs:/datasets/taxi/yellow_2023_01.parquet")
# Parquet embeds schema metadata; for CSV/JSON files, declare explicit schemas:
# df = spark.read.csv("dbfs:/path.csv", header=True, schema=schema)Using inferSchema=True forces a full scan over raw files; specifying explicit schemas accelerates read operations and avoids unexpected type inference errors.
11.4 References for this Chapter
- Chambers & Zaharia. Spark: The Definitive Guide. O’Reilly, 2018.
- PySpark Documentation
- Databricks Community Edition
- Databricks Free Training — official free courses.