Real-world data is rarely clean. A column might have missing values
that are easy to overlook. A few extreme (outlier) values might sneak
into an otherwise normal numeric column. Rows sometimes get duplicated
by accident during data collection or when merging files. A column that
should be numeric can end up stored as text, just because a few entries
contain things like "N/A" or "unknown". And a
column of dates can have a few entries that don’t match the format the
rest of the column uses.
validateR is a small set of tools that helps you catch
these problems before they affect your analysis. It checks a data frame
for five common issues:
This vignette walks through a complete example: starting with a messy data frame, running a check, and reading the results.
Let’s create a small data frame with a few realistic problems: a missing value, an outlier, a duplicated row, and a text column that should probably be numeric.
messy_data <- data.frame(
age = c(25, 12, NA, 17, 19),
weight = c("45", "34", "25", "N/A", "32"),
price = c(100, 120, 140, 1500, 125)
)
messy_data <- rbind(messy_data, messy_data[3, ])
messy_data
#> age weight price
#> 1 25 45 100
#> 2 12 34 120
#> 3 NA 25 140
#> 4 17 N/A 1500
#> 5 19 32 125
#> 31 NA 25 140validate_df()The main entry point is validate_df(). It runs four
checks automatically, and returns a single report object. A fifth check,
for date-like columns, is available too, but only runs if you ask for it
— more on that later in this vignette.
report <- validate_df(messy_data)
report
#> < validation_report for 'messy_data' >
#> 6 rows, 3 columns
#> Checked: 2026-08-31 19:26:23
#>
#> Missing values: 1 column(s) affected
#> Outliers: 1 column(s) affected
#> Duplicates: 1 group(s) affected
#> Numeric-like issues: 1 column(s) affected
#> Date-like issues: NOT checked
#>
#> For details, inspect report$results$missing, $outliers, $duplicates, $numeric_like, $date_likeThe printed summary tells you how many columns were affected
by each check, but not the specifics. The full results are stored inside
the report object, under report$results, and are worth
exploring directly when you need to act on what was found.
report$results$missing is a data frame with one row per
column, showing the count and percentage of missing values.
report$results$missing
#> column_name n_missing pct_missing
#> 1 age 2 33.33
#> 2 weight 0 0.00
#> 3 price 0 0.00Here, age has two missing values out of six rows, while
weight and price have none. If a column had a
high percentage of missing values, this table would make that
immediately clear, without needing to visually inspect the raw data.
report$results$outliers is also a data frame, one row
per numeric column, showing how many values were flagged as
outliers using the IQR method.
price shows one outlier, which is the 1500
value standing well apart from the rest of the column. age
shows none. Note that non-numeric columns, like weight,
don’t appear here at all, since outlier detection only makes sense for
numeric data.
report$results$duplicates is a list, rather than a data
frame. Each element, representing a group of duplicate rows, has row
numbers that are exact duplicates of each other.
This shows one group, containing rows 3 and 6 — the row we duplicated on purpose when building the example data. If there were several separate groups of duplicate rows, each would appear as its own element in this list.
report$results$numeric_like is a named list, with one
element per column that looks like it was meant to be numeric but
contains some values that don’t convert cleanly.
report$results$numeric_like
#> $weight
#> $weight$expected_type
#> [1] "numeric"
#>
#> $weight$proportion_convertible
#> [1] 83.33
#>
#> $weight$bad_rows
#> [1] 4The weight column is flagged here: about 83% of its
values convert to numbers, and the one value that doesn’t
("N/A", in row 4) is listed under bad_rows.
This is enough information to go back to the original data and decide
whether to fix, remove, or investigate that value.
For a quick visual overview, plot() shows two bar
charts: missing value counts and outlier counts, one column per bar.
Sometimes you don’t need the full report — maybe you already know
your data is fine except for one specific concern. Each check function
used by validate_df() is also exported and can be used on
its own.
check_outliers(messy_data)
#> column_name n_outliers pct_outliers
#> 1 age 0 0.00
#> 2 price 1 16.67This runs just the outlier check, without computing the others. The
same applies to check_missing(),
check_duplicates(), check_numeric_like(), and
check_date_like().
Some of the checks make a judgment call about what counts as a problem. It’s worth understanding how each one decides, so you know when to trust the default behavior and when you might want to inspect the results more closely yourself.
check_outliers() flags a value as an outlier using a
method commonly used in boxplots. For each numeric column, it calculates
the interquartile range (IQR) — the spread between the 25th and 75th
percentile of the values. A value is flagged if it falls more than 1.5
times the IQR below the 25th percentile, or more than 1.5 times the IQR
above the 75th percentile.
This method doesn’t assume the data follows any particular distribution, and it adapts to the natural spread of each column, so a