---
title: "Why myIO"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Why myIO}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
library(myIO)
```
## The problem
R has excellent interactive visualization packages. So why build another one?
myIO addresses a specific gap in the ecosystem:
1. **Translation layers lose statistical annotations.** Converting
`ggplot2` objects to interactive formats can drop confidence interval
ribbons, regression equations, and label geoms. This is an inherent
limitation of translating between grammars.
2. **Statistical overlays require manual assembly.** With most interactive
packages, users must pre-compute confidence intervals, regression
diagnostics, and error bars in R, then manually layer them onto charts.
This is tedious and error-prone.
3. **Native R-side computation enables composability.** By computing
transforms in R and rendering in D3.js, myIO avoids translation
artifacts entirely. The statistical layer and the rendering layer
connect through a clean data contract.
myIO solves this with a **composable transform pipeline**: statistical
computation happens in R (where it belongs), rendering happens in D3.js
(where it's beautiful), and the two connect through a clean data contract.
## What myIO does differently
### 1. Confidence intervals as first-class layers
Confidence interval bands are notoriously difficult to render correctly in
interactive charts built through translation layers. myIO computes CI bands
natively in R and renders them as a first-class area layer:
```{r ci-example, eval = FALSE}
myIO(data = mtcars) |>
addIoLayer(type = "point", color = "#4E79A7", label = "Data",
mapping = list(x_var = "wt", y_var = "mpg")) |>
addIoLayer(type = "line", color = "#E15759", label = "Trend",
transform = "lm",
mapping = list(x_var = "wt", y_var = "mpg")) |>
addIoLayer(type = "area", color = "#E15759", label = "95% CI",
transform = "ci",
mapping = list(x_var = "wt", y_var = "mpg"),
options = list(level = 0.95))
```
The `ci` transform uses `stats::predict()` with `interval = "confidence"` and
outputs a grid of `low_y` / `high_y` bounds. The area renderer displays them
as a smooth band. Prediction intervals work the same way with
`options = list(interval = "prediction")`.
### 2. One-line regression with R² annotation
Displaying a regression equation alongside an interactive chart typically
requires multiple packages and manual assembly. myIO's `regression` composite
handles the entire workflow in one call:
```{r regression-example, eval = FALSE}
myIO(data = mtcars) |>
addIoLayer(type = "regression", label = "MPG vs Weight",
mapping = list(x_var = "wt", y_var = "mpg"),
options = list(method = "lm", showCI = TRUE, showStats = TRUE))
```
This auto-expands into four sub-layers: scatter points, trend line, CI band,
and an R² annotation — all styled consistently. Change `method` to `"loess"`
or `"polynomial"` to switch the model.
### 3. Composable transforms
Every myIO chart uses the same pattern: **type + transform = layer**. Transforms
are R functions that compute statistical summaries and return a data frame. The
renderer doesn't know or care what the numbers mean — it just plots fields.
This means you can compose transforms freely:
```{r compose-example, eval = FALSE}
# Scatter + LOESS smooth + CI band + residual plot
myIO(data = mtcars) |>
addIoLayer(type = "point", label = "Data",
mapping = list(x_var = "wt", y_var = "mpg")) |>
addIoLayer(type = "line", label = "LOESS", transform = "loess",
mapping = list(x_var = "wt", y_var = "mpg"),
options = list(span = 0.5))
```
Available transforms:
| Transform | What it computes | Output |
|-----------|-----------------|--------|
| `"lm"` | Linear regression fitted values | Line |
| `"loess"` | LOESS non-linear smoothing | Line |
| `"polynomial"` | Polynomial regression (degree N) | Line |
| `"ci"` | Confidence or prediction interval | Area band |
| `"smooth"` | Simple or exponential moving average | Line |
| `"mean"` | Group mean | Points or bars |
| `"mean_ci"` | Group mean ± confidence interval | Range bars (error bars) |
| `"residuals"` | Regression residuals vs. fitted | Points |
| `"summary"` | Aggregation (count, sum, sd, var, min, max) | Points or bars |
All transforms use base R's `stats` package — no additional dependencies.
### 4. Composite charts expand into primitives
Complex statistical charts like boxplots and violins are defined as
**composites** that auto-expand into simpler layers:
```
boxplot → rangeBar (IQR) + point (whisker caps) + point (median) + point (outliers)
violin → area (density) + rangeBar (IQR box) + point (median)
regression → point (scatter) + line (trend) + area (CI band) + text (R²)
```
Each sub-layer renders independently, which means:
- The IQR box can have a different color than the whiskers
- Outliers can be toggled on/off via `options = list(showOutliers = TRUE)`
- Tooltips work on every sub-component
- Animations are layered (whiskers grow from box edges, then caps appear)
You don't need to know about this decomposition — just call
`addIoLayer(type = "boxplot", ...)` and it works. But if you need custom
control, you can compose the sub-layers manually.
### 5. Error bars with one line
Mean ± confidence interval bars are a standard in scientific publications.
In most interactive viz packages, you must compute the CI yourself and
manually assemble the layers.
myIO does it in one call:
```{r mean-ci-example, eval = FALSE}
myIO(data = iris) |>
addIoLayer(type = "rangeBar", label = "Mean ± 95% CI",
transform = "mean_ci",
mapping = list(x_var = "Species", y_var = "Sepal.Length"),
options = list(level = 0.95)) |>
defineCategoricalAxis(xAxis = TRUE) |>
setAxisFormat(xLabel = "Species", yLabel = "Sepal Length")
```
The `mean_ci` transform computes the group mean, standard error, and
t-distribution CI bounds — matching `stats::t.test()` output exactly.
## Design philosophy
myIO follows three principles:
1. **R computes, D3 renders.** Statistical computation stays in R where the
ecosystem is strongest. JavaScript handles rendering and interaction.
There is no translation layer that can break.
2. **Type + transform = layer.** Every chart is built from the same two
concepts. This makes the API small and predictable — 15 transforms and
18 chart types compose into hundreds of visualizations.
3. **Composites expand into primitives.** Complex charts decompose into
simpler ones. This means new statistical overlays don't require new
renderers — they reuse existing ones.
### 6. Bidirectional I/O
Most charting libraries are output-only: data goes in, a picture comes out.
myIO is an **information system** — user interactions flow back as structured
data:
- `setBrush()`: rectangle select returns selected rows
- `setAnnotation()`: click to label points; export annotations as a data frame
- `setLinked()`: Crosstalk cross-widget brushing
- `setSlider()`: parameter sliders that trigger R recomputation
This means the visualization is not just a display — it is an input device.
## When to use myIO
myIO is a good fit when you need:
- Interactive statistical charts in Shiny apps or R Markdown
- Confidence intervals, regression lines, or error bars that render correctly
- Composable overlays (scatter + trend + CI band + annotation)
- Brush selection that returns data, not just visual feedback
- Click-to-annotate for flagging outliers, events, or regions of interest
- Linked views across multiple charts via Crosstalk
- Moving average or smoothing overlays on time-series data
- Regression diagnostics (residual plots)
- Publication-quality D3.js rendering with minimal code
## When to use something else
- **ggplot2 + ggiraph**: If you need the full ggplot2 grammar and just want
hover/click interactivity added on top. ggiraph faithfully preserves ggplot2
output.
- **echarts4r**: If you need 30+ chart types and don't mind learning a
non-ggplot2 API. echarts4r has broader chart coverage.
- **plotly**: If you have existing ggplotly() code that works and don't need
CI bands or statistical annotations.