Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/tjmahr/printy

Tools for pretty-printing numbers in RMarkdown reports
https://github.com/tjmahr/printy

formatted-text r rmarkdown

Last synced: about 1 month ago
JSON representation

Tools for pretty-printing numbers in RMarkdown reports

Awesome Lists containing this project

README

        

---
output:
github_document:
default
---

```{r, include = FALSE}
library(dplyr, warn.conflicts = FALSE)
library(lme4)

knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "fig/README-"
)
```

# printy

Over the years, I've written a lot of one-off functions for formatting numbers
in RMarkdown documents. This packages collects them in a single location.

## Installation ๐Ÿ“š

You can install printy from github with:

```{r gh-installation, eval = FALSE}
# install.packages("remotes")
remotes::install_github("tjmahr/printy")
```

## Formatters โœ

`fmt_fix_digits()` prints a number with n digits of precision. R numbers lose
precision when converted to strings. This function converts the numbers to
strings and keeps precision. (It's a wrapper for `sprintf()`.)

```{r fix-digits}
library(dplyr)
library(printy)
test_cor <- cor(mtcars[, 1:4])

# Typical loss of trailing zeroes
test_cor[1:4, 3] |> round(2) |> as.character()

test_cor[1:4, 3] |> fmt_fix_digits(2)
```

`fmt_leading_zero()` removes a leading zero on numbers that are bounded between
โˆ’1 and 1, such as correlations or *p*-values.

```{r leading-zero}
fmt_leading_zero(c(-0.3, 0.4, 1))
```

`fmt_minus_sign()` formats negative numbers with a minus sign.

```{r minus-sign}
fmt_minus_sign(c(1, 2, -3, -0.4, -pi))
```

Putting it all together: Print a correlation matrix with 2 digits, no leading
zero and with minus signs.

```{r}
fmt_correlation <- function(xs, digits = 2) {
xs |> fmt_fix_digits(digits) |> fmt_leading_zero() |> fmt_minus_sign()
}

test_cor |>
as.data.frame() |>
tibble::rownames_to_column(".rowname") |>
tibble::as_tibble() |>
mutate(
across(-.rowname, fmt_correlation)
) |>
rename(` ` = .rowname) |>
knitr::kable(align = "lrrrr")
```

### *p*-values ๐ŸŽฃ

`fmt_p_value()` formats *p*-values with *n* digits of precision, with no leading
zero, and with very small values being printed with a `<` sign.

```{r}
p <- c(1, 0.1, 0.01, 0.001, 0.0001)
fmt_p_value(p, digits = 2)
fmt_p_value(p, digits = 3)
```

`fmt_p_value_md()` formats *p*-values in markdown with nice defaults.

* Use 3 digits of precision for values less than .06
* Otherwise, use 2 digits of precision.
* Include *p* in markdown

```{r}
p <- c(1, 0.1, 0.06, 0.059, 0.051, 0.01, 0.001, 0.0001)
fmt_p_value_md(p)
```

These render as: `r paste0(fmt_p_value_md(p), collapse = ", ")`.

### Experimental formatters ๐Ÿงช

`fmt_effect_md()` is an experimental function for getting model effects
formatted in markdown. You give the function a model, an effect and a string
listing the quantities you want.

```{r}
model <- lm(breaks ~ wool * tension, warpbreaks)
summary(model)
```

```{r}
# default to: b (beta), e (error), s (statistic), p (p value)
fmt_effect_md(model, "woolB", "besp")
```

`r fmt_effect_md(model, "woolB", "besp")`

```{r}
# Just a subset of them
fmt_effect_md(model, "woolB", terms = "bp")
```

`r fmt_effect_md(model, "woolB", terms = "bp")`

```{r}
# B for labeled b
fmt_effect_md(model, "woolB", terms = "Bp", b_lab = "Wool B")
```

`r fmt_effect_md(model, "woolB", terms = "Bp", b_lab = "Wool B")`

```{r bi}
# i for interval
fmt_effect_md(model, "woolB", terms = "bi")
```

`r fmt_effect_md(model, "woolB", terms = "bi")`

```{r bSp}
# S for statistic with df
fmt_effect_md(model, "woolB", terms = "bSp")
```

`r fmt_effect_md(model, "woolB", terms = "bSp")`

```{r}
# extra digits (except for p-values; those go through `fmt_p_value_md()`)
fmt_effect_md(model, "woolB", terms = "bep", digits = 6)
```

`r fmt_effect_md(model, "woolB", terms = "bep", digits = 6)`

These are the currently supported models:

- `lm()`
- `lme4::lmer()`

For lme4 models, Wald confidence intervals are provided. For *p*-values, the
Kenwood--Roger approximation for the degrees of freedom is used by default. We
can also choose a [method supported by the parameters
package](https://easystats.github.io/parameters/reference/p_value.lmerMod.html).

```{r}
library(lme4)
data(Machines, package = "nlme")

m <- lmer(score ~ 1 + Machine + (Machine | Worker), data = Machines)

# Default is Kenward
fmt_effect_md(m, "MachineB", terms = "beSp")
fmt_effect_md(m, "MachineB", terms = "beSp", p_value_method = "kenward")

# Note residual degrees of freedom for Wald
fmt_effect_md(m, "MachineB", terms = "beSp", p_value_method = "wald")

# This example doesn't find differences between Satterthwaite and Kenward
fmt_effect_md(m, "MachineB", terms = "beSp", p_value_method = "satterthwaite")
```

We can also format effects from `glmer()` models. `"S"` is not supported because
the model summary uses *z* statistics, not *t* statistics.

```{r, error = TRUE}
gm1 <- glmer(
cbind(incidence, size - incidence) ~ period + (1 | herd),
data = cbpp,
family = binomial
)

round(coef(summary(gm1)), 3)

fmt_effect_md(gm1, "period2", terms = "bespi")

# Don't use S here
fmt_effect_md(gm1, "period2", terms = "beSp")
```

Skeletons ๐Ÿฆด
-----------------------------------------------------------------------

I use `fmt_` for formatting functions. The other convention in the package is
`skel_` to plug values into a formatting skeleton.

`skel_conf_interval_pair()` creates a confidence interval from two numbers.

```{r}
skel_conf_interval_pair(c(1, 2))
```

`skel_conf_interval()` is the vectorized version. It is suitable for working
on columns of numbers.

```{r}
model <- lm(breaks ~ wool * tension, warpbreaks)

ci_starts <- confint(model)[, 1] |>
fmt_fix_digits(2) |>
fmt_minus_sign()

ci_ends <- confint(model)[, 2] |>
fmt_fix_digits(2) |>
fmt_minus_sign()

skel_conf_interval(ci_starts, ci_ends)
```

`skel_stat_n_value_pair()` creates *t*-test-like or correlation-like statistic
from a vector of two numbers.

```{r}
skel_stat_n_value_pair(c("20", "2.0"))
skel_stat_n_value_pair(c("39", ".98"), stat = "*r*")
```

`skel_se()` and `skel_ci()` are shorthand functions to help with inline
reporting.

```{r}
skel_se(c(10, 4))

skel_ci(c("[1, 2]"))

skel_ci(c("[1, 2]"), ci_width = 90)
```

## Formatting tables from lme4 models ๐Ÿ–‡

One thing I've had to do a lot is summarize mixed effects models fit with lme4.
This package provides `pretty_lme4_ranefs()` which creates a dataframe random
effect variances and covariances like those printed by `summary()`.

For example, we can fit the model.

```{r}
library(lme4)
model <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
summary(model)
```

`pretty_lme4_ranefs()` creates the following dataframe.

```{r}
pretty_lme4_ranefs(model)
```

Which in markdown renders as

```{r}
knitr::kable(
pretty_lme4_ranefs(model),
align = c("l", "l", "r", "r", "r")
)
```

Here's a dumb model with a lot going on in the random effects.

```{r, warning = FALSE}
model <- lmer(mpg ~ wt * hp + (drat | gear) + (hp * cyl | am), mtcars)
model

knitr::kable(
pretty_lme4_ranefs(model),
align = c("l", "l", "r", "r", "r", "r", "r", "r", "r")
)
```