What you will be able to do
- Remove fully identical rows from a DataFrame with distinct()
- Deduplicate on a chosen set of key columns with dropDuplicates(subset)
- Explain what dropDuplicates costs on a streaming DataFrame and how withWatermark limits it
Key concept
Duplicate comparison scope — Two rows are duplicates only with respect to the columns you compare. distinct() always compares every column. dropDuplicates() also compares every column by default, but it can be limited to a subset of key columns, which changes how many rows survive.
1.distinct(): removing rows that match in every column
Bronze data often contains exact copies of the same record, for example when a source system sends the same event twice. The simplest tool for this is distinct(). It takes no arguments and returns a new DataFrame that contains only the distinct rows. Two rows count as duplicates only when every column matches. Like other DataFrame transformations, it leaves the original DataFrame unchanged and gives you a new one.
df = spark.createDataFrame(
[(14, "Tom"), (23, "Alice"), (23, "Alice")], ["age", "name"])
df.distinct().show()
# +---+-----+
# |age| name|
# +---+-----+
# | 14| Tom|
# | 23|Alice|
# +---+-----+
df.distinct().count()
# 2Chaining count() after distinct() is the usual way to find out how many unique records a dataset really holds. Adding columns doesn't change the rule. In a second documented example, a three-column DataFrame with the rows (14, Tom, M), (23, Alice, F), (23, Alice, F) and (14, Tom, M) also reduces to two rows, because each pair matches in all three columns. distinct() has no parameter, so you can't tell it to compare only some of the columns. If two records share a business key but differ in a timestamp or some other column, distinct() keeps both.
Checkpoint 1 of 5· Check yourself
After running deduped = df.distinct(), what is true of df?
distinct() returns a new DataFrame and leaves the original untouched. The new DataFrame holds every distinct row, not only the rows that had duplicates.
“Returns a new DataFrame containing the distinct rows in this DataFrame.”Source: docs.databricks.com
Sources1
2.dropDuplicates(subset): deduplicating on key columns
dropDuplicates() covers both cases. Called with no arguments it compares all columns, just like distinct(). Its optional subset parameter takes a list of column names, and only those columns are used to decide whether two rows are duplicates. This is how you deduplicate on a business key while other columns differ.
| Call | Columns compared | Takes a column list? |
|---|---|---|
| distinct() | All columns | No |
| dropDuplicates() | All columns (the default) | Optional, omitted here |
| dropDuplicates(['name', 'height']) | Only name and height | Yes, through subset |
from pyspark.sql import Row
df = spark.createDataFrame([
Row(name='Alice', age=5, height=80),
Row(name='Alice', age=5, height=80),
Row(name='Alice', age=10, height=80)
])
df.dropDuplicates().show()
# +-----+---+------+
# | name|age|height|
# +-----+---+------+
# |Alice| 5| 80|
# |Alice| 10| 80|
# +-----+---+------+df.dropDuplicates(['name', 'height']).show()
# +-----+---+------+
# | name|age|height|
# +-----+---+------+
# |Alice| 5| 80|
# +-----+---+------+The row with age 10 is gone, even though its age differs. With a subset, the columns outside the subset don't stop two rows from being duplicates, but the row that survives still carries a value for each of them. In this example the kept row has age 5. The reference page doesn't say which row of a duplicate group is kept, so don't build logic on the assumption that a particular one, such as the first or the latest, will win.
Checkpoint 2 of 5· Fill the gap
Which method completes this call so that rows count as duplicates when only name and height match?
df. ? (['name', 'height']).show()dropDuplicates accepts a subset list of column names for the duplicate comparison. distinct() takes no arguments, drop removes columns, and dropna deals with nulls.
Source: docs.databricks.comCheckpoint 3 of 5· Check yourself
Which call returns the same rows as df.distinct()?
When subset is omitted, dropDuplicates compares all columns, which is exactly the comparison distinct() makes.
“List of columns to use for duplicate comparison (default All columns).”Source: docs.databricks.com
Checkpoint 4 of 5· Exam question
A data engineer loads a bronze table of clickstream events where upstream retries occasionally write the exact same row multiple times across all columns. Before writing to silver, the engineer wants to remove full-row duplicates while keeping the rest of the pipeline unchanged. Which transformation should the engineer apply to the DataFrame?
Correct answer: B — `df.distinct()` to compare every column across each row and keep only one copy of rows that are identical in all fields
- A. Restricting to a single column like `event_id` deduplicates on that key alone, so two rows sharing an ID but differing elsewhere would still be collapsed into one, which is not the same as removing exact full-row duplicates.
- B. Correct — `distinct()` considers all columns when comparing rows and returns only unique rows, so exact duplicate rows created by upstream retries are removed while distinct rows are preserved.
- C. Grouping by every column and filtering to a count of one would drop rows that appear more than once instead of keeping one copy of them, so any row written twice by a retry would be discarded entirely rather than deduplicated.
- D. `dropna()` removes rows containing null values and has no relationship to identifying or removing rows that are byte-for-byte duplicates of each other.
Sources2
3.dropDuplicates on streaming DataFrames: state and watermarks
The same method behaves differently depending on the kind of DataFrame. On a static batch DataFrame, all the rows are available at once, so dropDuplicates just removes the duplicates and finishes. A streaming DataFrame never has all of its data at once. A duplicate of a record seen in an earlier trigger could arrive in a later one. To catch it, Spark must remember what it has already seen.
The documentation answers this directly. For a streaming DataFrame, dropDuplicates keeps all data across triggers as intermediate state, so with no limit that state keeps growing. The control for this is withWatermark. A watermark sets how late duplicate data can be, and the system limits the state to match. There is a cost: data older than the watermark is dropped, so a very late duplicate can't slip through as a new row.
Checkpoint 5 of 5· Check yourself
A streaming job deduplicates events with dropDuplicates and its state keeps growing. What does the documentation recommend to bound the state?
withWatermark tells the system how late duplicates may arrive, so it can limit the state it keeps. The other options don't affect how state is retained.
“You can use withWatermark to limit how late the duplicate data can be and the system will accordingly limit the state.”Source: docs.databricks.com
Sources2
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.distinct() can be given a list of key columns so that it deduplicates on those columns only.Why is that wrong?
distinct() takes no parameters and always compares whole rows. To deduplicate on chosen columns, use dropDuplicates with a subset.
Covered in distinct(): removing rows that match in every column
2.dropDuplicates works the same way on a streaming DataFrame as on a batch DataFrame, at no extra cost.Why is that wrong?
On a streaming DataFrame it keeps data across triggers as intermediate state. You need withWatermark to bound that state, and data older than the watermark is dropped.
Covered in dropDuplicates on streaming DataFrames: state and watermarks
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Returns a new DataFrame containing the distinct rows in this DataFrame.”
↩︎ distinct(): removing rows that match in every column - 2.
“List of columns to use for duplicate comparison (default All columns).”
↩︎ dropDuplicates(subset): deduplicating on key columns“In addition, data older than watermark will be dropped to avoid any possibility of duplicates.”
↩︎ dropDuplicates on streaming DataFrames: state and watermarks“List of columns to use for duplicate comparison (default All columns).”
↩︎ Key concept“Return a new DataFrame with duplicate rows removed, optionally only considering certain columns.”
↩︎ Exam trap 1“For a streaming DataFrame, it will keep all data across triggers as intermediate state to drop duplicates rows.”
↩︎ Exam trap 2“Return a new DataFrame with duplicate rows removed, optionally only considering certain columns.”
↩︎ Prediction“You can use withWatermark to limit how late the duplicate data can be and the system will accordingly limit the state.”
↩︎ Checkpoint