Data Manipulation and Transformation Questions
Encompasses techniques and best practices for cleaning, transforming, and preparing data for analysis and production systems. Candidates should be able to handle missing values, duplicates, inconsistency resolution, normalization and denormalization, data typing and casting, and validation checks. Expect discussion of writing robust code that handles edge cases such as empty datasets and null values, defensive data validation, unit and integration testing for transformations, and strategies for performance and memory efficiency. At more senior levels include design of scalable, debuggable, and maintainable data pipelines and transformation architectures, idempotency, schema evolution, batch versus streaming trade offs, observability and monitoring, versioning and reproducibility, and tool selection such as SQL, pandas, Spark, or dedicated ETL frameworks.
Sample Answer
from pyspark.sql import SparkSession
from pyspark.sql.functions import from_json, col, explode_outer, coalesce, lit
from pyspark.sql.types import StructType, StructField, StringType, ArrayType, LongType, DoubleType
spark = SparkSession.builder.getOrCreate()
# Example schema -- define key nested structures explicitly for performance & safety
schema = StructType([
StructField("event_id", StringType()),
StructField("user", StructType([
StructField("id", StringType()),
StructField("profile", StructType([
StructField("age", LongType()),
StructField("prefs", ArrayType(StructType([
StructField("k", StringType()),
StructField("v", StringType())
])))
]))
])),
StructField("actions", ArrayType(StructType([
StructField("type", StringType()),
StructField("value", DoubleType()),
StructField("meta", StructType([StructField("src", StringType())]))
])))
])
# Load raw JSON lines and parse
raw = spark.read.text("/path/to/logs/*.json")
df = raw.select(from_json(col("value"), schema).alias("j")).select("j.*")
# Explode actions safely (handles missing/empty arrays)
df_actions = df.withColumn("action", explode_outer(col("actions")))
# Explode user.profile.prefs safely (if you need one pref per row)
df_prefs = df.withColumn("pref", explode_outer(col("user.profile.prefs")))
# Build wide analytical row by selecting fields with defaults
wide = df_actions.join(df_prefs.select("event_id", "pref"), on="event_id", how="left_outer") \
.select(
col("event_id"),
coalesce(col("user.id"), lit("unknown")).alias("user_id"),
col("user.profile.age").alias("user_age"),
col("action.type").alias("action_type"),
coalesce(col("action.value"), lit(0.0)).alias("action_value"),
col("action.meta.src").alias("action_src"),
col("pref.k").alias("pref_key"),
col("pref.v").alias("pref_value")
)Sample Answer
from pyspark.sql import functions as F
from delta.tables import DeltaTable
# incoming streaming DF schema: id, op_type ('I','U','D'), payload (struct), ts (timestamp)
cdc_stream = spark.readStream.format("kafka")... # omitted: read and parse into columns above
# assign numeric op priority so Delete > Update > Insert if same ts (optional)
op_priority = F.when(F.col("op_type") == "D", 3).when(F.col("op_type") == "U", 2).otherwise(1)
# In each microbatch, pick the latest event per id (highest ts, then highest priority)
def upsert_batch(batch_df, batch_id):
if batch_df.rdd.isEmpty():
return
dedup = (batch_df
.withColumn("_op_prio", op_priority)
.withColumn("_ts", F.col("ts").cast("long"))
.withColumn("_payload", F.col("payload"))
.select("id", "op_type", "_payload", "_ts", "_op_prio")
.withColumn("rank", F.row_number().over(
Window.partitionBy("id").orderBy(F.col("_ts").desc(), F.col("_op_prio").desc())
))
.filter(F.col("rank") == 1)
.drop("rank"))
# write dedup to temp view or temp table
tmp_path = "/tmp/cdc_batch"
dedup.createOrReplaceTempView("cdc_events_batch")
delta_table_path = "/mnt/delta/current_view"
# ensure table exists with columns: id, payload (struct), _last_ts (long), _is_deleted (boolean)
if not DeltaTable.isDeltaTable(spark, delta_table_path):
(dedup
.withColumn("payload", F.col("_payload"))
.withColumn("_last_ts", F.col("_ts"))
.withColumn("_is_deleted", F.when(F.col("op_type") == "D", True).otherwise(False))
.select("id","payload","_last_ts","_is_deleted")
.write.format("delta").mode("overwrite").save(delta_table_path))
delta = DeltaTable.forPath(spark, delta_table_path)
src = spark.table("cdc_events_batch").alias("src")
# Only apply if incoming ts >= target._last_ts (or target null)
delta.alias("t").merge(
source=src,
condition="t.id = src.id"
).whenMatchedUpdate(
condition="src._ts >= t._last_ts",
set={
"payload": "src._payload",
"_last_ts": "src._ts",
"_is_deleted": "CASE WHEN src.op_type = 'D' THEN true ELSE false END"
}
).whenNotMatchedInsert(
condition="true",
values={
"id": "src.id",
"payload": "src._payload",
"_last_ts": "src._ts",
"_is_deleted": "CASE WHEN src.op_type = 'D' THEN true ELSE false END"
}
).execute()
query = (cdc_stream
# parse/transform as needed to columns: id, op_type, payload, ts
.writeStream
.foreachBatch(upsert_batch)
.option("checkpointLocation", "/mnt/checkpoints/cdc_upsert")
.start())Sample Answer
Sample Answer
Sample Answer
version: 2
rules:
- id: orders_rowcount
description: "Row count shouldn't drop >10% vs baseline"
type: row_count
dataset: prod.orders
comparator: pct_change
threshold: -0.10
severity: high
- id: orders_total_nulls
description: "order_total should have <1% nulls"
type: null_rate
dataset: prod.orders
column: order_total
operator: "<="
threshold: 0.01
severity: medium
- id: order_amount_range
description: "order_amount within expected range"
type: value_range
dataset: prod.orders
column: order_amount
min: 0.0
max: 10000.0
severity: high
- id: order_amount_dist
description: "Distribution drift vs baseline (PSI)"
type: histogram_drift
dataset: prod.orders
column: order_amount
bins: 20
metric: psi
threshold: 0.10
severity: medium
- id: fk_orders_customers
description: "orders.customer_id must reference customers.id"
type: referential_integrity
left: prod.orders.customer_id
right: prod.customers.id
operator: >=
threshold: 0.995 # allowed fraction of valid refs
severity: highUnlock Full Question Bank
Get access to hundreds of Data Manipulation and Transformation interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.