---
title: "validateR"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{validateR}
  %\VignetteEncoding{UTF-8}
  %\VignetteEngine{knitr::rmarkdown}
editor_options: 
  markdown: 
    wrap: 72
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```
## Introduction

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:

- **Missing values** — how many values are missing in each column, and
  what percentage that represents
- **Outliers** — unusually extreme numeric values, detected using the
  IQR method
- **Duplicate rows** — rows that are exact copies of each other, grouped
  so you can see which rows match
- **Numeric-like inconsistencies** — text columns that look like they
  were meant to hold numbers, but have a few values that don't convert
  cleanly
- **Date-like inconsistencies** (optional) — text columns that look
  like they were meant to hold dates in a format you specify, but have
  a few values that don't parse cleanly

This vignette walks through a complete example: starting with a messy
data frame, running a check, and reading the results.

```{r setup}
library(validateR)
```

## A messy example

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.

```{r create-data}
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
```

## Running `validate_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.

```{r run-validate}
report <- validate_df(messy_data)
report
```

## Looking at the details

The 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.

### Missing values

`report$results$missing` is a data frame with one row per column,
showing the count and percentage of missing values.

```{r}
report$results$missing
```

Here, `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.

### Outliers

`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.

```{r}
report$results$outliers
```

`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.

### Duplicate rows

`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.

```{r}
report$results$duplicates
```

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.

### Numeric-like inconsistencies

`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.

```{r}
report$results$numeric_like
```

The `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.

## Visualizing the results

For a quick visual overview, `plot()` shows two bar charts: missing
value counts and outlier counts, one column per bar.

```{r, fig.width=6, fig.height=6}
plot(report)
```

## Running checks individually

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.

```{r}
check_outliers(messy_data)
```

This runs just the outlier check, without computing the others. The
same applies to `check_missing()`, `check_duplicates()`,
`check_numeric_like()`, and `check_date_like()`.

## How the thresholds work

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.

### Outliers: the IQR method

`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
