Skip to content

6.2. batch_ingest-nyc_taxi-spark-iceberg

Documents the scenario's paired Jupyter (notebook.ipynb) and Zeppelin (notebook.zpln) implementations. Both notebooks implement identical logic in PySpark and Scala.

1. Section map

Subsection Scala (Zeppelin) PySpark (Jupyter)
2.1 Setup
2.2 Read
2.3 Transform
2.4 Write
2.5 Verify

2. Walkthrough

2.1 Setup

Scala (Zeppelin):

import spark.implicits._
import org.apache.spark.sql.functions._
// spark is pre-bound by the Atlas Zeppelin interpreter (Spark Connect + lakehouse catalog)

PySpark (Jupyter):

from pyspark.sql import SparkSession
from pyspark.sql import functions as F

spark = SparkSession.builder.remote("sc://spark-connect:15002").getOrCreate()

Both setup implementations choose the scale from an explicit notebook override, then DATASET_SCALE, then small. They call the internal resolver once, validate its exact response and expected NYC Taxi object order, and retain one verified immutable generation as datasetSparkUris / dataset_spark_uris.

2.2 Read

Scala (Zeppelin):

val taxiPaths = datasetSparkUris
val raw = taxiPaths
  .map(path => spark.read.parquet(path).withColumn("passenger_count", col("passenger_count").cast("double")))
  .reduce(_.unionByName(_))
raw.printSchema()

PySpark (Jupyter):

taxi_paths = dataset_spark_uris
raw = None
for path in taxi_paths:
    normalized = spark.read.parquet(path).withColumn('passenger_count', F.col('passenger_count').cast('double'))
    raw = normalized if raw is None else raw.unionByName(normalized)
raw.printSchema()

2.3 Transform

Scala (Zeppelin):

val bronze = raw
  .where($"tpep_pickup_datetime".isNotNull && ($"passenger_count" > 0))
  .withColumn("trip_date", to_date($"tpep_pickup_datetime"))

PySpark (Jupyter):

bronze = (raw
  .where(F.col('tpep_pickup_datetime').isNotNull() & (F.col('passenger_count') > 0))
  .withColumn('trip_date', F.to_date('tpep_pickup_datetime')))

2.4 Write

Scala (Zeppelin):

bronze.writeTo("lakehouse.bronze.nyc_taxi_trips").using("iceberg").createOrReplace()

PySpark (Jupyter):

bronze.writeTo("lakehouse.bronze.nyc_taxi_trips").using("iceberg").createOrReplace()

2.5 Verify

Scala (Zeppelin):

spark.table("lakehouse.bronze.nyc_taxi_trips").count()

PySpark (Jupyter):

spark.table("lakehouse.bronze.nyc_taxi_trips").count()

3. Scala / PySpark parity

Both notebooks share the same numbered sections and produce identical Iceberg tables; only the language and interpreter differ.

4. How to run

Open the scenario's zeppelin/notebook.zpln on the Atlas Zeppelin UI or jupyter/notebook.ipynb on JupyterHub, then run all paragraphs/cells top to bottom.