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