validateR is a lightweight toolkit for checking data frames for common
data quality issues: missing values, outliers, duplicate rows,
numeric-like inconsistencies, and (optionally) date-like
inconsistencies. Results are returned as a structured report object with
print and plot methods, so you can quickly see what’s wrong with
your data before you start analyzing it.
You can install the development version of validateR from GitHub with:
# pak::pak("UgyenNorbu/validateR")
install.packages("validateR", repos = c('https://ugyennorbu.r-universe.dev', 'https://cloud.r-project.org'))
The main entry point is validate_df(), which runs several checks at
once and returns a single report:
library(validateR)
messy_df <- data.frame(
age = c(25, 30, NA, 45, 22, 31, 29, 200),
income = c("50000", "62000", "N/A", "48000",
"71000", "55000", "60000", "48000"),
city = c("Thimphu", "Paro", "Thimphu", "Paro",
"Thimphu", "Paro", "Punakha", "Paro"),
stringsAsFactors = FALSE
)
messy_df <- rbind(messy_df, messy_df[1, ], messy_df[4, ])
report <- validate_df(messy_df)
report
#> < validation_report for 'messy_df' >
#> 10 rows, 3 columns
#> Checked: 2026-08-11 14:36:44
#>
#> Missing values: 1 column(s) affected
#> Outliers: 1 column(s) affected
#> Duplicates: 2 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_like
You can drill into the full detail behind any of the checks:
report$results$missing
#> column_name n_missing pct_missing
#> 1 age 1 10
#> 2 income 0 0
#> 3 city 0 0
report$results$numeric_like
#> $income
#> $income$expected_type
#> [1] "numeric"
#>
#> $income$proportion_convertible
#> [1] 90
#>
#> $income$bad_rows
#> [1] 3
And visualize missing values and outliers per column:

validate_df() can also check whether character columns contain values
that look like dates in a format you specify. This check is optional,
since date formats are ambiguous (for example, "03/04/2023" could mean
March 4th or April 3rd, depending on convention) — so rather than
guessing, you tell it what format to expect for each column you want
checked.
dated_df <- data.frame(
signup_date = c("2023-01-15", "2023-02-20", "not a date",
"2023-03-10", "2023-04-01"),
stringsAsFactors = FALSE
)
report_with_dates <- validate_df(
dated_df,
date_columns = c(signup_date = "%Y-%m-%d")
)
report_with_dates
#> < validation_report for 'dated_df' >
#> 5 rows, 1 columns
#> Checked: 2026-08-11 14:36:44
#>
#> Missing values: 0 column(s) affected
#> Outliers: 0 column(s) affected
#> Duplicates: 0 group(s) affected
#> Numeric-like issues: 0 column(s) affected
#> Date-like issues: 1 column(s) affected
#>
#> For details, inspect report$results$missing, $outliers, $duplicates, $numeric_like, $date_like
If date_columns isn’t supplied, validate_df() skips date checking
entirely, and the report notes that it wasn’t checked rather than
implying the column was fine:
report_without_dates <- validate_df(dated_df)
report_without_dates
#> < validation_report for 'dated_df' >
#> 5 rows, 1 columns
#> Checked: 2026-08-11 14:36:44
#>
#> Missing values: 0 column(s) affected
#> Outliers: 0 column(s) affected
#> Duplicates: 0 group(s) affected
#> Numeric-like issues: 0 column(s) affected
#> Date-like issues: NOT checked
#>
#> For details, inspect report$results$missing, $outliers, $duplicates, $numeric_like, $date_like
Each check is also available on its own, if you only need one:
check_duplicates(messy_df)
#> [[1]]
#> [1] 1 9
#>
#> [[2]]
#> [1] 4 10