
Apache Spark Data Processing
- 345 installs
- 61 repo stars
- Updated June 13, 2026
- manutej/luxor-claude-marketplace
apache-spark-data-processing is a Claude Code skill that implements distributed Spark jobs for developers who need large-scale transforms, aggregations, and feature pipelines with sound partitioning and performance tunin
About
apache-spark-data-processing is a data engineering skill from manutej/luxor-claude-marketplace that guides developers implementing Apache Spark jobs for large-scale data processing. It covers distributed transforms, aggregations, and feature pipelines with emphasis on partitioning strategies and performance tuning for production workloads. Developers reach for apache-spark-data-processing when datasets exceed single-node capacity and require Spark DataFrame operations, shuffle optimization, and pipeline design for analytics or ML feature stores. The skill fits backend and data engineers building ETL flows, batch analytics, or ML preprocessing stages where correct partitioning and job configuration directly affect cluster cost and runtime.
- Distributed transform patterns
- Partitioning and shuffle optimization
- Batch and structured streaming jobs
- Data lake and warehouse I/O
- Cluster sizing and cost control
Apache Spark Data Processing by the numbers
- 345 all-time installs (skills.sh)
- +20 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #546 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill apache-spark-data-processingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 345 |
|---|---|
| repo stars | ★ 61 |
| Last updated | June 13, 2026 |
| Repository | manutej/luxor-claude-marketplace ↗ |
How do you build performant Apache Spark ETL jobs?
Implement distributed Spark jobs for large-scale transforms, aggregations, and feature pipelines with sound partitioning and performance tuning.
Who is it for?
Data engineers implementing large-scale Spark ETL, aggregations, or ML feature pipelines that need partitioning and performance tuning.
Skip if: Developers processing small datasets comfortably handled by pandas or single-node SQL without distributed compute.
When should I use this skill?
A developer asks to implement Spark transforms, aggregations, or feature pipelines with partitioning and performance optimization.
What you get
Distributed Spark job code, tuned partitioning configs, and production-ready transform and aggregation pipelines.
- Spark job implementations
- Partitioning and tuning configurations
Files
Apache Spark Data Processing
A comprehensive skill for mastering Apache Spark data processing, from basic RDD operations to advanced streaming, SQL, and machine learning workflows. Learn to build scalable, distributed data pipelines and analytics systems.
When to Use This Skill
Use Apache Spark when you need to:
- Process Large-Scale Data: Handle datasets too large for single-machine processing (TB to PB scale)
- Perform Distributed Computing: Execute parallel computations across cluster nodes
- Real-Time Stream Processing: Process continuous data streams with low latency
- Complex Data Analytics: Run sophisticated analytics, aggregations, and transformations
- Machine Learning at Scale: Train ML models on massive datasets
- ETL/ELT Pipelines: Build robust data transformation and loading workflows
- Interactive Data Analysis: Perform exploratory analysis on large datasets
- Unified Data Processing: Combine batch and streaming workloads in one framework
Not Ideal For:
- Small datasets (<100 GB) that fit in memory on a single machine
- Simple CRUD operations (use traditional databases)
- Ultra-low latency requirements (<10ms) where specialized stream processors excel
- Workflows requiring strong ACID transactions across distributed data
Core Concepts
Resilient Distributed Datasets (RDDs)
RDDs are Spark's fundamental data abstraction - immutable, distributed collections of objects that can be processed in parallel.
Key Characteristics:
- Resilient: Fault-tolerant through lineage tracking
- Distributed: Partitioned across cluster nodes
- Immutable: Transformations create new RDDs, not modify existing ones
- Lazy Evaluation: Transformations build computation graph; actions trigger execution
- In-Memory Computing: Cache intermediate results for iterative algorithms
RDD Operations:
- Transformations: Lazy operations that return new RDDs (map, filter, flatMap, reduceByKey)
- Actions: Operations that trigger computation and return values (collect, count, reduce, saveAsTextFile)
When to Use RDDs:
- Low-level control over data distribution and partitioning
- Custom partitioning schemes required
- Working with unstructured data (text files, binary data)
- Migrating legacy code from early Spark versions
Prefer DataFrames/Datasets when possible - they provide automatic optimization via Catalyst optimizer.
DataFrames and Datasets
DataFrames are distributed collections of data organized into named columns - similar to a database table or pandas DataFrame, but with powerful optimizations.
DataFrames:
- Structured data with schema
- Automatic query optimization (Catalyst)
- Cross-language support (Python, Scala, Java, R)
- Rich API for SQL-like operations
Datasets (Scala/Java only):
- Typed DataFrames with compile-time type safety
- Best performance in Scala due to JVM optimization
- Combine RDD type safety with DataFrame optimizations
Key Advantages Over RDDs:
- Query Optimization: Catalyst optimizer rewrites queries for efficiency
- Tungsten Execution: Optimized CPU and memory usage
- Columnar Storage: Efficient data representation
- Code Generation: Compile-time bytecode generation for faster execution
Lazy Evaluation
Spark uses lazy evaluation to optimize execution:
1. Transformations build a Directed Acyclic Graph (DAG) of operations 2. Actions trigger execution of the DAG 3. Spark's optimizer analyzes the entire DAG and creates an optimized execution plan 4. Work is distributed across cluster nodes
Benefits:
- Minimize data movement across network
- Combine multiple operations into single stage
- Eliminate unnecessary computations
- Optimize memory usage
Partitioning
Data is divided into partitions for parallel processing:
- Default Partitioning: Typically based on HDFS block size or input source
- Hash Partitioning: Distribute data by key hash (used by groupByKey, reduceByKey)
- Range Partitioning: Distribute data by key ranges (useful for sorted data)
- Custom Partitioning: Define your own partitioning logic
Partition Count Considerations:
- Too few partitions: Underutilized cluster, large task execution time
- Too many partitions: Scheduling overhead, small task execution time
- General rule: 2-4 partitions per CPU core in cluster
- Use
repartition()orcoalesce()to adjust partition count
Caching and Persistence
Cache frequently accessed data in memory for performance:
# Cache DataFrame in memory
df.cache() # Shorthand for persist(StorageLevel.MEMORY_AND_DISK)
# Different storage levels
df.persist(StorageLevel.MEMORY_ONLY) # Fast but may lose data if evicted
df.persist(StorageLevel.MEMORY_AND_DISK) # Spill to disk if memory full
df.persist(StorageLevel.DISK_ONLY) # Store only on disk
df.persist(StorageLevel.MEMORY_ONLY_SER) # Serialized in memory (more compact)
# Unpersist when done
df.unpersist()When to Cache:
- Data used multiple times in workflow
- Iterative algorithms (ML training)
- Interactive analysis sessions
- Expensive transformations reused downstream
When Not to Cache:
- Data used only once
- Very large datasets that exceed cluster memory
- Streaming applications with continuous new data
Spark SQL
Spark SQL allows you to query structured data using SQL or DataFrame API:
- Execute SQL queries on DataFrames and tables
- Register DataFrames as temporary views
- Join structured and semi-structured data
- Connect to Hive metastore for table metadata
- Support for various data sources (Parquet, ORC, JSON, CSV, JDBC)
Performance Features:
- Catalyst Optimizer: Rule-based and cost-based query optimization
- Tungsten Execution Engine: Whole-stage code generation, vectorized processing
- Adaptive Query Execution (AQE): Runtime optimization based on statistics
- Dynamic Partition Pruning: Skip irrelevant partitions during execution
Broadcast Variables and Accumulators
Shared variables for efficient distributed computing:
Broadcast Variables:
- Read-only variables cached on each node
- Efficient for sharing large read-only data (lookup tables, ML models)
- Avoid sending large data with every task
# Broadcast a lookup table
lookup_table = {"key1": "value1", "key2": "value2"}
broadcast_lookup = sc.broadcast(lookup_table)
# Use in transformations
rdd.map(lambda x: broadcast_lookup.value.get(x, "default"))Accumulators:
- Write-only variables for aggregating values across tasks
- Used for counters and sums in distributed operations
- Only driver can read final accumulated value
# Create accumulator
error_count = sc.accumulator(0)
# Increment in tasks
rdd.foreach(lambda x: error_count.add(1) if is_error(x) else None)
# Read final value in driver
print(f"Total errors: {error_count.value}")Spark SQL Deep Dive
DataFrame Creation
Create DataFrames from various sources:
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("SparkSQLExample").getOrCreate()
# From structured data
data = [("Alice", 1), ("Bob", 2), ("Charlie", 3)]
columns = ["name", "id"]
df = spark.createDataFrame(data, columns)
# From files
df_json = spark.read.json("path/to/file.json")
df_parquet = spark.read.parquet("path/to/file.parquet")
df_csv = spark.read.option("header", "true").csv("path/to/file.csv")
# From JDBC sources
df_jdbc = spark.read \
.format("jdbc") \
.option("url", "jdbc:postgresql://host:port/database") \
.option("dbtable", "table_name") \
.option("user", "username") \
.option("password", "password") \
.load()DataFrame Operations
Common DataFrame transformations:
# Select columns
df.select("name", "age").show()
# Filter rows
df.filter(df.age > 21).show()
df.where(df["age"] > 21).show() # Alternative syntax
# Add/modify columns
from pyspark.sql.functions import col, lit
df.withColumn("age_plus_10", col("age") + 10).show()
df.withColumn("country", lit("USA")).show()
# Aggregations
df.groupBy("department").count().show()
df.groupBy("department").agg({"salary": "avg", "age": "max"}).show()
# Sorting
df.orderBy("age").show()
df.orderBy(col("age").desc()).show()
# Joins
df1.join(df2, df1.id == df2.user_id, "inner").show()
df1.join(df2, "id", "left_outer").show()
# Unions
df1.union(df2).show()SQL Queries
Execute SQL on DataFrames:
# Register DataFrame as temporary view
df.createOrReplaceTempView("people")
# Run SQL queries
sql_result = spark.sql("SELECT name FROM people WHERE age > 21")
sql_result.show()
# Complex queries
result = spark.sql("""
SELECT
department,
COUNT(*) as employee_count,
AVG(salary) as avg_salary,
MAX(age) as max_age
FROM people
WHERE age > 25
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY avg_salary DESC
""")
result.show()Data Sources
Spark SQL supports multiple data formats:
Parquet (Recommended for Analytics):
- Columnar storage format
- Excellent compression and query performance
- Schema embedded in file
- Supports predicate pushdown and column pruning
# Write
df.write.parquet("output/path", mode="overwrite", compression="snappy")
# Read with partition pruning
df = spark.read.parquet("output/path").filter(col("date") == "2025-01-01")ORC (Optimized Row Columnar):
- Similar to Parquet with slightly better compression
- Preferred for Hive integration
- Built-in indexes for faster queries
df.write.orc("output/path", mode="overwrite")
df = spark.read.orc("output/path")JSON (Semi-Structured Data):
- Human-readable but less efficient
- Schema inference on read
- Good for nested/complex data
# Read with explicit schema
from pyspark.sql.types import StructType, StructField, StringType, IntegerType
schema = StructType([
StructField("name", StringType(), True),
StructField("age", IntegerType(), True)
])
df = spark.read.schema(schema).json("data.json")CSV (Legacy/Simple Data):
- Widely compatible but slow
- Requires header inference or explicit schema
- Minimal compression benefits
df.write.csv("output.csv", header=True, mode="overwrite")
df = spark.read.option("header", "true").option("inferSchema", "true").csv("data.csv")Window Functions
Advanced analytics with window functions:
from pyspark.sql.window import Window
from pyspark.sql.functions import row_number, rank, dense_rank, lag, lead, sum, avg
# Define window specification
window_spec = Window.partitionBy("department").orderBy(col("salary").desc())
# Ranking functions
df.withColumn("rank", rank().over(window_spec)).show()
df.withColumn("row_num", row_number().over(window_spec)).show()
df.withColumn("dense_rank", dense_rank().over(window_spec)).show()
# Aggregate functions over window
df.withColumn("dept_avg_salary", avg("salary").over(window_spec)).show()
df.withColumn("running_total", sum("salary").over(window_spec.rowsBetween(Window.unboundedPreceding, Window.currentRow))).show()
# Offset functions
df.withColumn("prev_salary", lag("salary", 1).over(window_spec)).show()
df.withColumn("next_salary", lead("salary", 1).over(window_spec)).show()User-Defined Functions (UDFs)
Create custom transformations:
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType, IntegerType
# Python UDF (slower due to serialization overhead)
def categorize_age(age):
if age < 18:
return "Minor"
elif age < 65:
return "Adult"
else:
return "Senior"
categorize_udf = udf(categorize_age, StringType())
df.withColumn("age_category", categorize_udf(col("age"))).show()
# Pandas UDF (vectorized, faster for large datasets)
from pyspark.sql.functions import pandas_udf
import pandas as pd
@pandas_udf(IntegerType())
def square(series: pd.Series) -> pd.Series:
return series ** 2
df.withColumn("age_squared", square(col("age"))).show()UDF Performance Tips:
- Use built-in Spark functions when possible (always faster)
- Prefer Pandas UDFs over Python UDFs for better performance
- Use Scala UDFs for maximum performance (no serialization overhead)
- Cache DataFrames before applying UDFs if used multiple times
Transformations and Actions
Common Transformations
map: Apply function to each element
# RDD
rdd = sc.parallelize([1, 2, 3, 4, 5])
squared = rdd.map(lambda x: x * 2) # [2, 4, 6, 8, 10]
# DataFrame (use select with functions)
from pyspark.sql.functions import col
df.select(col("value") * 2).show()filter: Select elements matching predicate
# RDD
rdd.filter(lambda x: x > 2).collect() # [3, 4, 5]
# DataFrame
df.filter(col("age") > 25).show()flatMap: Map and flatten results
# RDD - Split text into words
lines = sc.parallelize(["hello world", "apache spark"])
words = lines.flatMap(lambda line: line.split(" ")) # ["hello", "world", "apache", "spark"]reduceByKey: Aggregate values by key
# Word count example
words = sc.parallelize(["apple", "banana", "apple", "cherry", "banana", "apple"])
word_pairs = words.map(lambda word: (word, 1))
word_counts = word_pairs.reduceByKey(lambda a, b: a + b)
# Result: [("apple", 3), ("banana", 2), ("cherry", 1)]groupByKey: Group values by key (avoid when possible - use reduceByKey instead)
# Less efficient than reduceByKey
word_pairs.groupByKey().mapValues(list).collect()
# Result: [("apple", [1, 1, 1]), ("banana", [1, 1]), ("cherry", [1])]join: Combine datasets by key
# RDD join
users = sc.parallelize([("user1", "Alice"), ("user2", "Bob")])
orders = sc.parallelize([("user1", 100), ("user2", 200), ("user1", 150)])
users.join(orders).collect()
# Result: [("user1", ("Alice", 100)), ("user1", ("Alice", 150)), ("user2", ("Bob", 200))]
# DataFrame join (more efficient)
df_users.join(df_orders, "user_id", "inner").show()distinct: Remove duplicates
# RDD
rdd.distinct().collect()
# DataFrame
df.distinct().show()
df.dropDuplicates(["user_id"]).show() # Drop based on specific columnscoalesce/repartition: Change partition count
# Reduce partitions (no shuffle, more efficient)
df.coalesce(1).write.parquet("output")
# Increase/decrease partitions (involves shuffle)
df.repartition(10).write.parquet("output")
df.repartition(10, "user_id").write.parquet("output") # Partition by columnCommon Actions
collect: Retrieve all data to driver
results = rdd.collect() # Returns list
# WARNING: Only use on small datasets that fit in driver memorycount: Count elements
total = df.count() # Number of rowsfirst/take: Get first N elements
first_elem = rdd.first()
first_five = rdd.take(5)reduce: Aggregate all elements
total_sum = rdd.reduce(lambda a, b: a + b)foreach: Execute function on each element
# Side effects only (no return value)
rdd.foreach(lambda x: print(x))saveAsTextFile: Write to file system
rdd.saveAsTextFile("hdfs://path/to/output")show: Display DataFrame rows (action)
df.show(20, truncate=False) # Show 20 rows, don't truncate columnsStructured Streaming
Process continuous data streams using DataFrame API.
Core Concepts
Streaming DataFrame:
- Unbounded table that grows continuously
- Same operations as batch DataFrames
- Micro-batch processing (default) or continuous processing
Input Sources:
- File sources (JSON, Parquet, CSV, ORC, text)
- Kafka
- Socket (for testing)
- Rate source (for testing)
- Custom sources
Output Modes:
- Append: Only new rows added to result table
- Complete: Entire result table written every trigger
- Update: Only updated rows written
Output Sinks:
- File sinks (Parquet, ORC, JSON, CSV, text)
- Kafka
- Console (for debugging)
- Memory (for testing)
- Foreach/ForeachBatch (custom logic)
Basic Streaming Example
from pyspark.sql import SparkSession
from pyspark.sql.functions import col
spark = SparkSession.builder.appName("StreamingExample").getOrCreate()
# Read stream from JSON files
input_stream = spark.readStream \
.format("json") \
.schema(schema) \
.option("maxFilesPerTrigger", 1) \
.load("input/directory")
# Transform streaming data
processed = input_stream \
.filter(col("value") > 10) \
.select("id", "value", "timestamp")
# Write stream to Parquet
query = processed.writeStream \
.format("parquet") \
.option("path", "output/directory") \
.option("checkpointLocation", "checkpoint/directory") \
.outputMode("append") \
.start()
# Wait for termination
query.awaitTermination()Stream-Static Joins
Join streaming data with static reference data:
# Static DataFrame (loaded once)
static_df = spark.read.parquet("reference/data")
# Streaming DataFrame
streaming_df = spark.readStream.format("kafka").load()
# Inner join (supported)
joined = streaming_df.join(static_df, "type")
# Left outer join (supported)
joined = streaming_df.join(static_df, "type", "left_outer")
# Write result
joined.writeStream \
.format("parquet") \
.option("path", "output") \
.option("checkpointLocation", "checkpoint") \
.start()Windowed Aggregations
Aggregate data over time windows:
from pyspark.sql.functions import window, col, count
# 10-minute tumbling window
windowed_counts = streaming_df \
.groupBy(
window(col("timestamp"), "10 minutes"),
col("word")
) \
.count()
# 10-minute sliding window with 5-minute slide
windowed_counts = streaming_df \
.groupBy(
window(col("timestamp"), "10 minutes", "5 minutes"),
col("word")
) \
.count()
# Write to console for debugging
query = windowed_counts.writeStream \
.outputMode("complete") \
.format("console") \
.option("truncate", "false") \
.start()Watermarking for Late Data
Handle late-arriving data with watermarks:
from pyspark.sql.functions import window
# Define watermark (10 minutes tolerance for late data)
windowed_counts = streaming_df \
.withWatermark("timestamp", "10 minutes") \
.groupBy(
window(col("timestamp"), "10 minutes"),
col("word")
) \
.count()
# Data arriving more than 10 minutes late will be droppedWatermark Benefits:
- Limit state size by dropping old aggregation state
- Handle late data within tolerance window
- Improve performance by not maintaining infinite state
Session Windows
Group events into sessions based on inactivity gaps:
from pyspark.sql.functions import session_window, when
# Dynamic session window based on user
session_window_spec = session_window(
col("timestamp"),
when(col("userId") == "user1", "5 seconds")
.when(col("userId") == "user2", "20 seconds")
.otherwise("5 minutes")
)
sessionized_counts = streaming_df \
.withWatermark("timestamp", "10 minutes") \
.groupBy(session_window_spec, col("userId")) \
.count()Stateful Stream Processing
Maintain state across micro-batches:
from pyspark.sql.functions import expr
# Deduplication using state
deduplicated = streaming_df \
.withWatermark("timestamp", "1 hour") \
.dropDuplicates(["user_id", "event_id"])
# Stream-stream joins (stateful)
stream1 = spark.readStream.format("kafka").option("subscribe", "topic1").load()
stream2 = spark.readStream.format("kafka").option("subscribe", "topic2").load()
joined = stream1 \
.withWatermark("timestamp", "10 minutes") \
.join(
stream2.withWatermark("timestamp", "20 minutes"),
expr("stream1.user_id = stream2.user_id AND stream1.timestamp >= stream2.timestamp AND stream1.timestamp <= stream2.timestamp + interval 15 minutes"),
"inner"
)Checkpointing
Ensure fault tolerance with checkpoints:
# Checkpoint location stores:
# - Stream metadata (offsets, configuration)
# - State information (for stateful operations)
# - Write-ahead logs
query = streaming_df.writeStream \
.format("parquet") \
.option("path", "output") \
.option("checkpointLocation", "checkpoint/dir") # REQUIRED for production \
.start()
# Recovery: Restart query with same checkpoint location
# Spark will resume from last committed offsetCheckpoint Best Practices:
- Always set checkpointLocation for production streams
- Use reliable distributed storage (HDFS, S3) for checkpoints
- Don't delete checkpoint directory while stream is running
- Back up checkpoints for disaster recovery
Machine Learning with MLlib
Spark's scalable machine learning library.
Core Components
MLlib Features:
- ML Pipelines: Chain transformations and models
- Featurization: Vector assemblers, scalers, encoders
- Classification & Regression: Linear models, tree-based models, neural networks
- Clustering: K-means, Gaussian Mixture, LDA
- Collaborative Filtering: ALS (Alternating Least Squares)
- Dimensionality Reduction: PCA, SVD
- Model Selection: Cross-validation, train-test split, parameter tuning
ML Pipelines
Chain transformations and estimators:
from pyspark.ml import Pipeline
from pyspark.ml.feature import VectorAssembler, StandardScaler
from pyspark.ml.classification import LogisticRegression
# Load data
df = spark.read.format("libsvm").load("data/sample_libsvm_data.txt")
# Define pipeline stages
assembler = VectorAssembler(
inputCols=["feature1", "feature2", "feature3"],
outputCol="features"
)
scaler = StandardScaler(
inputCol="features",
outputCol="scaled_features",
withStd=True,
withMean=True
)
lr = LogisticRegression(
featuresCol="scaled_features",
labelCol="label",
maxIter=10,
regParam=0.01
)
# Create pipeline
pipeline = Pipeline(stages=[assembler, scaler, lr])
# Split data
train_df, test_df = df.randomSplit([0.8, 0.2], seed=42)
# Train model
model = pipeline.fit(train_df)
# Make predictions
predictions = model.transform(test_df)
predictions.select("label", "prediction", "probability").show()Feature Engineering
Transform raw data into features:
from pyspark.ml.feature import StringIndexer, OneHotEncoder, VectorAssembler, MinMaxScaler
# Categorical encoding
indexer = StringIndexer(inputCol="category", outputCol="category_index")
encoder = OneHotEncoder(inputCol="category_index", outputCol="category_vec")
# Numerical scaling
scaler = MinMaxScaler(inputCol="features", outputCol="scaled_features")
# Assemble features
assembler = VectorAssembler(
inputCols=["category_vec", "numeric_feature1", "numeric_feature2"],
outputCol="features"
)
# Text processing
from pyspark.ml.feature import Tokenizer, HashingTF, IDF
tokenizer = Tokenizer(inputCol="text", outputCol="words")
hashing_tf = HashingTF(inputCol="words", outputCol="raw_features", numFeatures=10000)
idf = IDF(inputCol="raw_features", outputCol="features")Streaming Linear Regression
Train models on streaming data:
from pyspark.mllib.regression import LabeledPoint
from pyspark.streaming import StreamingContext
from pyspark.streaming.ml import StreamingLinearRegressionWithSGD
# Create StreamingContext
ssc = StreamingContext(sc, batchDuration=1)
# Define data streams
training_stream = ssc.textFileStream("training/data/path")
testing_stream = ssc.textFileStream("testing/data/path")
# Parse streams into LabeledPoint objects
def parse_point(line):
values = [float(x) for x in line.strip().split(',')]
return LabeledPoint(values[0], values[1:])
parsed_training = training_stream.map(parse_point)
parsed_testing = testing_stream.map(parse_point)
# Initialize model
num_features = 3
model = StreamingLinearRegressionWithSGD(initialWeights=[0.0] * num_features)
# Train and predict
model.trainOn(parsed_training)
predictions = model.predictOnValues(parsed_testing.map(lambda lp: (lp.label, lp.features)))
# Print predictions
predictions.pprint()
# Start streaming
ssc.start()
ssc.awaitTermination()Model Evaluation
Evaluate model performance:
from pyspark.ml.evaluation import BinaryClassificationEvaluator, MulticlassClassificationEvaluator, RegressionEvaluator
# Binary classification
binary_evaluator = BinaryClassificationEvaluator(
labelCol="label",
rawPredictionCol="rawPrediction",
metricName="areaUnderROC"
)
auc = binary_evaluator.evaluate(predictions)
print(f"AUC: {auc}")
# Multiclass classification
multi_evaluator = MulticlassClassificationEvaluator(
labelCol="label",
predictionCol="prediction",
metricName="accuracy"
)
accuracy = multi_evaluator.evaluate(predictions)
print(f"Accuracy: {accuracy}")
# Regression
regression_evaluator = RegressionEvaluator(
labelCol="label",
predictionCol="prediction",
metricName="rmse"
)
rmse = regression_evaluator.evaluate(predictions)
print(f"RMSE: {rmse}")Hyperparameter Tuning
Optimize model parameters with cross-validation:
from pyspark.ml.tuning import ParamGridBuilder, CrossValidator
from pyspark.ml.classification import RandomForestClassifier
from pyspark.ml.evaluation import MulticlassClassificationEvaluator
# Define model
rf = RandomForestClassifier(labelCol="label", featuresCol="features")
# Build parameter grid
param_grid = ParamGridBuilder() \
.addGrid(rf.numTrees, [10, 20, 50]) \
.addGrid(rf.maxDepth, [5, 10, 15]) \
.addGrid(rf.minInstancesPerNode, [1, 5, 10]) \
.build()
# Define evaluator
evaluator = MulticlassClassificationEvaluator(metricName="accuracy")
# Cross-validation
cv = CrossValidator(
estimator=rf,
estimatorParamMaps=param_grid,
evaluator=evaluator,
numFolds=5,
parallelism=4
)
# Train
cv_model = cv.fit(train_df)
# Best model
best_model = cv_model.bestModel
print(f"Best numTrees: {best_model.getNumTrees}")
print(f"Best maxDepth: {best_model.getMaxDepth()}")
# Evaluate on test set
predictions = cv_model.transform(test_df)
accuracy = evaluator.evaluate(predictions)
print(f"Test Accuracy: {accuracy}")Distributed Matrix Operations
MLlib provides distributed matrix representations:
from pyspark.mllib.linalg.distributed import RowMatrix, IndexedRowMatrix, CoordinateMatrix
from pyspark.mllib.linalg import Vectors
# RowMatrix: Distributed matrix without row indices
rows = sc.parallelize([
Vectors.dense([1.0, 2.0, 3.0]),
Vectors.dense([4.0, 5.0, 6.0]),
Vectors.dense([7.0, 8.0, 9.0])
])
row_matrix = RowMatrix(rows)
# Compute statistics
print(f"Rows: {row_matrix.numRows()}")
print(f"Cols: {row_matrix.numCols()}")
print(f"Column means: {row_matrix.computeColumnSummaryStatistics().mean()}")
# IndexedRowMatrix: Matrix with row indices
from pyspark.mllib.linalg.distributed import IndexedRow
indexed_rows = sc.parallelize([
IndexedRow(0, Vectors.dense([1.0, 2.0, 3.0])),
IndexedRow(1, Vectors.dense([4.0, 5.0, 6.0]))
])
indexed_matrix = IndexedRowMatrix(indexed_rows)
# CoordinateMatrix: Sparse matrix using (row, col, value) entries
from pyspark.mllib.linalg.distributed import MatrixEntry
entries = sc.parallelize([
MatrixEntry(0, 0, 1.0),
MatrixEntry(0, 2, 3.0),
MatrixEntry(1, 1, 5.0)
])
coord_matrix = CoordinateMatrix(entries)Stratified Sampling
Sample data while preserving class distribution:
# Scala/Java approach
data = [("a", 1), ("b", 2), ("a", 3), ("b", 4), ("a", 5), ("c", 6)]
rdd = sc.parallelize(data)
# Define sampling fractions per key
fractions = {"a": 0.5, "b": 0.5, "c": 0.5}
# Approximate sample (faster, one pass)
sampled_rdd = rdd.sampleByKey(withReplacement=False, fractions=fractions)
# Exact sample (slower, guaranteed exact counts)
exact_sampled = rdd.sampleByKeyExact(withReplacement=False, fractions=fractions)
print(sampled_rdd.collect())Performance Tuning
Memory Management
Memory Breakdown:
- Execution Memory: Used for shuffles, joins, sorts, aggregations
- Storage Memory: Used for caching and broadcast variables
- User Memory: Used for user data structures and UDFs
- Reserved Memory: Reserved for Spark internal operations
Configuration:
spark = SparkSession.builder \
.appName("MemoryTuning") \
.config("spark.executor.memory", "4g") \
.config("spark.driver.memory", "2g") \
.config("spark.memory.fraction", "0.6") # Fraction for execution + storage \
.config("spark.memory.storageFraction", "0.5") # Fraction of above for storage \
.getOrCreate()Memory Best Practices:
- Monitor memory usage via Spark UI
- Use appropriate storage levels for caching
- Avoid collecting large datasets to driver
- Increase executor memory for memory-intensive operations
- Use kryo serialization for better memory efficiency
Shuffle Optimization
Shuffles are expensive operations - minimize them:
Causes of Shuffles:
- groupByKey, reduceByKey, aggregateByKey
- join, cogroup
- repartition, coalesce (with increase)
- distinct, intersection
- sortByKey
Optimization Strategies:
# 1. Use reduceByKey instead of groupByKey
# Bad: groupByKey shuffles all data
word_pairs.groupByKey().mapValues(sum)
# Good: reduceByKey combines locally before shuffle
word_pairs.reduceByKey(lambda a, b: a + b)
# 2. Broadcast small tables in joins
from pyspark.sql.functions import broadcast
large_df.join(broadcast(small_df), "key")
# 3. Partition data appropriately
df.repartition(200, "user_id") # Partition by key for subsequent aggregations
# 4. Coalesce instead of repartition when reducing partitions
df.coalesce(10) # No shuffle, just merge partitions
# 5. Tune shuffle partitions
spark.conf.set("spark.sql.shuffle.partitions", 200) # Default is 200Shuffle Configuration:
spark = SparkSession.builder \
.config("spark.sql.shuffle.partitions", 200) \
.config("spark.default.parallelism", 200) \
.config("spark.sql.adaptive.enabled", "true") # Enable AQE \
.config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
.getOrCreate()Partitioning Strategies
Partition Count Guidelines:
- Too few: Underutilized cluster, OOM errors
- Too many: Task scheduling overhead
- Sweet spot: 2-4x number of CPU cores
- For large shuffles: 100-200+ partitions
Partition by Column:
# Partition writes by date for easy filtering
df.write.partitionBy("date", "country").parquet("output")
# Read with partition pruning (only reads relevant partitions)
spark.read.parquet("output").filter(col("date") == "2025-01-15").show()Custom Partitioning:
from pyspark.rdd import portable_hash
# Custom partitioner for RDD
def custom_partitioner(key):
return portable_hash(key) % 100
rdd.partitionBy(100, custom_partitioner)Caching Strategies
When to Cache:
# Iterative algorithms (ML)
training_data.cache()
for i in range(num_iterations):
model = train_model(training_data)
# Multiple aggregations on same data
base_df.cache()
result1 = base_df.groupBy("country").count()
result2 = base_df.groupBy("city").avg("sales")
# Interactive analysis
df.cache()
df.filter(condition1).show()
df.filter(condition2).show()
df.groupBy("category").count().show()Storage Levels:
from pyspark import StorageLevel
# Memory only (fastest, but may lose data)
df.persist(StorageLevel.MEMORY_ONLY)
# Memory and disk (spill to disk if needed)
df.persist(StorageLevel.MEMORY_AND_DISK)
# Serialized in memory (more compact, slower access)
df.persist(StorageLevel.MEMORY_ONLY_SER)
# Disk only (slowest, but always available)
df.persist(StorageLevel.DISK_ONLY)
# Replicated (fault tolerance)
df.persist(StorageLevel.MEMORY_AND_DISK_2) # 2 replicasBroadcast Joins
Optimize joins with small tables:
from pyspark.sql.functions import broadcast
# Automatic broadcast (tables < spark.sql.autoBroadcastJoinThreshold)
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", 10 * 1024 * 1024) # 10 MB
# Explicit broadcast hint
large_df.join(broadcast(small_df), "key")
# Benefits:
# - No shuffle of large table
# - Small table sent to all executors once
# - Much faster for small dimension tablesAdaptive Query Execution (AQE)
Enable runtime query optimization:
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true")
spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")
# AQE Benefits:
# - Dynamically coalesce partitions after shuffle
# - Handle skewed joins by splitting large partitions
# - Optimize join strategy at runtimeData Format Selection
Performance Comparison: 1. Parquet (Best for analytics): Columnar, compressed, fast queries 2. ORC (Best for Hive): Similar to Parquet, slightly better compression 3. Avro (Best for row-oriented): Good for write-heavy workloads 4. JSON (Slowest): Human-readable but inefficient 5. CSV (Legacy): Compatible but slow and no schema
Recommendation:
- Use Parquet for most analytics workloads
- Enable compression (snappy, gzip, lzo)
- Partition by commonly filtered columns
- Use columnar formats for read-heavy workloads
Catalyst Optimizer
Understand query optimization:
# View physical plan
df.explain(mode="extended")
# Optimizations include:
# - Predicate pushdown: Push filters to data source
# - Column pruning: Read only required columns
# - Constant folding: Evaluate constants at compile time
# - Join reordering: Optimize join order
# - Partition pruning: Skip irrelevant partitionsProduction Deployment
Cluster Managers
Standalone:
- Simple, built-in cluster manager
- Easy setup for development and small clusters
- No resource sharing with other frameworks
# Start master
$SPARK_HOME/sbin/start-master.sh
# Start workers
$SPARK_HOME/sbin/start-worker.sh spark://master:7077
# Submit application
spark-submit --master spark://master:7077 app.pyYARN:
- Hadoop's resource manager
- Share cluster resources with MapReduce, Hive, etc.
- Two modes: cluster (driver on YARN) and client (driver on local machine)
# Cluster mode (driver runs on YARN)
spark-submit --master yarn --deploy-mode cluster app.py
# Client mode (driver runs locally)
spark-submit --master yarn --deploy-mode client app.pyKubernetes:
- Modern container orchestration
- Dynamic resource allocation
- Cloud-native deployments
spark-submit \
--master k8s://https://k8s-master:443 \
--deploy-mode cluster \
--name spark-app \
--conf spark.executor.instances=5 \
--conf spark.kubernetes.container.image=spark:latest \
app.pyMesos:
- General-purpose cluster manager
- Fine-grained or coarse-grained resource sharing
Application Submission
Basic spark-submit:
spark-submit \
--master yarn \
--deploy-mode cluster \
--driver-memory 4g \
--executor-memory 8g \
--executor-cores 4 \
--num-executors 10 \
--conf spark.sql.shuffle.partitions=200 \
--py-files dependencies.zip \
--files config.json \
application.pyConfiguration Options:
--master: Cluster manager URL--deploy-mode: Where to run driver (client or cluster)--driver-memory: Memory for driver process--executor-memory: Memory per executor--executor-cores: Cores per executor--num-executors: Number of executors--conf: Spark configuration properties--py-files: Python dependencies--files: Additional files to distribute
Resource Allocation
General Guidelines:
- Driver Memory: 1-4 GB (unless collecting large results)
- Executor Memory: 4-16 GB per executor
- Executor Cores: 4-5 cores per executor (diminishing returns beyond 5)
- Number of Executors: Fill cluster capacity, leave resources for OS/other services
- Parallelism: 2-4x total cores
Example Calculations:
Cluster: 10 nodes, 32 cores each, 128 GB RAM each
Option 1: Many small executors
- 30 executors (3 per node)
- 10 cores per executor
- 40 GB memory per executor
- Total: 300 cores
Option 2: Fewer large executors (RECOMMENDED)
- 50 executors (5 per node)
- 5 cores per executor
- 24 GB memory per executor
- Total: 250 coresDynamic Allocation
Automatically scale executors based on workload:
spark = SparkSession.builder \
.appName("DynamicAllocation") \
.config("spark.dynamicAllocation.enabled", "true") \
.config("spark.dynamicAllocation.minExecutors", 2) \
.config("spark.dynamicAllocation.maxExecutors", 100) \
.config("spark.dynamicAllocation.initialExecutors", 10) \
.config("spark.dynamicAllocation.executorIdleTimeout", "60s") \
.getOrCreate()Benefits:
- Better resource utilization
- Automatic scaling for varying workloads
- Reduced costs in cloud environments
Monitoring and Logging
Spark UI:
- Web UI at http://driver:4040
- Stages, tasks, storage, environment, executors
- SQL query plans and execution details
- Identify bottlenecks and performance issues
History Server:
# Start history server
$SPARK_HOME/sbin/start-history-server.sh
# Configure event logging
spark.conf.set("spark.eventLog.enabled", "true")
spark.conf.set("spark.eventLog.dir", "hdfs://namenode/spark-logs")Metrics:
# Enable metrics collection
spark.conf.set("spark.metrics.conf.*.sink.console.class", "org.apache.spark.metrics.sink.ConsoleSink")
spark.conf.set("spark.metrics.conf.*.sink.console.period", 10)Logging:
# Configure log level
spark.sparkContext.setLogLevel("WARN") # ERROR, WARN, INFO, DEBUG
# Custom logging
import logging
logger = logging.getLogger(__name__)
logger.info("Custom log message")Fault Tolerance
Automatic Recovery:
- Task failures: Automatically retry failed tasks
- Executor failures: Reschedule tasks on other executors
- Driver failures: Restore from checkpoint (streaming)
- Node failures: Recompute lost partitions from lineage
Checkpointing:
# Set checkpoint directory
spark.sparkContext.setCheckpointDir("hdfs://namenode/checkpoints")
# Checkpoint RDD (breaks lineage for very long chains)
rdd.checkpoint()
# Streaming checkpoint (required for production)
query = streaming_df.writeStream \
.option("checkpointLocation", "hdfs://namenode/streaming-checkpoint") \
.start()Speculative Execution:
# Enable speculative execution for slow tasks
spark.conf.set("spark.speculation", "true")
spark.conf.set("spark.speculation.multiplier", 1.5)
spark.conf.set("spark.speculation.quantile", 0.75)Data Locality
Optimize data placement for performance:
Locality Levels: 1. PROCESS_LOCAL: Data in same JVM as task (fastest) 2. NODE_LOCAL: Data on same node, different process 3. RACK_LOCAL: Data on same rack 4. ANY: Data on different rack (slowest)
Improve Locality:
# Increase locality wait time
spark.conf.set("spark.locality.wait", "10s")
spark.conf.set("spark.locality.wait.node", "5s")
spark.conf.set("spark.locality.wait.rack", "3s")
# Partition data to match cluster topology
df.repartition(num_nodes * cores_per_node)Best Practices
Code Organization
1. Modular Design: Separate data loading, transformation, and output logic 2. Configuration Management: Externalize configuration (use config files) 3. Error Handling: Implement robust error handling and logging 4. Testing: Unit test transformations, integration test pipelines 5. Documentation: Document complex transformations and business logic
Performance
1. Avoid Shuffles: Use reduceByKey instead of groupByKey 2. Cache Wisely: Only cache data reused multiple times 3. Broadcast Small Tables: Use broadcast joins for small reference data 4. Partition Appropriately: 2-4x CPU cores, partition by frequently filtered columns 5. Use Parquet: Columnar format for analytical workloads 6. Enable AQE: Leverage adaptive query execution for runtime optimization 7. Tune Memory: Balance executor memory and cores 8. Monitor: Use Spark UI to identify bottlenecks
Development Workflow
1. Start Small: Develop with sample data locally 2. Profile Early: Monitor performance from the start 3. Iterate: Optimize incrementally based on metrics 4. Test at Scale: Validate with production-sized data before deployment 5. Version Control: Track code, configurations, and schemas
Data Quality
1. Schema Validation: Enforce schemas on read/write 2. Null Handling: Explicitly handle null values 3. Data Validation: Check for expected ranges, formats, constraints 4. Deduplication: Remove duplicates based on business logic 5. Audit Logging: Track data lineage and transformations
Security
1. Authentication: Enable Kerberos for YARN/HDFS 2. Authorization: Use ACLs for data access control 3. Encryption: Encrypt data at rest and in transit 4. Secrets Management: Use secure credential providers 5. Audit Trails: Log data access and modifications
Cost Optimization
1. Right-Size Resources: Don't over-provision executors 2. Dynamic Allocation: Scale executors based on workload 3. Spot Instances: Use spot/preemptible instances in cloud 4. Data Compression: Use efficient formats (Parquet, ORC) 5. Partitioning: Prune unnecessary data reads 6. Auto-Shutdown: Terminate idle clusters
Common Patterns
ETL Pipeline Pattern
def etl_pipeline(spark, input_path, output_path):
# Extract
raw_df = spark.read.parquet(input_path)
# Transform
cleaned_df = raw_df \
.dropDuplicates(["id"]) \
.filter(col("value").isNotNull()) \
.withColumn("processed_date", current_date())
# Enrich
enriched_df = cleaned_df.join(broadcast(reference_df), "key")
# Aggregate
aggregated_df = enriched_df \
.groupBy("category", "date") \
.agg(
count("*").alias("count"),
sum("amount").alias("total_amount"),
avg("value").alias("avg_value")
)
# Load
aggregated_df.write \
.partitionBy("date") \
.mode("overwrite") \
.parquet(output_path)Incremental Processing Pattern
def incremental_process(spark, input_path, output_path, checkpoint_path):
# Read last processed timestamp
last_timestamp = read_checkpoint(checkpoint_path)
# Read new data
new_data = spark.read.parquet(input_path) \
.filter(col("timestamp") > last_timestamp)
# Process
processed = transform(new_data)
# Write
processed.write.mode("append").parquet(output_path)
# Update checkpoint
max_timestamp = new_data.agg(max("timestamp")).collect()[0][0]
write_checkpoint(checkpoint_path, max_timestamp)Slowly Changing Dimension (SCD) Pattern
def scd_type2_upsert(spark, dimension_df, updates_df):
# Mark existing records as inactive if updated
inactive_records = dimension_df \
.join(updates_df, "business_key") \
.select(
dimension_df["*"],
lit(False).alias("is_active"),
current_date().alias("end_date")
)
# Add new records
new_records = updates_df \
.withColumn("is_active", lit(True)) \
.withColumn("start_date", current_date()) \
.withColumn("end_date", lit(None))
# Union unchanged, inactive, and new records
result = dimension_df \
.join(updates_df, "business_key", "left_anti") \
.union(inactive_records) \
.union(new_records)
return resultWindow Analytics Pattern
def calculate_running_metrics(df):
from pyspark.sql.window import Window
from pyspark.sql.functions import row_number, lag, sum, avg
# Define window
window_spec = Window.partitionBy("user_id").orderBy("timestamp")
# Calculate metrics
result = df \
.withColumn("row_num", row_number().over(window_spec)) \
.withColumn("prev_value", lag("value", 1).over(window_spec)) \
.withColumn("running_total", sum("value").over(window_spec.rowsBetween(Window.unboundedPreceding, Window.currentRow))) \
.withColumn("moving_avg", avg("value").over(window_spec.rowsBetween(-2, 0)))
return resultTroubleshooting
Out of Memory Errors
Symptoms:
java.lang.OutOfMemoryError- Executor failures
- Slow garbage collection
Solutions:
# Increase executor memory
spark.conf.set("spark.executor.memory", "8g")
# Increase driver memory (if collecting data)
spark.conf.set("spark.driver.memory", "4g")
# Reduce memory pressure
df.persist(StorageLevel.MEMORY_AND_DISK) # Spill to disk
df.coalesce(100) # Reduce partition count
spark.conf.set("spark.sql.shuffle.partitions", 400) # Increase shuffle partitions
# Avoid collect() on large datasets
# Use take() or limit() instead
df.take(100)Shuffle Performance Issues
Symptoms:
- Long shuffle read/write times
- Skewed partition sizes
- Task stragglers
Solutions:
# Increase shuffle partitions
spark.conf.set("spark.sql.shuffle.partitions", 400)
# Handle skew with salting
df_salted = df.withColumn("salt", (rand() * 10).cast("int"))
result = df_salted.groupBy("key", "salt").agg(...)
# Use broadcast for small tables
large_df.join(broadcast(small_df), "key")
# Enable AQE for automatic optimization
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")Streaming Job Failures
Symptoms:
- Streaming query stopped
- Checkpoint corruption
- Processing lag increasing
Solutions:
# Increase executor memory for stateful operations
spark.conf.set("spark.executor.memory", "8g")
# Tune watermark for late data
.withWatermark("timestamp", "15 minutes")
# Increase trigger interval to reduce micro-batch overhead
.trigger(processingTime="30 seconds")
# Monitor lag and adjust parallelism
spark.conf.set("spark.sql.shuffle.partitions", 200)
# Recover from checkpoint corruption
# Delete checkpoint directory and restart (data loss possible)
# Or implement custom state recovery logicData Skew
Symptoms:
- Few tasks take much longer than others
- Unbalanced partition sizes
- Executor OOM errors
Solutions:
# 1. Salting technique (add random prefix to keys)
from pyspark.sql.functions import concat, lit, rand
df_salted = df.withColumn("salted_key", concat(col("key"), lit("_"), (rand() * 10).cast("int")))
result = df_salted.groupBy("salted_key").agg(...)
# 2. Repartition by skewed column
df.repartition(200, "skewed_column")
# 3. Isolate skewed keys
skewed_keys = df.groupBy("key").count().filter(col("count") > threshold).select("key")
skewed_df = df.join(broadcast(skewed_keys), "key")
normal_df = df.join(broadcast(skewed_keys), "key", "left_anti")
# Process separately
skewed_result = process_with_salting(skewed_df)
normal_result = process_normally(normal_df)
final = skewed_result.union(normal_result)Context7 Code Integration
This skill integrates real-world code examples from Apache Spark's official repository. All code snippets in the EXAMPLES.md file are sourced from Context7's Apache Spark library documentation, ensuring production-ready patterns and best practices.
Version and Compatibility
- Apache Spark Version: 3.x (compatible with 2.4+)
- Python: 3.7+
- Scala: 2.12+
- Java: 8+
- R: 3.5+
References
- Official Documentation: https://spark.apache.org/docs/latest/
- API Reference: https://spark.apache.org/docs/latest/api.html
- GitHub Repository: https://github.com/apache/spark
- Databricks Blog: https://databricks.com/blog
- Context7 Library: /apache/spark
---
Skill Version: 1.0.0 Last Updated: October 2025 Skill Category: Big Data, Distributed Computing, Data Engineering, Machine Learning Context7 Integration: /apache/spark with 8000 tokens of documentation
Apache Spark Data Processing - Production Examples
This file contains 20+ production-ready Apache Spark examples sourced from the official Apache Spark repository via Context7. All examples demonstrate real-world patterns and best practices.
Table of Contents
1. Word Count with DataFrame SQL - Python 2. Word Count with DataFrame SQL - Scala 3. Word Count with DataFrame SQL - Java 4. Stream-Static Joins 5. RDD Transformations and Actions 6. DataFrame Creation and Operations 7. Windowed Aggregations in Streaming 8. Session Windows for User Sessions 9. Streaming Linear Regression - Python 10. Streaming Linear Regression - Scala 11. Stratified Sampling - Python 12. Stratified Sampling - Scala 13. Stratified Sampling - Java 14. ML Pipeline with Feature Engineering 15. Parquet Performance Optimization 16. ORC Performance Optimization 17. Broadcast Exchange for Joins 18. Reused Exchange for Query Optimization 19. Hash Aggregation for Final Sum 20. Chained Time Window Aggregations 21. Distributed Matrix Operations 22. Date and Time Operations
---
1. Word Count with DataFrame SQL - Python
Source: Apache Spark Streaming Programming Guide (Context7: /apache/spark)
Use Case: Convert streaming RDD of strings to DataFrame, perform SQL word count
Pattern: Streaming data processing with SQL transformations
from pyspark.sql import Row, SparkSession
def getSparkSessionInstance(sparkConf):
"""Get or create singleton SparkSession"""
if ('sparkSessionSingletonInstance' not in globals()):
globals()['sparkSessionSingletonInstance'] = SparkSession \
.builder \
.config(conf=sparkConf) \
.getOrCreate()
return globals()['sparkSessionSingletonInstance']
# DStream of strings
words = ... # DStream of words
def process(time, rdd):
print("========= %s =========" % str(time))
try:
# Get the singleton instance of SparkSession
spark = getSparkSessionInstance(rdd.context.getConf())
# Convert RDD[String] to RDD[Row] to DataFrame
rowRdd = rdd.map(lambda w: Row(word=w))
wordsDataFrame = spark.createDataFrame(rowRdd)
# Creates a temporary view using the DataFrame
wordsDataFrame.createOrReplaceTempView("words")
# Do word count on table using SQL and print it
wordCountsDataFrame = spark.sql("select word, count(*) as total from words group by word")
wordCountsDataFrame.show()
except Exception as e:
print(f"Error processing batch: {e}")
pass
words.foreachRDD(process)Key Concepts:
- Singleton SparkSession pattern for streaming
- RDD to DataFrame conversion
- Temporary view registration for SQL queries
- Error handling in streaming contexts
Performance Tips:
- Reuse SparkSession across micro-batches (singleton pattern)
- Use DataFrame API for automatic optimization
- Consider caching if same transformations repeat
---
2. Word Count with DataFrame SQL - Scala
Source: Apache Spark Streaming Programming Guide (Context7: /apache/spark)
Use Case: Scala implementation of streaming word count with SQL
Pattern: Type-safe streaming with Scala implicits
import org.apache.spark.sql.SparkSession
val words: DStream[String] = ...
words.foreachRDD { rdd =>
// Get the singleton instance of SparkSession
val spark = SparkSession.builder.config(rdd.sparkContext.getConf).getOrCreate()
import spark.implicits._
// Convert RDD[String] to DataFrame
val wordsDataFrame = rdd.toDF("word")
// Create a temporary view
wordsDataFrame.createOrReplaceTempView("words")
// Do word count on DataFrame using SQL and print it
val wordCountsDataFrame =
spark.sql("select word, count(*) as total from words group by word")
wordCountsDataFrame.show()
}Key Concepts:
- Scala implicits for automatic RDD to DataFrame conversion
- Type-safe transformations with Scala
- Efficient integration with Spark SQL
Performance Tips:
- Use
.toDF()for automatic schema inference - Leverage Catalyst optimizer through SQL
- Cache wordCountsDataFrame if reused across iterations
---
3. Word Count with DataFrame SQL - Java
Source: Apache Spark Streaming Programming Guide (Context7: /apache/spark)
Use Case: Java implementation with Java Bean for schema definition
Pattern: Java Bean pattern for DataFrame schema
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.streaming.api.java.JavaDStream;
/** Java Bean class for converting RDD to DataFrame */
public class JavaRow implements java.io.Serializable {
private String word;
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
}
// Streaming setup
JavaDStream<String> words = ...
words.foreachRDD((rdd, time) -> {
// Get the singleton instance of SparkSession
SparkSession spark = SparkSession.builder().config(rdd.sparkContext().getConf()).getOrCreate();
// Convert RDD[String] to RDD[JavaRow] to DataFrame
JavaRDD<JavaRow> rowRDD = rdd.map(word -> {
JavaRow record = new JavaRow();
record.setWord(word);
return record;
});
Dataset<Row> wordsDataFrame = spark.createDataFrame(rowRDD, JavaRow.class);
// Creates a temporary view using the DataFrame
wordsDataFrame.createOrReplaceTempView("words");
// Do word count on table using SQL and print it
Dataset<Row> wordCountsDataFrame =
spark.sql("select word, count(*) as total from words group by word");
wordCountsDataFrame.show();
});Key Concepts:
- Java Bean pattern for schema definition
- Serializable classes for distributed processing
- Lambda expressions for cleaner code
Java-Specific Considerations:
- Implement Serializable for all custom classes
- Use Dataset<Row> instead of DataFrame
- Handle checked exceptions appropriately
---
4. Stream-Static Joins
Source: Apache Spark Structured Streaming Guide (Context7: /apache/spark)
Use Case: Join streaming data with static reference tables
Pattern: Enrichment pattern for streaming data
Python
# Static DataFrame (loaded once)
staticDf = spark.read.parquet("reference/data")
# Streaming DataFrame
streamingDf = spark.readStream.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "input-topic") \
.load()
# Inner equi-join with a static DF
enriched = streamingDf.join(staticDf, "type")
# Left outer join with a static DF
enriched_left = streamingDf.join(staticDf, "type", "left_outer")
# Write enriched stream
query = enriched.writeStream \
.format("parquet") \
.option("path", "output/enriched") \
.option("checkpointLocation", "checkpoint/enriched") \
.start()Scala
val staticDf = spark.read.parquet("reference/data")
val streamingDf = spark.readStream.format("kafka").load()
// Inner equi-join with a static DF
val enriched = streamingDf.join(staticDf, "type")
// Left outer join with a static DF
val enrichedLeft = streamingDf.join(staticDf, "type", "left_outer")Java
Dataset<Row> staticDf = spark.read().parquet("reference/data");
Dataset<Row> streamingDf = spark.readStream().format("kafka").load();
// Inner equi-join with a static DF
Dataset<Row> enriched = streamingDf.join(staticDf, "type");
// Left outer join with a static DF
Dataset<Row> enrichedLeft = streamingDf.join(staticDf, "type", "left_outer");Key Concepts:
- Stream-static joins are not stateful (efficient)
- Static data loaded once, not for every micro-batch
- Supports inner and left outer joins
Use Cases:
- Enrich events with user profiles
- Add product information to transactions
- Augment with geo-location data
- Add configuration or mapping data
Performance Tips:
- Broadcast static DataFrame if small (<10 MB)
- Reload static data periodically for updates
- Use left outer join if not all stream records have matches
---
5. RDD Transformations and Actions
Source: Apache Spark Core Documentation (Context7: /apache/spark)
Use Case: Fundamental RDD operations for distributed data processing
Pattern: Low-level distributed computing with RDDs
from pyspark import SparkContext, SparkConf
# Create SparkContext
conf = SparkConf().setAppName("RDDExample").setMaster("local[*]")
sc = SparkContext(conf=conf)
# Example 1: Creating an RDD
data = [1, 2, 3, 4, 5]
rdd = sc.parallelize(data)
# Example 2: Map transformation
result = rdd.map(lambda x: x * 2).collect()
print(f"Doubled: {result}") # [2, 4, 6, 8, 10]
# Example 3: Filter transformation
filtered = rdd.filter(lambda x: x % 2 == 0).collect()
print(f"Even numbers: {filtered}") # [2, 4]
# Example 4: FlatMap transformation
lines = sc.parallelize(["hello world", "apache spark"])
words = lines.flatMap(lambda line: line.split(" ")).collect()
print(f"Words: {words}") # ["hello", "world", "apache", "spark"]
# Example 5: ReduceByKey for aggregation
word_pairs = sc.parallelize([("apple", 1), ("banana", 1), ("apple", 1), ("cherry", 1)])
word_counts = word_pairs.reduceByKey(lambda a, b: a + b).collect()
print(f"Word counts: {word_counts}") # [("apple", 2), ("banana", 1), ("cherry", 1)]
# Example 6: Join two RDDs
users = sc.parallelize([("user1", "Alice"), ("user2", "Bob")])
orders = sc.parallelize([("user1", 100), ("user2", 200), ("user1", 150)])
joined = users.join(orders).collect()
print(f"Joined: {joined}")
# [("user1", ("Alice", 100)), ("user1", ("Alice", 150)), ("user2", ("Bob", 200))]
# Example 7: Distinct elements
duplicates = sc.parallelize([1, 2, 2, 3, 3, 3, 4])
unique = duplicates.distinct().collect()
print(f"Unique: {unique}") # [1, 2, 3, 4]
# Example 8: Count action
count = rdd.count()
print(f"Count: {count}") # 5
# Example 9: Reduce action
total_sum = rdd.reduce(lambda a, b: a + b)
print(f"Sum: {total_sum}") # 15
# Example 10: Take first N elements
first_three = rdd.take(3)
print(f"First 3: {first_three}") # [1, 2, 3]
sc.stop()Key Concepts:
- RDDs are immutable, distributed collections
- Transformations are lazy (build DAG)
- Actions trigger computation
- Lineage tracking for fault tolerance
When to Use RDDs:
- Low-level control over data and partitioning
- Custom partitioning logic required
- Unstructured data (text, binary)
- Legacy code migration
Performance Considerations:
- Prefer DataFrames/Datasets for structured data
- Use
reduceByKeyinstead ofgroupByKeyto minimize shuffle - Cache RDDs that are reused multiple times
---
6. DataFrame Creation and Operations
Source: Apache Spark SQL Guide (Context7: /apache/spark)
Use Case: Structured data processing with DataFrames
Pattern: Declarative data manipulation with automatic optimization
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, avg, count, sum, max
spark = SparkSession.builder.appName("DataFrameExample").getOrCreate()
# Example 1: Create DataFrame from data
data = [("Alice", 1, 28), ("Bob", 2, 35), ("Charlie", 3, 42)]
columns = ["name", "id", "age"]
df = spark.createDataFrame(data, columns)
df.show()
# Example 2: Read from JSON
df_json = spark.read.json("data.json")
df_json.printSchema()
# Example 3: Read from Parquet
df_parquet = spark.read.parquet("data.parquet")
# Example 4: Read from CSV with options
df_csv = spark.read \
.option("header", "true") \
.option("inferSchema", "true") \
.csv("data.csv")
# Example 5: Select columns
df.select("name", "age").show()
df.select(col("name"), col("age") + 10).show()
# Example 6: Filter rows
df.filter(df.age > 30).show()
df.where(col("age") > 30).show() # Alternative syntax
# Example 7: Register temporary view and run SQL
df.createOrReplaceTempView("people")
sql_result = spark.sql("SELECT name FROM people WHERE age > 25")
sql_result.show()
# Example 8: Complex SQL with aggregations
employees = spark.createDataFrame([
("Alice", "Engineering", 100000),
("Bob", "Sales", 80000),
("Charlie", "Engineering", 120000),
("Diana", "Sales", 90000)
], ["name", "department", "salary"])
employees.createOrReplaceTempView("employees")
result = spark.sql("""
SELECT
department,
COUNT(*) as employee_count,
AVG(salary) as avg_salary,
MAX(salary) as max_salary
FROM employees
GROUP BY department
ORDER BY avg_salary DESC
""")
result.show()
# Example 9: DataFrame API aggregations
dept_stats = employees.groupBy("department").agg(
count("*").alias("count"),
avg("salary").alias("avg_salary"),
max("salary").alias("max_salary")
)
dept_stats.show()
# Example 10: Join DataFrames
users = spark.createDataFrame([
(1, "Alice", "Engineering"),
(2, "Bob", "Sales")
], ["id", "name", "department"])
salaries = spark.createDataFrame([
(1, 100000),
(2, 80000)
], ["user_id", "salary"])
joined = users.join(salaries, users.id == salaries.user_id, "inner")
joined.show()
# Example 11: Add/modify columns
from pyspark.sql.functions import lit, when
df_with_country = df.withColumn("country", lit("USA"))
df_with_category = df.withColumn("age_category",
when(col("age") < 30, "Young")
.when(col("age") < 40, "Middle")
.otherwise("Senior")
)
df_with_category.show()
# Example 12: Write to various formats
df.write.parquet("output/parquet", mode="overwrite")
df.write.json("output/json", mode="overwrite")
df.write.csv("output/csv", header=True, mode="overwrite")
spark.stop()Key Concepts:
- DataFrames provide structured data abstraction
- Catalyst optimizer automatically optimizes queries
- Support for SQL and programmatic API
- Schema enforcement and type safety
Advantages Over RDDs:
- Automatic query optimization
- Better memory management (Tungsten)
- Cross-language support
- Rich API for common operations
Best Practices:
- Use Parquet for columnar storage and compression
- Define explicit schemas for better performance
- Cache DataFrames that are reused
- Use SQL for complex queries, DataFrame API for programmatic logic
---
7. Windowed Aggregations in Streaming
Source: Apache Spark Structured Streaming Guide (Context7: /apache/spark)
Use Case: Time-based aggregations on streaming data
Pattern: Tumbling and sliding windows for real-time analytics
Python
from pyspark.sql.functions import window, col, count
# Streaming DataFrame with timestamp and word columns
words = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "words-topic") \
.load()
# Parse JSON and extract timestamp and word
parsed = words.selectExpr("CAST(value AS STRING)") \
.select(from_json(col("value"), schema).alias("data")) \
.select("data.timestamp", "data.word")
# Example 1: 10-minute tumbling window
tumbling_counts = parsed \
.groupBy(
window(col("timestamp"), "10 minutes"),
col("word")
) \
.count()
# Example 2: 10-minute sliding window with 5-minute slide
sliding_counts = parsed \
.groupBy(
window(col("timestamp"), "10 minutes", "5 minutes"),
col("word")
) \
.count()
# Write to console
query = sliding_counts.writeStream \
.outputMode("complete") \
.format("console") \
.option("truncate", "false") \
.start()
query.awaitTermination()Scala
import spark.implicits._
import org.apache.spark.sql.functions.{window, col}
val words = spark.readStream.format("kafka").load()
// 10-minute tumbling window
val tumblingCounts = words.groupBy(
window($"timestamp", "10 minutes"),
$"word"
).count()
// 10-minute sliding window with 5-minute slide
val slidingCounts = words.groupBy(
window($"timestamp", "10 minutes", "5 minutes"),
$"word"
).count()Java
import static org.apache.spark.sql.functions.*;
Dataset<Row> words = spark.readStream().format("kafka").load();
// 10-minute tumbling window
Dataset<Row> tumblingCounts = words.groupBy(
window(col("timestamp"), "10 minutes"),
col("word")
).count();
// 10-minute sliding window with 5-minute slide
Dataset<Row> slidingCounts = words.groupBy(
window(col("timestamp"), "10 minutes", "5 minutes"),
col("word")
).count();Key Concepts:
- Tumbling windows: Non-overlapping, fixed-size intervals
- Sliding windows: Overlapping intervals with configurable slide
- Late data handling with watermarks
- Stateful aggregations
Window Types:
- Tumbling:
window("10 minutes")- Non-overlapping 10-minute windows - Sliding:
window("10 minutes", "5 minutes")- 10-minute windows every 5 minutes - Session: Dynamic windows based on inactivity gaps
Performance Tips:
- Use watermarks to limit state size
- Choose appropriate window and slide durations
- Consider outputMode (complete, update, append)
---
8. Session Windows for User Sessions
Source: Apache Spark Structured Streaming Guide (Context7: /apache/spark)
Use Case: Group events into sessions based on inactivity gaps
Pattern: Dynamic session windows with user-specific timeouts
Python
from pyspark.sql.functions import session_window, when, col
# Streaming DataFrame of events
events = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "events") \
.load()
# Define dynamic session window based on userId
sessionWindow = session_window(
col("timestamp"),
when(col("userId") == "user1", "5 seconds")
.when(col("userId") == "user2", "20 seconds")
.otherwise("5 minutes")
)
# Group by session window and userId, compute count
sessionizedCounts = events \
.withWatermark("timestamp", "10 minutes") \
.groupBy(sessionWindow, col("userId")) \
.count()
# Write results
query = sessionizedCounts.writeStream \
.format("console") \
.outputMode("update") \
.start()
query.awaitTermination()Scala
import spark.implicits._
import org.apache.spark.sql.functions.{session_window, when, col}
val events = spark.readStream.format("kafka").load()
val sessionWindow = session_window($"timestamp",
when($"userId" === "user1", "5 seconds")
.when($"userId" === "user2", "20 seconds")
.otherwise("5 minutes")
)
val sessionizedCounts = events
.withWatermark("timestamp", "10 minutes")
.groupBy(sessionWindow, $"userId")
.count()Java
import static org.apache.spark.sql.functions.*;
Dataset<Row> events = spark.readStream().format("kafka").load();
Column sessionWindow = session_window(
col("timestamp"),
when(col("userId").equalTo("user1"), "5 seconds")
.when(col("userId").equalTo("user2"), "20 seconds")
.otherwise("5 minutes")
);
Dataset<Row> sessionizedCounts = events
.withWatermark("timestamp", "10 minutes")
.groupBy(sessionWindow, col("userId"))
.count();Key Concepts:
- Session windows group events separated by inactivity gaps
- Dynamic session duration based on attributes (userId)
- Watermarks required to limit state growth
- Use for user behavior analysis
Use Cases:
- Web analytics (user sessions on website)
- Gaming analytics (gaming sessions)
- IoT device sessions
- User engagement metrics
Configuration:
- Gap duration: Inactivity period before new session starts
- Watermark: How late can data arrive and still be processed
- Per-user customization: Different gaps for different users
---
9. Streaming Linear Regression - Python
Source: Apache Spark MLlib Guide (Context7: /apache/spark)
Use Case: Train linear regression model on streaming data
Pattern: Online learning with continuous model updates
from pyspark.mllib.regression import LabeledPoint
from pyspark.streaming import StreamingContext
from pyspark.streaming.ml import StreamingLinearRegressionWithSGD
import sys
# Assumes a StreamingContext 'ssc' has already been created
# ssc = StreamingContext(sc, batchDuration=1)
# Define the paths for training and testing data directories
training_data_path = sys.argv[1]
testing_data_path = sys.argv[2]
# Create DStreams for training and testing data
training_stream = ssc.textFileStream(training_data_path)
testing_stream = ssc.textFileStream(testing_data_path)
# Parse the streams into LabeledPoint objects
# Format: y,[x1,x2,x3]
def parse_point(line):
values = [float(x) for x in line.strip().replace('[', '').replace(']', '').split(',')]
return LabeledPoint(values[0], values[1:])
parsed_training_stream = training_stream.map(parse_point)
parsed_testing_stream = testing_stream.map(parse_point)
# Initialize the StreamingLinearRegressionWithSGD model
# Set initial weights to 0
num_features = 3
model = StreamingLinearRegressionWithSGD(initialWeights=[0.0] * num_features)
# Configure model parameters
model.setInitialWeights([0.0] * num_features)
model.setStepSize(0.01) # Learning rate
model.setNumIterations(50)
# Register the streams for training and testing
model.trainOn(parsed_training_stream)
# Predict on testing stream
predictions = model.predictOnValues(
parsed_testing_stream.map(lambda lp: (lp.label, lp.features))
)
# Print predictions (label, predicted value)
predictions.pprint()
# Start the streaming context
# ssc.start()
# ssc.awaitTermination()Key Concepts:
- Online learning: Model updates with each micro-batch
- Streaming SGD: Stochastic gradient descent on streaming data
- LabeledPoint: (label, features) representation
- Continuous model improvement
Use Cases:
- Real-time price prediction
- Continuous sensor calibration
- Adaptive forecasting
- Online recommendation systems
Configuration:
initialWeights: Starting model parametersstepSize: Learning rate (0.001-0.1 typical)numIterations: Iterations per micro-batchminiBatchFraction: Fraction of data per iteration
Performance Tips:
- Tune learning rate for convergence
- Monitor prediction error over time
- Checkpoint model periodically
- Use feature scaling for faster convergence
---
10. Streaming Linear Regression - Scala
Source: Apache Spark MLlib Guide (Context7: /apache/spark)
Use Case: Scala implementation of streaming linear regression
Pattern: Type-safe streaming ML with Scala
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.streaming.ml.StreamingLinearRegressionWithSGD
// Assumes a StreamingContext 'ssc' has already been created
// val ssc = new StreamingContext(sc, Seconds(1))
// Define the paths for training and testing data directories
val trainingDataPath = args(0)
val testingDataPath = args(1)
// Create DStreams for training and testing data
val trainingStream: DStream[String] = ssc.textFileStream(trainingDataPath)
val testingStream: DStream[String] = ssc.textFileStream(testingDataPath)
// Function to parse lines into LabeledPoint objects
// Format: y,[x1,x2,x3]
def parsePoint(line: String): LabeledPoint = {
val values = line.split(',').map(_.trim)
val label = values(0).toDouble
val features = Vectors.dense(
values(1).stripPrefix("[").stripSuffix("]")
.split(",")
.map(_.toDouble)
)
LabeledPoint(label, features)
}
val parsedTrainingStream: DStream[LabeledPoint] = trainingStream.map(parsePoint)
val parsedTestingStream: DStream[LabeledPoint] = testingStream.map(parsePoint)
// Initialize the StreamingLinearRegressionWithSGD model
val numFeatures = 3
val model = new StreamingLinearRegressionWithSGD()
.setInitialWeights(Vectors.dense(Array.fill(numFeatures)(0.0)))
.setStepSize(0.01)
.setNumIterations(50)
// Register the streams for training and testing
model.trainOn(parsedTrainingStream)
// Predict on the testing stream and print results
model.predictOnValues(parsedTestingStream.map(lp => (lp.label, lp.features)))
.print()
// Start the streaming context
// ssc.start()
// ssc.awaitTermination()Scala-Specific Features:
- Type safety with LabeledPoint and Vectors
- Pattern matching for error handling
- Functional transformations
- Efficient execution on JVM
Best Practices:
- Use Vectors.dense for dense features
- Use Vectors.sparse for sparse features
- Validate input data format
- Monitor model coefficients over time
---
11. Stratified Sampling - Python
Source: Apache Spark MLlib Statistics Guide (Context7: /apache/spark)
Use Case: Sample data while preserving class distribution
Pattern: Balanced sampling for imbalanced datasets
from pyspark import SparkContext
sc = SparkContext("local", "StratifiedSamplingExample")
# Create RDD of key-value pairs
data = [("a", 1), ("b", 2), ("a", 3), ("b", 4), ("a", 5), ("c", 6)]
rdd = sc.parallelize(data)
# Define sampling fractions per key
# Sample approximately 50% of each class
fractions = {"a": 0.5, "b": 0.5, "c": 0.5}
# Sample approximately ceil(f_k * n_k) items for each key k
# One pass over the data (faster but approximate)
sampled_rdd = rdd.sampleByKey(withReplacement=False, fractions=fractions)
print("Sampled RDD (approximate):")
print(sampled_rdd.collect())
# Note: sampleByKeyExact not available in Python
# Use Scala/Java for exact sampling
sc.stop()Key Concepts:
- Stratified sampling: Preserve proportion of each class
sampleByKey: Approximate sampling (one pass)sampleByKeyExact: Exact sampling (multiple passes, Scala/Java only)- Without replacement: Each element selected at most once
Use Cases:
- Balance training datasets for ML
- Sample for exploratory data analysis
- Create validation sets with class distribution
- Reduce data size while preserving characteristics
Parameters:
withReplacement: True allows duplicates, False doesn'tfractions: Dictionary mapping keys to sampling fractions (0.0-1.0)- Seed: Optional random seed for reproducibility
---
12. Stratified Sampling - Scala
Source: Apache Spark MLlib Statistics Guide (Context7: /apache/spark)
Use Case: Exact and approximate stratified sampling in Scala
Pattern: Statistical sampling with guarantees
import org.apache.spark.{SparkConf, SparkContext}
val conf = new SparkConf().setAppName("StratifiedSamplingExample")
val sc = new SparkContext(conf)
val data = Seq(("a", 1), ("b", 2), ("a", 3), ("b", 4), ("a", 5), ("c", 6))
val rdd = sc.parallelize(data)
// Define sampling fractions per key
val fractions = Map("a" -> 0.5, "b" -> 0.5, "c" -> 0.5)
// Using sampleByKey for expected sample size (approximate)
// One pass over data, faster
val sampledRdd = rdd.sampleByKey(withReplacement = false, fractions = fractions)
println("Sampled RDD (sampleByKey - approximate):")
sampledRdd.collect().foreach(println)
// Using sampleByKeyExact for exact sample size (guaranteed)
// Extra pass over data, exact counts
val exactSampledRdd = rdd.sampleByKeyExact(withReplacement = false, fractions = fractions)
println("Sampled RDD (sampleByKeyExact - exact):")
exactSampledRdd.collect().foreach(println)
sc.stop()Sampling Methods:
- sampleByKey: Approximate, one pass, faster
- Expected sample size: ~fraction * count
- Good for large datasets where exact count not critical
- sampleByKeyExact: Exact, extra pass, slower
- Guaranteed sample size: exactly fraction * count
- Use when exact distribution required
Resource Requirements:
- Without replacement: Extra pass to compute exact counts
- With replacement: Two passes to compute fractions
- Memory: Proportional to number of distinct keys
---
13. Stratified Sampling - Java
Source: Apache Spark MLlib Statistics Guide (Context7: /apache/spark)
Use Case: Java implementation of stratified sampling
Pattern: Type-safe sampling with Java collections
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class JavaStratifiedSamplingExample {
public static void main(String[] args) {
JavaSparkContext sc = new JavaSparkContext("local", "JavaStratifiedSamplingExample");
// Create JavaPairRDD
JavaPairRDD<String, Integer> rdd = sc.parallelizePairs(Arrays.asList(
new Tuple2<>("a", 1),
new Tuple2<>("b", 2),
new Tuple2<>("a", 3),
new Tuple2<>("b", 4),
new Tuple2<>("a", 5),
new Tuple2<>("c", 6)
));
// Define sampling fractions
Map<String, Double> fractions = new HashMap<>();
fractions.put("a", 0.5);
fractions.put("b", 0.5);
fractions.put("c", 0.5);
// Using sampleByKey for expected sample size (approximate)
JavaPairRDD<String, Integer> sampledRdd = rdd.sampleByKey(false, fractions);
System.out.println("Sampled RDD (sampleByKey - approximate):");
sampledRdd.collect().forEach(System.out::println);
// Using sampleByKeyExact for exact sample size (guaranteed)
JavaPairRDD<String, Integer> exactSampledRdd = rdd.sampleByKeyExact(false, fractions);
System.out.println("Sampled RDD (sampleByKeyExact - exact):");
exactSampledRdd.collect().forEach(System.out::println);
sc.stop();
}
}Java-Specific Patterns:
- Use JavaPairRDD for key-value pairs
- Map<String, Double> for fractions (not Scala Map)
- Tuple2 for pair creation
- Lambda expressions for cleaner code
Type Safety:
- Compile-time type checking
- Generics for type parameters
- No runtime type erasure issues
---
14. ML Pipeline with Feature Engineering
Source: Apache Spark MLlib (Context7: /apache/spark)
Use Case: Complete machine learning pipeline with transformations
Pattern: Feature engineering, training, and prediction pipeline
from pyspark.ml import Pipeline
from pyspark.ml.feature import VectorAssembler, StandardScaler, StringIndexer, OneHotEncoder
from pyspark.ml.classification import LogisticRegression, RandomForestClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("MLPipelineExample").getOrCreate()
# Load data
df = spark.read.format("libsvm").load("data/sample_libsvm_data.txt")
# Example 1: Basic Pipeline
assembler = VectorAssembler(
inputCols=["feature1", "feature2", "feature3"],
outputCol="features"
)
scaler = StandardScaler(
inputCol="features",
outputCol="scaled_features",
withStd=True,
withMean=True
)
lr = LogisticRegression(
featuresCol="scaled_features",
labelCol="label",
maxIter=10,
regParam=0.01
)
pipeline = Pipeline(stages=[assembler, scaler, lr])
# Split data
train_df, test_df = df.randomSplit([0.8, 0.2], seed=42)
# Train
model = pipeline.fit(train_df)
# Predict
predictions = model.transform(test_df)
predictions.select("label", "prediction", "probability").show()
# Evaluate
evaluator = BinaryClassificationEvaluator(metricName="areaUnderROC")
auc = evaluator.evaluate(predictions)
print(f"AUC: {auc}")
# Example 2: Categorical Feature Encoding
# Sample data with categories
categorical_df = spark.createDataFrame([
(0, "male", "engineer", 50000),
(1, "female", "doctor", 80000),
(0, "male", "teacher", 45000),
(1, "female", "engineer", 75000)
], ["label", "gender", "occupation", "salary"])
# String indexing
gender_indexer = StringIndexer(inputCol="gender", outputCol="gender_index")
occupation_indexer = StringIndexer(inputCol="occupation", outputCol="occupation_index")
# One-hot encoding
gender_encoder = OneHotEncoder(inputCol="gender_index", outputCol="gender_vec")
occupation_encoder = OneHotEncoder(inputCol="occupation_index", outputCol="occupation_vec")
# Assemble features
feature_assembler = VectorAssembler(
inputCols=["gender_vec", "occupation_vec", "salary"],
outputCol="features"
)
# Classifier
rf = RandomForestClassifier(featuresCol="features", labelCol="label", numTrees=20)
# Complete pipeline
full_pipeline = Pipeline(stages=[
gender_indexer,
occupation_indexer,
gender_encoder,
occupation_encoder,
feature_assembler,
rf
])
# Example 3: Hyperparameter Tuning with Cross-Validation
param_grid = ParamGridBuilder() \
.addGrid(rf.numTrees, [10, 20, 50]) \
.addGrid(rf.maxDepth, [5, 10, 15]) \
.addGrid(rf.minInstancesPerNode, [1, 5]) \
.build()
cv = CrossValidator(
estimator=full_pipeline,
estimatorParamMaps=param_grid,
evaluator=evaluator,
numFolds=5,
parallelism=4
)
# Train with cross-validation
cv_model = cv.fit(train_df)
# Best model
best_model = cv_model.bestModel
print(f"Best numTrees: {best_model.stages[-1].getNumTrees}")
print(f"Best maxDepth: {best_model.stages[-1].getMaxDepth()}")
# Evaluate on test set
test_predictions = cv_model.transform(test_df)
test_auc = evaluator.evaluate(test_predictions)
print(f"Test AUC: {test_auc}")
spark.stop()Pipeline Stages: 1. String Indexing: Convert categories to numeric indices 2. One-Hot Encoding: Convert indices to binary vectors 3. Feature Assembly: Combine features into single vector 4. Scaling: Normalize features for faster convergence 5. Model Training: Train classifier on processed features
Best Practices:
- Always split data before fitting pipeline
- Use cross-validation for hyperparameter tuning
- Save/load models for reuse:
model.save("path") - Monitor feature importance for interpretability
---
15. Parquet Performance Optimization
Source: Apache Spark Benchmarks (Context7: /apache/spark)
Use Case: Optimize Parquet reads for analytical queries
Pattern: Vectorized execution and column pruning
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("ParquetOptimization") \
.config("spark.sql.parquet.enableVectorizedReader", "true") \
.config("spark.sql.parquet.columnarReaderBatchSize", 4096) \
.config("spark.sql.files.maxPartitionBytes", 128 * 1024 * 1024) \
.getOrCreate()
# Example 1: Vectorized Parquet Read (fastest)
# DataPageV2 typically performs better than DataPageV1
df_vectorized = spark.read \
.option("parquet.page.write.version", "v2") \
.parquet("data/sample.parquet")
# Example 2: Predicate Pushdown (read only required data)
# Filter pushes down to Parquet file reader
filtered_df = spark.read.parquet("data/large.parquet") \
.filter("date >= '2025-01-01' AND country = 'USA'")
# Example 3: Column Pruning (read only required columns)
# Only specified columns read from Parquet
selected_df = spark.read.parquet("data/wide_table.parquet") \
.select("user_id", "timestamp", "amount")
# Example 4: Partition Pruning (skip entire partitions)
# Partitioned by date, only relevant dates read
partitioned_df = spark.read.parquet("data/partitioned_by_date") \
.filter("date = '2025-01-15'")
# Example 5: Optimal Write Configuration
df.write \
.mode("overwrite") \
.option("compression", "snappy") \
.option("parquet.block.size", 128 * 1024 * 1024) \
.option("parquet.page.size", 1 * 1024 * 1024) \
.parquet("output/optimized")
# Example 6: Nested Column Access (efficient with vectorization)
from pyspark.sql.functions import col
nested_df = spark.read.parquet("data/nested_schema.parquet")
# With nested column disabled: slower
# With nested column enabled: 20x faster
result = nested_df.select(col("user.profile.name"), col("user.stats.count"))
spark.stop()Performance Metrics (from Context7 benchmarks):
- Vectorized vs MR: 5-25x faster with vectorization
- DataPageV2 vs V1: 10-15% improvement with V2
- Nested columns: 20x faster with vectorization enabled
Optimization Techniques: 1. Vectorized Reader: Process multiple rows at once (4096 batch size) 2. Predicate Pushdown: Filter at file level, skip reading unnecessary data 3. Column Pruning: Read only required columns (columnar format advantage) 4. Partition Pruning: Skip entire partitions based on filters 5. Compression: Use snappy for balance of speed and size
Configuration Tuning:
# Vectorization settings
spark.conf.set("spark.sql.parquet.enableVectorizedReader", "true")
spark.conf.set("spark.sql.parquet.columnarReaderBatchSize", 4096)
# File sizing
spark.conf.set("spark.sql.files.maxPartitionBytes", 128 * 1024 * 1024)
spark.conf.set("parquet.block.size", 128 * 1024 * 1024)---
16. ORC Performance Optimization
Source: Apache Spark Benchmarks (Context7: /apache/spark)
Use Case: Optimize ORC reads for Hive integration
Pattern: Vectorized ORC with built-in indexes
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("ORCOptimization") \
.config("spark.sql.orc.enableVectorizedReader", "true") \
.config("spark.sql.orc.columnarReaderBatchSize", 4096) \
.getOrCreate()
# Example 1: Vectorized ORC Read
# Significantly faster than MR mode
df_vectorized = spark.read.orc("data/sample.orc")
# Example 2: Predicate Pushdown with ORC Indexes
# ORC has built-in min/max indexes per stripe
filtered_df = spark.read.orc("data/large.orc") \
.filter("amount > 1000 AND date >= '2025-01-01'")
# Example 3: Column Statistics
# ORC stores column statistics in footer
# Helps with query planning and optimization
df = spark.read.orc("data/statistics.orc")
df.explain(extended=True) # See statistics usage
# Example 4: Optimal ORC Write
df.write \
.mode("overwrite") \
.option("compression", "zlib") \
.option("orc.stripe.size", 64 * 1024 * 1024) \
.option("orc.compress.size", 256 * 1024) \
.orc("output/optimized")
# Example 5: Bloom Filters for Fast Lookups
# ORC supports bloom filters for point queries
spark.read \
.option("orc.bloom.filter.columns", "user_id,product_id") \
.option("orc.bloom.filter.fpp", 0.05) \
.orc("data/with_bloom_filters") \
.filter("user_id = 'user123'")
spark.stop()Performance Metrics (from Context7 benchmarks):
- Vectorized vs MR: 6-25x faster with vectorization
- ORC vs Parquet: Similar performance, ORC slightly better compression
- Nested columns: 18x faster with vectorization
ORC Advantages:
- Built-in indexes (min/max per column per stripe)
- Bloom filters for fast lookups
- Better compression than Parquet
- Native Hive integration
- ACID transaction support (with Delta/Iceberg)
When to Use ORC:
- Hive-based data warehouses
- Need for ACID transactions
- Point queries with bloom filters
- Slightly better compression required
---
17. Broadcast Exchange for Joins
Source: Apache Spark TPC-DS Plans (Context7: /apache/spark)
Use Case: Optimize joins by broadcasting small tables
Pattern: Broadcast hash join for dimension tables
from pyspark.sql import SparkSession
from pyspark.sql.functions import broadcast, col
spark = SparkSession.builder \
.appName("BroadcastJoin") \
.config("spark.sql.autoBroadcastJoinThreshold", 10 * 1024 * 1024) \
.getOrCreate()
# Example 1: Explicit Broadcast Hint
# Broadcast date_dim table to all executors
fact_table = spark.read.parquet("data/large_fact_table")
date_dim = spark.read.parquet("data/date_dim")
# Explicit broadcast (recommended for clarity)
result = fact_table.join(
broadcast(date_dim),
fact_table.date_key == date_dim.d_date_sk,
"inner"
)
# Example 2: SQL Broadcast Hint
fact_table.createOrReplaceTempView("fact")
date_dim.createOrReplaceTempView("date_dim")
sql_result = spark.sql("""
SELECT /*+ BROADCAST(date_dim) */
fact.*,
date_dim.d_date
FROM fact
JOIN date_dim ON fact.date_key = date_dim.d_date_sk
WHERE date_dim.d_date BETWEEN '2025-01-01' AND '2025-03-31'
""")
# Example 3: Multiple Broadcasts
# Join with multiple small dimension tables
user_dim = spark.read.parquet("data/user_dim")
product_dim = spark.read.parquet("data/product_dim")
multi_join = fact_table \
.join(broadcast(date_dim), fact_table.date_key == date_dim.d_date_sk) \
.join(broadcast(user_dim), fact_table.user_key == user_dim.user_sk) \
.join(broadcast(product_dim), fact_table.product_key == product_dim.product_sk)
# Example 4: Check Broadcast Configuration
print(f"Auto broadcast threshold: {spark.conf.get('spark.sql.autoBroadcastJoinThreshold')}")
# Disable auto broadcast (force shuffle join)
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
# Example 5: Monitor Broadcast in Physical Plan
result.explain()
# Look for "BroadcastExchange" and "BroadcastHashJoin" in plan
spark.stop()Broadcast Exchange Pattern:
BroadcastExchange HashedRelationBroadcastMode(List(cast(input[0, int, false] as bigint))), [plan_id=XX]
+- *(1) Filter isnotnull(d_date_sk#0)
+- *(1) ColumnarToRow
+- FileScan parquet date_dimKey Benefits:
- No Shuffle: Small table sent to all executors once
- Fast Joins: Hash join on broadcasted data
- Memory Efficient: Broadcasted data cached in memory
- Reduced Network: Avoids shuffling large fact table
Best Practices:
- Broadcast tables < 10 MB for best performance
- Use explicit broadcast() for clarity
- Monitor executor memory usage
- Broadcast multiple small tables if needed
When Not to Broadcast:
- Table size > 100 MB (may cause OOM)
- Limited executor memory
- Very large number of executors (broadcast overhead)
---
18. Reused Exchange for Query Optimization
Source: Apache Spark TPC-DS Plans (Context7: /apache/spark)
Use Case: Reuse shuffled data across query stages
Pattern: Common Table Expression (CTE) optimization
from pyspark.sql import SparkSession
from pyspark.sql.functions import col
spark = SparkSession.builder \
.appName("ReusedExchange") \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.sql.cte.enabled", "true") \
.getOrCreate()
# Load tables
customer = spark.read.parquet("data/customer")
store_sales = spark.read.parquet("data/store_sales")
date_dim = spark.read.parquet("data/date_dim")
# Create views
customer.createOrReplaceTempView("customer")
store_sales.createOrReplaceTempView("store_sales")
date_dim.createOrReplaceTempView("date_dim")
# Example 1: SQL with CTEs (enables exchange reuse)
result = spark.sql("""
WITH customer_sales AS (
SELECT
c_customer_sk,
c_customer_id,
SUM(ss_net_paid) as total_sales
FROM customer
JOIN store_sales ON c_customer_sk = ss_customer_sk
GROUP BY c_customer_sk, c_customer_id
)
SELECT
cs1.c_customer_id,
cs1.total_sales as year1_sales,
cs2.total_sales as year2_sales
FROM customer_sales cs1
JOIN customer_sales cs2 ON cs1.c_customer_sk = cs2.c_customer_sk
WHERE cs1.total_sales > 1000
""")
# Example 2: Detect Reused Exchange in Plan
result.explain(extended=True)
# Look for "ReusedExchange [Reuses operator id: XX]" in physical plan
# Example 3: Cache for Manual Reuse
aggregated = customer.join(store_sales, "c_customer_sk") \
.groupBy("c_customer_sk", "c_customer_id") \
.agg({"ss_net_paid": "sum"})
# Cache to reuse in multiple queries
aggregated.cache()
# Use in multiple downstream operations
high_value = aggregated.filter(col("sum(ss_net_paid)") > 5000)
low_value = aggregated.filter(col("sum(ss_net_paid)") < 1000)
spark.stop()ReusedExchange Pattern (from Context7):
ReusedExchange [Reuses operator id: 84]
Output [2]: [d_date_sk#45, d_year#46]
ReusedExchange [Reuses operator id: 12]
Output [8]: [c_customer_sk#47, c_customer_id#48, ...]Benefits:
- Avoid Duplicate Shuffles: Reuse already shuffled data
- Faster Execution: Skip redundant computations
- Lower Resource Usage: Reduce network and CPU
- Automatic Optimization: Catalyst detects reuse opportunities
When Exchange Reuse Happens:
- Common subqueries in SQL
- WITH clauses (CTEs)
- Multiple aggregations on same data
- Self-joins on previously aggregated data
Enable Exchange Reuse:
# Adaptive Query Execution (required for exchange reuse)
spark.conf.set("spark.sql.adaptive.enabled", "true")
# Enable CTE optimization
spark.conf.set("spark.sql.cte.enabled", "true")---
19. Hash Aggregation for Final Sum
Source: Apache Spark TPC-DS Plans (Context7: /apache/spark)
Use Case: Efficient aggregation with hash-based grouping
Pattern: Two-stage aggregation (partial + final)
from pyspark.sql import SparkSession
from pyspark.sql.functions import sum, count, avg, col
spark = SparkSession.builder \
.appName("HashAggregation") \
.config("spark.sql.adaptive.enabled", "true") \
.getOrCreate()
# Load data
sales = spark.read.parquet("data/sales")
# Example 1: Simple Aggregation (automatic hash aggregation)
category_totals = sales.groupBy("category") \
.agg(
sum("total_sum").alias("total_sales"),
count("*").alias("count"),
avg("amount").alias("avg_amount")
)
# View physical plan to see HashAggregate
category_totals.explain()
# Example 2: Multi-Level Grouping
# Partial aggregation at executor level
# Final aggregation at driver/central location
hierarchical = sales.groupBy("category", "subcategory") \
.agg(sum("amount").alias("total"))
# Example 3: Window with Aggregation
from pyspark.sql.window import Window
# Partial hash aggregations per partition
window_spec = Window.partitionBy("category")
with_running_total = sales.withColumn(
"running_total",
sum("amount").over(window_spec)
)
# Example 4: Adaptive Execution with Dynamic Coalescing
# AQE may adjust partitions for final aggregation
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true")
spark.conf.set("spark.sql.adaptive.advisoryPartitionSizeInBytes", "64MB")
result = sales.groupBy("category", "date") \
.agg(sum("amount").alias("total"))
result.explain()
# Check for "AdaptiveSparkPlan" and optimized HashAggregate
spark.stop()HashAggregate Pattern (from Context7):
HashAggregate [codegen id : 9]
Input [3]: [i_category#16, sum#23, isEmpty#24]
Keys [1]: [i_category#16]
Functions [1]: [sum(total_sum#20)]
Aggregate Attributes [1]: [sum(total_sum#20)#25]
Results [6]: [sum(total_sum#20)#25 AS total_sum#26, i_category#16, ...]Two-Stage Aggregation: 1. Partial Aggregation (at executors):
- Combine values locally per partition
- Reduce data before shuffle
2. Final Aggregation (after shuffle):
- Combine partial results
- Produce final aggregated values
Performance Benefits:
- Reduced Shuffle: Partial aggregation minimizes data transfer
- Better Memory Usage: Hash tables instead of sorted data
- Faster Execution: O(1) lookups in hash table
- Automatic Fallback: Falls back to sort-based if hash table too large
---
20. Chained Time Window Aggregations
Source: Apache Spark Structured Streaming Guide (Context7: /apache/spark)
Use Case: Multi-level windowed aggregations with window_time
Pattern: Aggregate over fine-grained windows, then coarser windows
Python
from pyspark.sql.functions import window, window_time, col
# Streaming DataFrame of schema { timestamp: Timestamp, word: String }
words = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.load()
# First level: 10-minute windows with 5-minute slide
windowedCounts = words.groupBy(
window(col("timestamp"), "10 minutes", "5 minutes"),
col("word")
).count()
# Second level: 1-hour windows based on first window's time
# window_time extracts representative timestamp from window
anotherWindowedCounts = windowedCounts.groupBy(
window(window_time(col("window")), "1 hour"),
col("word")
).count()
# Write to console
query = anotherWindowedCounts.writeStream \
.outputMode("complete") \
.format("console") \
.start()
query.awaitTermination()Scala
import spark.implicits._
import org.apache.spark.sql.functions.{window, window_time}
val words = spark.readStream.format("kafka").load()
// Group by 10-minute window and word
val windowedCounts = words.groupBy(
window($"timestamp", "10 minutes", "5 minutes"),
$"word"
).count()
// Group windowed data by 1-hour window
val anotherWindowedCounts = windowedCounts.groupBy(
window(window_time($"window"), "1 hour"),
$"word"
).count()Java
import static org.apache.spark.sql.functions.*;
Dataset<Row> words = spark.readStream().format("kafka").load();
// First window aggregation
Dataset<Row> windowedCounts = words.groupBy(
window(col("timestamp"), "10 minutes", "5 minutes"),
col("word")
).count();
// Second window aggregation using window_time
Dataset<Row> anotherWindowedCounts = windowedCounts.groupBy(
window(window_time(col("window")), "1 hour"),
col("word")
).count();Key Concepts:
- window_time: Extract representative timestamp from window struct
- Chained Aggregations: Aggregate pre-aggregated data
- Multi-Resolution: Combine fine and coarse time granularities
Use Cases:
- Real-Time Dashboards: 1-minute updates with hourly trends
- Metrics Rollups: Compute 5-min, 1-hour, 1-day aggregates
- Anomaly Detection: Compare current 10-min window to hourly average
- Capacity Planning: Track short-term spikes and long-term trends
Performance Benefits:
- Reduced State: First aggregation reduces data size
- Flexible Granularity: Different time scales from same stream
- Efficient Computation: Reuse first-level aggregations
---
21. Distributed Matrix Operations
Source: Apache Spark MLlib Guide (Context7: /apache/spark)
Use Case: Linear algebra on distributed matrices
Pattern: Scalable matrix computations for large datasets
from pyspark.mllib.linalg import Vectors
from pyspark.mllib.linalg.distributed import RowMatrix, IndexedRow, IndexedRowMatrix, MatrixEntry, CoordinateMatrix
# Example 1: RowMatrix (no row indices)
rows = sc.parallelize([
Vectors.dense([1.0, 2.0, 3.0]),
Vectors.dense([4.0, 5.0, 6.0]),
Vectors.dense([7.0, 8.0, 9.0])
])
row_matrix = RowMatrix(rows)
# Compute column statistics
summary = row_matrix.computeColumnSummaryStatistics()
print(f"Rows: {row_matrix.numRows()}")
print(f"Cols: {row_matrix.numCols()}")
print(f"Column means: {summary.mean()}")
print(f"Column variances: {summary.variance()}")
# Compute Gramian matrix (X^T * X)
gramian = row_matrix.computeGramianMatrix()
print(f"Gramian matrix:\n{gramian}")
# Singular Value Decomposition (SVD)
svd = row_matrix.computeSVD(k=2, computeU=True)
print(f"Singular values: {svd.s}")
# Example 2: IndexedRowMatrix (with row indices)
indexed_rows = sc.parallelize([
IndexedRow(0, Vectors.dense([1.0, 2.0, 3.0])),
IndexedRow(1, Vectors.dense([4.0, 5.0, 6.0])),
IndexedRow(5, Vectors.dense([7.0, 8.0, 9.0])) # Sparse row indices
])
indexed_matrix = IndexedRowMatrix(indexed_rows)
print(f"Indexed matrix rows: {indexed_matrix.numRows()}")
print(f"Indexed matrix cols: {indexed_matrix.numCols()}")
# Convert to RowMatrix
row_mat = indexed_matrix.toRowMatrix()
# Example 3: CoordinateMatrix (sparse matrix)
entries = sc.parallelize([
MatrixEntry(0, 0, 1.0),
MatrixEntry(0, 2, 3.0),
MatrixEntry(1, 1, 5.0),
MatrixEntry(2, 0, 7.0),
MatrixEntry(2, 2, 9.0)
])
coord_matrix = CoordinateMatrix(entries)
print(f"Coordinate matrix entries: {coord_matrix.entries.count()}")
# Convert to IndexedRowMatrix for computations
indexed_from_coord = coord_matrix.toIndexedRowMatrix()
# Example 4: Matrix Transpose
transposed = coord_matrix.transpose()
# Example 5: BlockMatrix (for distributed matrix multiplication)
from pyspark.mllib.linalg.distributed import BlockMatrix
# Convert to BlockMatrix for efficient operations
block_matrix = indexed_matrix.toBlockMatrix(rowsPerBlock=2, colsPerBlock=2)
print(f"BlockMatrix blocks: {block_matrix.numRowBlocks} x {block_matrix.numColBlocks}")
# Matrix multiplication
result = block_matrix.multiply(block_matrix.transpose())Matrix Types:
1. RowMatrix:
- No row indices
- Efficient for column statistics, SVD, PCA
- Use when rows don't need indexing
2. IndexedRowMatrix:
- Rows have Long indices
- Efficient for row operations
- Use when row indices matter
3. CoordinateMatrix:
- Stores (row, col, value) entries
- Efficient for very sparse matrices
- Use when most values are zero
4. BlockMatrix:
- Divides matrix into blocks
- Efficient for matrix multiplication
- Use for large matrix operations
Common Operations:
- Column statistics (mean, variance, min, max)
- SVD (Singular Value Decomposition)
- PCA (Principal Component Analysis)
- Matrix multiplication
- Transpose
Use Cases:
- Feature matrix computations in ML
- Collaborative filtering (user-item matrices)
- Graph analytics (adjacency matrices)
- Dimensionality reduction (PCA, SVD)
---
22. Date and Time Operations
Source: Apache Spark Date/Time Benchmarks (Context7: /apache/spark)
Use Case: Efficient date and timestamp processing
Pattern: Optimize date operations for large-scale data
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, to_date, to_timestamp, date_format, year, month, dayofmonth, hour, current_date, current_timestamp, datediff, date_add
spark = SparkSession.builder.appName("DateTimeOperations").getOrCreate()
# Example 1: Date Conversions
df = spark.createDataFrame([
("2025-01-15",),
("2025-02-20",),
("2025-03-10",)
], ["date_string"])
# String to Date
df_with_date = df.withColumn("date", to_date(col("date_string"), "yyyy-MM-dd"))
# String to Timestamp
df_with_ts = df.withColumn("timestamp", to_timestamp(col("date_string"), "yyyy-MM-dd"))
# Example 2: Extract Date Parts
df_parts = df_with_date \
.withColumn("year", year(col("date"))) \
.withColumn("month", month(col("date"))) \
.withColumn("day", dayofmonth(col("date")))
df_parts.show()
# Example 3: Date Formatting
df_formatted = df_with_date \
.withColumn("formatted", date_format(col("date"), "MMM dd, yyyy")) \
.withColumn("iso_format", date_format(col("date"), "yyyy-MM-dd'T'HH:mm:ss"))
df_formatted.show(truncate=False)
# Example 4: Date Arithmetic
df_arithmetic = df_with_date \
.withColumn("tomorrow", date_add(col("date"), 1)) \
.withColumn("next_week", date_add(col("date"), 7)) \
.withColumn("days_from_now", datediff(current_date(), col("date")))
df_arithmetic.show()
# Example 5: Timestamp Operations
from pyspark.sql.functions import unix_timestamp, from_unixtime
df_ts = spark.createDataFrame([
("2025-01-15 10:30:00",),
("2025-02-20 14:45:30",)
], ["ts_string"])
df_ts_ops = df_ts \
.withColumn("timestamp", to_timestamp(col("ts_string"))) \
.withColumn("unix_time", unix_timestamp(col("timestamp"))) \
.withColumn("hour", hour(col("timestamp")))
df_ts_ops.show()
# Example 6: Collect Date/Timestamp Performance
# From Context7 benchmarks: Collecting java.sql.Date is efficient
dates = spark.range(1000000) \
.withColumn("date", date_add(current_date(), col("id")))
# Collect operation performance
collected_dates = dates.select("date").take(100)
# Example 7: Window Operations with Dates
from pyspark.sql.window import Window
sales = spark.createDataFrame([
("2025-01-01", 100),
("2025-01-02", 150),
("2025-01-03", 200),
("2025-01-04", 120),
("2025-01-05", 180)
], ["date", "amount"])
# 3-day moving average
window_spec = Window.orderBy("date").rowsBetween(-2, 0)
sales_with_ma = sales.withColumn(
"moving_avg",
avg("amount").over(window_spec)
)
sales_with_ma.show()
# Example 8: Date Filtering for Partition Pruning
partitioned_data = spark.read.parquet("data/partitioned_by_date")
# Efficient: Prunes partitions based on date filter
filtered = partitioned_data.filter(
(col("date") >= "2025-01-01") & (col("date") < "2025-02-01")
)
spark.stop()Performance Tips:
- Use native date types (date, timestamp) instead of strings
- Partition by date columns for efficient filtering
- Use date arithmetic functions (datediff, date_add) over UDFs
- Collect operations on date types are efficient (from Context7 benchmarks)
Common Patterns:
- Convert strings to dates early in pipeline
- Extract date parts for grouping/filtering
- Use date arithmetic for time-based windows
- Partition data by date for time-series analysis
---
Summary
This collection of 22 production examples demonstrates:
- Streaming: Word count, windowing, session windows, stream-static joins
- Machine Learning: Linear regression, pipelines, feature engineering, sampling
- Performance: Parquet/ORC optimization, broadcast joins, exchange reuse
- Core Operations: RDDs, DataFrames, SQL, aggregations
- Advanced: Matrix operations, date/time handling, chained windows
All examples are sourced from Apache Spark's official repository via Context7 (/apache/spark), ensuring production-ready patterns and best practices.
Key Takeaways: 1. Use DataFrames over RDDs for automatic optimization 2. Leverage Catalyst optimizer with SQL and DataFrame API 3. Enable vectorization for Parquet/ORC performance 4. Broadcast small tables to avoid shuffles 5. Use appropriate windowing for streaming analytics 6. Cache strategically for iterative algorithms 7. Monitor physical plans for optimization opportunities
---
Examples Version: 1.0.0 Last Updated: October 2025 Source: Apache Spark via Context7 (/apache/spark) Total Examples: 22 production-ready patterns
Apache Spark Data Processing Skill
Master Apache Spark for distributed data processing, streaming analytics, and machine learning at scale.
Overview
Apache Spark is a unified analytics engine for large-scale data processing, offering high-level APIs in Java, Scala, Python, and R. This skill provides comprehensive guidance for building production-ready Spark applications across batch processing, real-time streaming, SQL analytics, and machine learning workflows.
Key Capabilities:
- Process petabyte-scale datasets with distributed computing
- Real-time stream processing with sub-second latency
- Interactive SQL queries on structured and semi-structured data
- Scalable machine learning with MLlib
- Unified API for batch and streaming workloads
What You'll Learn
Core Data Processing
- RDDs (Resilient Distributed Datasets): Low-level distributed data abstraction with fault tolerance
- DataFrames & Datasets: Structured data processing with automatic query optimization
- Transformations & Actions: Lazy evaluation patterns for efficient computation
- Partitioning: Data distribution strategies for optimal parallelism
Spark SQL
- DataFrame API: Declarative data manipulation with type safety
- SQL Queries: Execute ANSI SQL on distributed datasets
- Data Sources: Read/write Parquet, ORC, JSON, CSV, JDBC, Hive
- Query Optimization: Catalyst optimizer and Tungsten execution engine
- Window Functions: Advanced analytics with ranking, aggregations, and offsets
Streaming Processing
- Structured Streaming: Unified batch and streaming API
- Stream Sources: Kafka, files, sockets, and custom sources
- Windowing: Tumbling, sliding, and session windows
- Watermarking: Handle late-arriving data with configurable tolerance
- Stateful Processing: Maintain state across micro-batches
- Stream-Static Joins: Enrich streaming data with reference tables
Machine Learning (MLlib)
- ML Pipelines: Chain transformations, feature engineering, and models
- Classification & Regression: Logistic regression, random forests, gradient boosting
- Clustering: K-means, Gaussian mixture models
- Dimensionality Reduction: PCA, SVD
- Feature Engineering: Encoders, scalers, assemblers
- Model Selection: Cross-validation and hyperparameter tuning
- Streaming ML: Train models on continuous data streams
Performance Optimization
- Caching & Persistence: Memory and disk storage strategies
- Broadcast Variables: Efficiently share large read-only data
- Shuffle Optimization: Minimize data movement across network
- Adaptive Query Execution (AQE): Runtime query optimization
- Data Formats: Choose optimal formats (Parquet, ORC) for performance
- Partition Tuning: Balance parallelism and overhead
Production Deployment
- Cluster Managers: Standalone, YARN, Kubernetes, Mesos
- Resource Allocation: Executor sizing and dynamic allocation
- Monitoring: Spark UI, metrics, and logging
- Fault Tolerance: Automatic recovery and checkpointing
- Security: Authentication, authorization, encryption
Apache Spark Architecture
High-Level Components
┌─────────────────────────────────────────────────────┐
│ Driver Program │
│ ┌────────────┐ ┌─────────────────────────────┐ │
│ │ SparkContext│ │ DAG Scheduler │ │
│ │ │ │ Task Scheduler │ │
│ └────────────┘ └─────────────────────────────┘ │
└─────────────────────┬───────────────────────────────┘
│ Cluster Manager
│ (Standalone/YARN/K8s/Mesos)
┌─────────────┼─────────────┐
│ │ │
┌───────▼──────┐ ┌────▼──────┐ ┌───▼────────┐
│ Executor 1 │ │ Executor 2│ │ Executor N │
│ ┌──────────┐ │ │┌──────────┐│ │┌──────────┐│
│ │ Task 1 │ │ ││ Task 3 ││ ││ Task N ││
│ ├──────────┤ │ │├──────────┤│ │├──────────┤│
│ │ Task 2 │ │ ││ Task 4 ││ ││ Task N+1││
│ └──────────┘ │ │└──────────┘│ │└──────────┘│
│ Cache │ │ Cache │ │ Cache │
└──────────────┘ └────────────┘ └────────────┘Components:
- Driver: Coordinates execution, maintains application state
- Executors: Distributed processes that execute tasks and store data
- Cluster Manager: Allocates resources across applications
- Tasks: Individual units of work sent to executors
Execution Flow
1. Application Submission: Driver program creates SparkContext 2. DAG Construction: Transformations build Directed Acyclic Graph 3. Stage Division: DAG divided into stages at shuffle boundaries 4. Task Scheduling: Tasks scheduled on executors based on data locality 5. Execution: Executors run tasks, cache intermediate results 6. Result Collection: Actions trigger computation and return results
Data Flow
Input Data → RDD/DataFrame → Transformations → Actions → Output
(Partitioned) (Lazy DAG) (Trigger)Lazy Evaluation:
- Transformations (map, filter, join) build computation graph
- Actions (collect, count, save) trigger actual execution
- Optimizer analyzes entire DAG before execution
- Minimizes data movement and computation
When to Use Apache Spark
Ideal Use Cases
Large-Scale Batch Processing:
- ETL pipelines processing TB-PB datasets
- Log aggregation and analysis
- Data warehousing and data lake processing
- Historical data analytics
Real-Time Stream Processing:
- Real-time dashboards and metrics
- Fraud detection and anomaly detection
- IoT sensor data processing
- Click stream analysis
Interactive Analytics:
- Ad-hoc queries on large datasets
- Business intelligence and reporting
- Data exploration and discovery
- SQL analytics on data lakes
Machine Learning:
- Training models on massive datasets
- Feature engineering at scale
- Distributed hyperparameter tuning
- Production ML pipelines
Unified Workloads:
- Combining batch and streaming in single application
- Lambda architecture implementations
- Complex multi-stage data pipelines
Not Ideal For
Small Data (<100 GB):
- Single-machine tools (pandas, R) are simpler and faster
- Spark overhead not justified for small datasets
Ultra-Low Latency (<10ms):
- Specialized stream processors (Flink, Storm) better for microsecond latency
- Spark's micro-batch approach has 100ms+ latency floor
OLTP Workloads:
- Transactional databases (PostgreSQL, MySQL) better for CRUD operations
- Spark optimized for analytical, not transactional, workloads
Simple Transformations:
- Traditional ETL tools may be simpler for basic operations
- Spark's power needed for complex, distributed transformations
Quick Start
Installation
PySpark (Python):
# Install via pip
pip install pyspark
# Or with Conda
conda install -c conda-forge pysparkSpark Standalone:
# Download from Apache Spark website
wget https://archive.apache.org/dist/spark/spark-3.5.0/spark-3.5.0-bin-hadoop3.tgz
tar -xzf spark-3.5.0-bin-hadoop3.tgz
export SPARK_HOME=/path/to/spark-3.5.0-bin-hadoop3
export PATH=$PATH:$SPARK_HOME/binHello World Example
Word Count (Classic Big Data Example):
from pyspark.sql import SparkSession
# Create SparkSession
spark = SparkSession.builder \
.appName("WordCount") \
.master("local[*]") \
.getOrCreate()
# Read text file
text_rdd = spark.sparkContext.textFile("input.txt")
# Word count transformation
word_counts = text_rdd \
.flatMap(lambda line: line.split()) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b: a + b)
# Collect results
results = word_counts.collect()
for word, count in results:
print(f"{word}: {count}")
# Or save to file
word_counts.saveAsTextFile("output")
spark.stop()DataFrame Example:
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, count
spark = SparkSession.builder.appName("DataFrameExample").getOrCreate()
# Create DataFrame
data = [
("Alice", "Engineering", 100000),
("Bob", "Sales", 80000),
("Charlie", "Engineering", 120000),
("Diana", "Sales", 90000)
]
df = spark.createDataFrame(data, ["name", "department", "salary"])
# Transformations
result = df.groupBy("department") \
.agg(count("*").alias("count"),
avg("salary").alias("avg_salary")) \
.orderBy(col("avg_salary").desc())
# Show results
result.show()
spark.stop()Local Development Setup
Configure Local Spark:
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("LocalDevelopment") \
.master("local[4]") # 4 local threads \
.config("spark.driver.memory", "4g") \
.config("spark.executor.memory", "4g") \
.config("spark.sql.shuffle.partitions", 8) # Reduce for local \
.getOrCreate()
# Set log level to reduce verbosity
spark.sparkContext.setLogLevel("WARN")Skill Structure
This skill is organized into three comprehensive files:
1. SKILL.md (This File)
- Core concepts and architecture
- Deep dives into RDDs, DataFrames, Spark SQL
- Streaming processing guide
- MLlib machine learning
- Performance tuning strategies
- Production deployment best practices
- Troubleshooting and common patterns
2. EXAMPLES.md
- 20+ production-ready code examples
- Real-world scenarios and use cases
- Performance optimization examples
- Streaming analytics patterns
- Machine learning workflows
- All examples sourced from Context7's Apache Spark library
3. README.md (You Are Here)
- Overview and quick start
- Architecture diagrams
- When to use Spark
- Installation and setup
- Skill navigation guide
Performance Characteristics
Execution Speed
In-Memory Processing:
- 10-100x faster than Hadoop MapReduce for iterative algorithms
- Sub-second query latency on cached data
- Efficient for machine learning workloads with multiple passes
Disk-Based Processing:
- 2-10x faster than MapReduce on disk-based workloads
- Optimized shuffle and serialization
- Efficient DAG execution
Scalability
Horizontal Scaling:
- Linear scalability to 1000+ nodes
- Process petabyte-scale datasets
- Dynamic resource allocation
Vertical Scaling:
- Leverage multi-core CPUs efficiently
- Optimize memory usage with Tungsten
- SIMD vectorization in execution engine
Latency
Batch Processing:
- Seconds to hours depending on data size
- Optimized for throughput over latency
Streaming:
- 100ms to seconds micro-batch latency
- Continuous processing mode for lower latency
- Trade-off between throughput and latency
Data Processing Patterns
Lambda Architecture
Combine batch and streaming for comprehensive analytics:
Batch Layer (Historical) Speed Layer (Real-time)
↓ ↓
Spark Batch Jobs Spark Streaming
↓ ↓
Master Dataset Real-time Views
└──────────┬────────────────┘
↓
Serving Layer
(Combined Views)Kappa Architecture
Unified streaming-only architecture:
All Data → Kafka → Spark Streaming → Data Store
↓
Reprocessing (same code)Medallion Architecture (Databricks)
Structured data pipeline:
Bronze Layer (Raw) → Silver Layer (Cleaned) → Gold Layer (Aggregated)
Raw ingestion Validation & cleaning Business-level aggregates
Parquet/Delta Delta Lake format Star/Snowflake schemaIntegration Ecosystem
Data Sources
- Cloud Storage: S3, Azure Blob, Google Cloud Storage
- Databases: PostgreSQL, MySQL, Oracle, SQL Server (JDBC)
- NoSQL: Cassandra, MongoDB, HBase
- Data Warehouses: Snowflake, Redshift, BigQuery
- Streaming: Kafka, Kinesis, Event Hubs
- Files: Parquet, ORC, Avro, JSON, CSV, text
Data Formats
- Parquet: Best for analytics (columnar, compressed)
- ORC: Optimized for Hive (columnar, indexed)
- Avro: Row-oriented, schema evolution
- Delta Lake: ACID transactions, time travel
- Iceberg: Open table format, schema evolution
Orchestration
- Apache Airflow: Workflow orchestration
- Databricks Jobs: Managed Spark jobs
- AWS Glue: Serverless ETL
- Azure Data Factory: Cloud ETL/ELT
Visualization
- Tableau: Connect via JDBC/ODBC
- Power BI: Spark connector
- Superset: Open-source BI
- Databricks Notebooks: Built-in visualization
Learning Path
Beginner (Week 1-2)
1. Understand Spark architecture and core concepts 2. Learn RDD basics and transformations 3. Practice DataFrame operations 4. Execute simple SQL queries 5. Work with different data formats
Intermediate (Week 3-4)
1. Master DataFrame API and SQL 2. Implement streaming applications 3. Basic performance tuning (caching, partitioning) 4. Use MLlib for simple ML tasks 5. Deploy to cluster (YARN/Kubernetes)
Advanced (Week 5-8)
1. Advanced performance optimization 2. Complex streaming patterns (stateful, windowing) 3. Production MLlib pipelines 4. Custom UDFs and data sources 5. Tuning for large-scale production workloads
Expert (Ongoing)
1. Contribute to Spark open source 2. Develop custom Spark extensions 3. Optimize query plans and execution 4. Design large-scale architectures 5. Train and mentor teams
Common Challenges and Solutions
Memory Management
Challenge: OutOfMemoryError in executors Solution: Increase executor memory, use appropriate storage levels, avoid collect() on large datasets
Data Skew
Challenge: Few tasks take much longer due to unbalanced partitions Solution: Use salting, repartition by skewed column, isolate and process skewed keys separately
Shuffle Performance
Challenge: Slow shuffle operations consuming resources Solution: Minimize shuffles (use reduceByKey vs groupByKey), broadcast small tables, tune shuffle partitions
Small Files Problem
Challenge: Many small files causing overhead Solution: Coalesce before writing, use appropriate partitioning, compact files periodically
Streaming Lag
Challenge: Processing falls behind data arrival rate Solution: Increase parallelism, tune watermarks, optimize transformations, scale cluster
Best Practices Summary
1. Use DataFrames over RDDs - Better optimization and performance 2. Cache Wisely - Only cache data reused multiple times 3. Partition Appropriately - 2-4x CPU cores, partition by commonly filtered columns 4. Use Parquet/ORC - Columnar formats for analytical workloads 5. Broadcast Small Tables - Avoid shuffling large tables in joins 6. Enable AQE - Leverage adaptive query execution 7. Monitor with Spark UI - Identify bottlenecks early 8. Test with Representative Data - Use production-scale samples 9. Version Control Everything - Code, configs, schemas 10. Implement Checkpointing - Ensure fault tolerance in streaming
Resources
Official Documentation
- Apache Spark Docs: https://spark.apache.org/docs/latest/
- API Reference: https://spark.apache.org/docs/latest/api.html
- Programming Guides: https://spark.apache.org/docs/latest/rdd-programming-guide.html
Community
- GitHub: https://github.com/apache/spark
- Stack Overflow: [apache-spark] tag
- Spark User Mailing List: user@spark.apache.org
- Spark Summit: Annual conference and videos
Learning Resources
- Databricks Blog: https://databricks.com/blog
- Spark by Examples: https://sparkbyexamples.com/
- Context7 Library: /apache/spark
Tools
- Databricks: Managed Spark platform
- AWS EMR: Managed Spark on AWS
- Azure Synapse: Managed Spark on Azure
- Google Dataproc: Managed Spark on GCP
Next Steps
1. Read SKILL.md - Deep dive into all Spark components 2. Review EXAMPLES.md - Study 20+ production examples 3. Set Up Local Environment - Install PySpark and run examples 4. Build a Project - Apply skills to real dataset 5. Deploy to Cluster - Move from local to distributed execution 6. Optimize Performance - Profile and tune your application 7. Contribute Back - Share learnings with community
---
Skill Version: 1.0.0 Last Updated: October 2025 Maintainer: Apache Spark Community Context7 Integration: /apache/spark (8000 tokens) License: Apache License 2.0
Related skills
How it compares
Choose apache-spark-data-processing when data volume requires distributed Spark execution rather than single-node pandas or SQL scripts.
FAQ
What workloads does apache-spark-data-processing cover?
apache-spark-data-processing covers distributed transforms, aggregations, and feature pipelines on Apache Spark. The skill emphasizes sound partitioning and performance tuning for large-scale datasets that exceed single-node capacity.
When should developers use apache-spark-data-processing?
apache-spark-data-processing fits backend data engineering when batch ETL, analytics, or ML feature stores require Spark cluster jobs. Use it when partitioning and shuffle optimization materially affect runtime and cost.