Compare Two Data Frames in R
R provides several ways to compare two data frames, depending on whether you need to check complete equality, identify changed values, find unmatched rows, compare column names, or match records by a key column.
This tutorial begins with the compare() function from the compare package and then covers commonly used base R and dplyr methods for inspecting specific differences.
Install and Load the compare Package in R
The compare() function discussed in the first part of this tutorial belongs to the compare package. Install the package once, and then load it in each new R session where it is required.
install.packages("compare")
library(compare)
Syntax of compare() for R Data Frames
The syntax of compare() is shown below.
compare(model, comparison,
equal = TRUE,
coerce = allowAll,
shorten = allowAll,
ignoreOrder = allowAll,
ignoreNameCase = allowAll,
ignoreNames = allowAll,
ignoreAttrs = allowAll,
round = FALSE,
ignoreCase = allowAll,
trim = allowAll,
dropLevels = allowAll,
ignoreLevelOrder = allowAll,
ignoreDimOrder = allowAll,
ignoreColOrder = allowAll,
ignoreComponentOrder = allowAll,
colsOnly = !allowAll,
allowAll = FALSE)
The two required arguments are model, which is treated as the reference object, and comparison, which is checked against the reference.
modelThe “correct” object.comparisonThe object to be compared with themodel.equalTest for equality if test for identity fails.coerceIf objects are not the same, allow coercion of comparsion to model class.shortenIf the length of one object is less than the other, shorten the longer object.ignoreOrderIgnore the order of values when comparing.ignoreNameCaseIgnore the case of names when comparing.ignoreNamesIgnore names attributes altogether.ignoreAttrsIgnore attributes altogether.roundIf objects are not the same, allow numbers to be rounded.ignoreCaseIgnore the case of string values.trimIgnore leading and trailing spaces in string values.dropLevelsIf factors are not the same, allow unused levels to be dropped.ignoreLevelOrderIgnore the order of factor levels.ignoreDimOrderIgnore the order of dimensions when comparing matrices, arrays, or tables.ignoreColOrderIgnore the order of columns when comparing data frames.ignoreComponentOrderIgnore the order of components when comparing lists.colsOnlyOnly transform columns (not rows) when comparing data frames.allowAllAllow any sort of transformation (almost; see Details).
Most comparisons only require the first two arguments. The remaining arguments control whether differences such as row order, column order, text case, attributes, factor levels, or numeric precision may be ignored.
Basic Comparison of Two R Data Frames with compare()
Consider two data frames named DF1 and DF2. They contain the same IDs, but the name in the fourth row is different.
> DF1 = data.frame(id=c(1,2,3,4), name=c("John", "Manu", "Surya", "Amith"))
> DF2 = data.frame(id=c(1,2,3,4), name=c("John", "Manu", "Surya", "Tinu"))
> DF1
id name
1 1 John
2 2 Manu
3 3 Surya
4 4 Amith
> DF2
id name
1 1 John
2 2 Manu
3 3 Surya
4 4 Tinu
>
Now compare DF2 against DF1.
> compare(DF1, DF2)
FALSE [TRUE, FALSE]
>
The result is FALSE because the data frames are not equal. The bracketed result indicates that one component matches while another does not.
When the data frames contain the same values, compare() returns TRUE.
> DF1 = data.frame(id=c(1,2,3,4), name=c("John", "Manu", "Surya", "Amith"))
> DF2 = data.frame(id=c(1,2,3,4), name=c("John", "Manu", "Surya", "Amith"))
> compare(DF1, DF2)
TRUE
Check Whether Two Data Frames Are Exactly Identical
Use base R’s identical() when you need a strict comparison. It checks values, data types, dimensions, column order, row order, names, and relevant attributes.
df1 <- data.frame(
id = c(1L, 2L, 3L),
score = c(81, 92, 75)
)
df2 <- data.frame(
id = c(1L, 2L, 3L),
score = c(81, 92, 75)
)
identical(df1, df2)
[1] TRUE
A difference in column type can make identical() return FALSE, even when the printed values look the same. For example, an integer column and a double column are not strictly identical.
Use all.equal() to Explain Data Frame Differences
The base R function all.equal() is useful when you want a descriptive result instead of only TRUE or FALSE. It reports the type of mismatch it finds.
df1 <- data.frame(id = 1:3, score = c(80, 90, 70))
df2 <- data.frame(id = 1:3, score = c(80, 91, 70))
all.equal(df1, df2)
[1] "Component “score”: Mean relative difference: 0.01111111"
For a safe Boolean test, wrap all.equal() in isTRUE(). This is necessary because all.equal() returns either TRUE or a character description.
isTRUE(all.equal(df1, df2))
[1] FALSE
Find Individual Cells That Differ Between Two Data Frames
When two data frames have the same dimensions and their rows and columns are already aligned, use an element-wise comparison to locate changed cells.
df1 <- data.frame(
id = 1:4,
name = c("John", "Manu", "Surya", "Amith"),
score = c(78, 85, 91, 73)
)
df2 <- data.frame(
id = 1:4,
name = c("John", "Manu", "Surya", "Tinu"),
score = c(78, 88, 91, 73)
)
difference_matrix <- df1 != df2
difference_matrix
id name score
[1,] FALSE FALSE FALSE
[2,] FALSE FALSE TRUE
[3,] FALSE FALSE FALSE
[4,] FALSE TRUE FALSE
Use which(..., arr.ind = TRUE) to obtain the row and column positions of the changed cells.
which(difference_matrix, arr.ind = TRUE)
row col
[1,] 4 2
[2,] 2 3
This approach assumes both data frames have the same shape and corresponding records appear in the same row. If rows may be reordered, align them by a key column before comparing values.
Handle NA Values While Comparing R Data Frames
A direct expression such as df1 != df2 can produce NA when either side contains a missing value. A missing-value-aware comparison should distinguish three cases: equal non-missing values, missing values on both sides, and a missing value on only one side.
different <- (is.na(df1) != is.na(df2)) |
(!is.na(df1) & !is.na(df2) & df1 != df2)
which(different, arr.ind = TRUE)
The first condition detects positions where only one data frame contains NA. The second condition compares values only when both entries are present.
Find Rows Present in One Data Frame but Not the Other
Use dplyr::anti_join() when you need complete rows that appear in one data frame but do not have a matching row in another. This is often more useful than checking strict equality.
library(dplyr)
df1 <- data.frame(
id = c(1, 2, 3, 4),
name = c("John", "Manu", "Surya", "Amith")
)
df2 <- data.frame(
id = c(1, 2, 3, 5),
name = c("John", "Manu", "Surya", "Tinu")
)
only_in_df1 <- anti_join(df1, df2, by = c("id", "name"))
only_in_df2 <- anti_join(df2, df1, by = c("id", "name"))
only_in_df1
only_in_df2
id name
1 4 Amith
id name
1 5 Tinu
When the data frames have identical column structures, base R’s setdiff() can also identify unmatched rows.
setdiff(df1, df2)
setdiff(df2, df1)
Run the comparison in both directions because setdiff(df1, df2) returns only rows found in df1 but not in df2.
Compare Matching Records by an ID Column
Two data frames may represent the same entities while storing them in a different row order. In that case, join the data frames by a stable key such as id and compare the paired columns.
library(dplyr)
df_old <- data.frame(
id = c(1, 2, 3),
score = c(80, 90, 70)
)
df_new <- data.frame(
id = c(3, 1, 2),
score = c(72, 80, 90)
)
comparison <- full_join(
df_old,
df_new,
by = "id",
suffix = c("_old", "_new")
) |>
mutate(changed = score_old != score_new)
comparison
id score_old score_new changed
1 1 80 80 FALSE
2 2 90 90 FALSE
3 3 70 72 TRUE
A full_join() also retains keys that exist in only one data frame. Missing values in the paired columns can therefore indicate added or removed records.
Compare Column Names in Two R Data Frames
Before comparing row values, confirm that both data frames contain the expected columns.
identical(names(df1), names(df2))
setdiff(names(df1), names(df2))
setdiff(names(df2), names(df1))
identical(names(df1), names(df2)) checks both column names and their order. The two setdiff() calls identify columns that occur in only one data frame.
To test whether both data frames contain the same column names regardless of order, sort the names first.
identical(sort(names(df1)), sort(names(df2)))
Compare Two Columns from Different Data Frames
If the rows are already aligned, compare individual columns directly.
df1$score == df2$score
which(df1$score != df2$score)
For records that are not guaranteed to be in the same order, first match the rows using an ID or another unique key.
matched_positions <- match(df1$id, df2$id)
df1$score == df2$score[matched_positions]
Ignore Row Order When Comparing Two Data Frames
When row order is not meaningful, sort both data frames using the same unique key and reset row names before comparing them.
df1_sorted <- df1[order(df1$id), ]
df2_sorted <- df2[order(df2$id), ]
row.names(df1_sorted) <- NULL
row.names(df2_sorted) <- NULL
identical(df1_sorted, df2_sorted)
The sorting column should uniquely identify each row. Sorting by a non-unique column may not align duplicate records consistently.
Compare Numeric Data Frames with a Tolerance
Floating-point calculations can produce very small differences even when two results are practically equivalent. Use all.equal() with a suitable tolerance instead of comparing decimal values with ==.
df1 <- data.frame(value = c(0.3, 1.5))
df2 <- data.frame(value = c(0.1 + 0.2, 1.50000001))
isTRUE(all.equal(df1, df2, tolerance = 1e-7))
[1] TRUE
Select a tolerance that is appropriate for the precision of the data. A tolerance that is too large can hide meaningful differences.
Choosing the Right R Data Frame Comparison Method
| Comparison requirement | Recommended method |
|---|---|
| Strictly identical values, types, order, and attributes | identical(df1, df2) |
| Descriptive explanation of a mismatch | all.equal(df1, df2) |
| Flexible comparison using the compare package | compare(df1, df2) |
| Changed cells in aligned data frames | df1 != df2 with which() |
| Rows found in only one data frame | anti_join() or setdiff() |
| Changed records matched by an ID | full_join() followed by column comparison |
| Different column names | setdiff(names(df1), names(df2)) |
| Nearly equal floating-point values | all.equal() with tolerance |
Common Problems When Comparing R Data Frames
- Comparing rows without a key: Two matching records may appear different only because their row order changed.
- Ignoring column types: Character, factor, integer, and double columns can print similarly but behave differently in strict comparisons.
- Using one-directional set difference: Run
setdiff()oranti_join()in both directions to find all unmatched rows. - Comparing values before checking the schema: Confirm column names, order, and data types before locating cell-level differences.
- Forgetting about missing values: Direct comparisons involving
NAmay returnNArather thanTRUEorFALSE. - Using exact equality for calculated decimals: Apply a justified numeric tolerance when comparing floating-point results.
Frequently Asked Questions about Comparing Data Frames in R
How do I check whether two data frames are exactly the same in R?
Use identical(df1, df2). It returns TRUE only when values, data types, row order, column order, names, dimensions, and relevant attributes match.
How do I find rows that are different between two R data frames?
Use dplyr::anti_join(df1, df2) to find rows in df1 that are absent from df2, and reverse the arguments to find rows present only in df2. Base R’s setdiff() is another option when both data frames have compatible columns.
How do I compare two data frames when their rows are in a different order?
Align both data frames using a unique key. You can sort both by the key and then compare them, or join them by that key and compare the resulting paired columns.
How do I compare column names in two R data frames?
Use identical(names(df1), names(df2)) to check names and order. Use setdiff(names(df1), names(df2)) and its reverse to list columns missing from either data frame.
What is the difference between identical() and all.equal() in R?
identical() performs a strict test and returns a single Boolean value. all.equal() is designed to assess near equality and describe mismatches, including small numeric differences. Use isTRUE(all.equal(x, y)) when a Boolean result is required.
Editorial QA Checklist for This R Data Frame Comparison Tutorial
- Confirm that the
comparepackage is installed before running the originalcompare()examples. - Verify that data frames are aligned by a unique key before using element-wise comparisons.
- Check column names, column order, and data types before interpreting value differences.
- Test missing-value comparisons with cases where both values are
NAand where only one value isNA. - Run row-difference methods in both directions so that added and removed records are both reported.
- Use a documented and domain-appropriate tolerance for floating-point comparisons.
Conclusion
In this R Tutorial, we learned how to compare two data frames using compare(), identical(), all.equal(), element-wise comparisons, joins, and set-difference operations. The correct method depends on whether you need strict equality, changed cells, unmatched rows, schema differences, key-based record matching, or tolerance-aware numeric comparison.
TutorialKart.com