{"id":13857528,"url":"https://github.com/tjmahr/printy","last_synced_at":"2026-03-07T17:31:29.825Z","repository":{"id":65407558,"uuid":"90981371","full_name":"tjmahr/printy","owner":"tjmahr","description":"Tools for pretty-printing numbers in RMarkdown reports","archived":false,"fork":false,"pushed_at":"2024-03-01T16:25:41.000Z","size":130,"stargazers_count":52,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-01-13T03:41:33.432Z","etag":null,"topics":["formatted-text","r","rmarkdown"],"latest_commit_sha":null,"homepage":null,"language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tjmahr.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-11T13:19:14.000Z","updated_at":"2025-10-06T09:21:30.000Z","dependencies_parsed_at":"2024-11-12T04:43:37.781Z","dependency_job_id":"db6255fd-46c2-4115-ae91-fec5ae7132da","html_url":"https://github.com/tjmahr/printy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tjmahr/printy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tjmahr%2Fprinty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tjmahr%2Fprinty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tjmahr%2Fprinty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tjmahr%2Fprinty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tjmahr","download_url":"https://codeload.github.com/tjmahr/printy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tjmahr%2Fprinty/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30223248,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T17:00:40.062Z","status":"ssl_error","status_checked_at":"2026-03-07T17:00:39.026Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formatted-text","r","rmarkdown"],"created_at":"2024-08-05T03:01:39.761Z","updated_at":"2026-03-07T17:31:29.757Z","avatar_url":"https://github.com/tjmahr.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: \n  github_document: \n    default\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nlibrary(dplyr, warn.conflicts = FALSE)\nlibrary(lme4)\n\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"fig/README-\"\n)\n```\n\n# printy\n\nOver the years, I've written a lot of one-off functions for formatting numbers\nin RMarkdown documents. This packages collects them in a single location.\n\n## Installation 📚\n\nYou can install printy from github with:\n\n```{r gh-installation, eval = FALSE}\n# install.packages(\"remotes\")\nremotes::install_github(\"tjmahr/printy\")\n```\n\n## Formatters ✍ \n\n`fmt_fix_digits()` prints a number with n digits of precision. R numbers lose\nprecision when converted to strings. This function converts the numbers to\nstrings and keeps precision. (It's a wrapper for `sprintf()`.)\n\n```{r fix-digits}\nlibrary(dplyr)\nlibrary(printy)\ntest_cor \u003c- cor(mtcars[, 1:4]) \n\n# Typical loss of trailing zeroes\ntest_cor[1:4, 3] |\u003e round(2) |\u003e as.character()\n\ntest_cor[1:4, 3] |\u003e fmt_fix_digits(2)\n```\n\n`fmt_leading_zero()` removes a leading zero on numbers that are bounded between\n−1 and 1, such as correlations or *p*-values.\n\n```{r leading-zero}\nfmt_leading_zero(c(-0.3, 0.4, 1))\n```\n\n`fmt_minus_sign()` formats negative numbers with a minus sign.\n\n```{r minus-sign}\nfmt_minus_sign(c(1, 2, -3, -0.4, -pi))\n```\n\nPutting it all together: Print a correlation matrix with 2 digits, no leading\nzero and with minus signs.\n\n```{r}\nfmt_correlation \u003c- function(xs, digits = 2) {\n  xs |\u003e fmt_fix_digits(digits) |\u003e fmt_leading_zero() |\u003e fmt_minus_sign()\n}\n\ntest_cor |\u003e \n  as.data.frame() |\u003e \n  tibble::rownames_to_column(\".rowname\") |\u003e \n  tibble::as_tibble() |\u003e \n  mutate(\n    across(-.rowname, fmt_correlation)\n  ) |\u003e \n  rename(` ` = .rowname) |\u003e \n  knitr::kable(align = \"lrrrr\")\n```\n\n### *p*-values 🎣\n\n`fmt_p_value()` formats *p*-values with *n* digits of precision, with no leading\nzero, and with very small values being printed with a `\u003c` sign.\n\n```{r}\np \u003c- c(1, 0.1, 0.01, 0.001, 0.0001)\nfmt_p_value(p, digits = 2)\nfmt_p_value(p, digits = 3)\n```\n\n`fmt_p_value_md()` formats *p*-values in markdown with nice defaults. \n\n* Use 3 digits of precision for values less than .06\n* Otherwise, use 2 digits of precision.\n* Include *p* in markdown\n\n```{r}\np \u003c- c(1, 0.1, 0.06, 0.059, 0.051, 0.01, 0.001, 0.0001)\nfmt_p_value_md(p)\n```\n\nThese render as: `r paste0(fmt_p_value_md(p), collapse = \", \")`.\n\n\n### Experimental formatters  🧪\n\n`fmt_effect_md()` is an experimental function for getting model effects\nformatted in markdown. You give the function a model, an effect and a string\nlisting the quantities you want.\n\n```{r}\nmodel \u003c- lm(breaks ~ wool * tension, warpbreaks) \nsummary(model)\n```\n\n```{r}\n# default to: b (beta), e (error), s (statistic), p (p value)\nfmt_effect_md(model, \"woolB\", \"besp\")\n```\n\n`r fmt_effect_md(model, \"woolB\", \"besp\")`\n\n```{r}\n# Just a subset of them\nfmt_effect_md(model, \"woolB\", terms = \"bp\")\n```\n\n`r fmt_effect_md(model, \"woolB\", terms = \"bp\")`\n\n```{r}\n# B for labeled b\nfmt_effect_md(model, \"woolB\", terms = \"Bp\", b_lab = \"Wool B\")\n```\n\n`r  fmt_effect_md(model, \"woolB\", terms = \"Bp\", b_lab = \"Wool B\")`\n\n```{r bi}\n# i for interval\nfmt_effect_md(model, \"woolB\", terms = \"bi\")\n```\n\n`r fmt_effect_md(model, \"woolB\", terms = \"bi\")`\n\n```{r bSp}\n# S for statistic with df\nfmt_effect_md(model, \"woolB\", terms = \"bSp\")\n```\n\n`r fmt_effect_md(model, \"woolB\", terms = \"bSp\")`\n\n```{r}\n# extra digits (except for p-values; those go through `fmt_p_value_md()`)\nfmt_effect_md(model, \"woolB\", terms = \"bep\", digits = 6)\n```\n\n`r fmt_effect_md(model, \"woolB\", terms = \"bep\", digits = 6)`\n\nThese are the currently supported models:\n\n  - `lm()`\n  - `lme4::lmer()` \n\nFor lme4 models, Wald confidence intervals are provided. For *p*-values, the\nKenwood--Roger approximation for the degrees of freedom is used by default. We\ncan also choose a [method supported by the parameters\npackage](https://easystats.github.io/parameters/reference/p_value.lmerMod.html).\n\n```{r}\nlibrary(lme4)\ndata(Machines, package = \"nlme\")\n\nm \u003c- lmer(score ~ 1 + Machine + (Machine | Worker), data = Machines)\n\n# Default is Kenward\nfmt_effect_md(m, \"MachineB\", terms = \"beSp\")\nfmt_effect_md(m, \"MachineB\", terms = \"beSp\", p_value_method = \"kenward\")\n\n# Note residual degrees of freedom for Wald\nfmt_effect_md(m, \"MachineB\", terms = \"beSp\", p_value_method = \"wald\")\n\n# This example doesn't find differences between Satterthwaite and Kenward\nfmt_effect_md(m, \"MachineB\", terms = \"beSp\", p_value_method = \"satterthwaite\")\n```\n\nWe can also format effects from `glmer()` models. `\"S\"` is not supported because\nthe model summary uses *z* statistics, not *t* statistics.\n\n```{r, error = TRUE}\ngm1 \u003c- glmer(\n  cbind(incidence, size - incidence) ~ period + (1 | herd),\n  data = cbpp, \n  family = binomial\n)\n\nround(coef(summary(gm1)), 3)\n\nfmt_effect_md(gm1, \"period2\", terms = \"bespi\")\n\n# Don't use S here\nfmt_effect_md(gm1, \"period2\", terms = \"beSp\")\n```\n\n\n\nSkeletons 🦴\n-----------------------------------------------------------------------\n\nI use `fmt_` for formatting functions. The other convention in the package is\n`skel_` to plug values into a formatting skeleton. \n\n`skel_conf_interval_pair()` creates a confidence interval from two numbers.\n\n```{r}\nskel_conf_interval_pair(c(1, 2))\n```\n\n`skel_conf_interval()` is the vectorized version. It is suitable for working\non columns of numbers.\n\n```{r}\nmodel \u003c- lm(breaks ~ wool * tension, warpbreaks) \n\nci_starts \u003c- confint(model)[, 1] |\u003e \n  fmt_fix_digits(2) |\u003e \n  fmt_minus_sign()\n\nci_ends \u003c- confint(model)[, 2] |\u003e \n  fmt_fix_digits(2) |\u003e \n  fmt_minus_sign()\n\nskel_conf_interval(ci_starts, ci_ends)\n```\n\n`skel_stat_n_value_pair()` creates *t*-test-like or correlation-like statistic\nfrom a vector of two numbers.\n\n```{r}\nskel_stat_n_value_pair(c(\"20\", \"2.0\"))\nskel_stat_n_value_pair(c(\"39\", \".98\"), stat = \"*r*\")\n```\n\n`skel_se()` and `skel_ci()` are shorthand functions to help with inline\nreporting.\n\n```{r}\nskel_se(c(10, 4))\n\nskel_ci(c(\"[1, 2]\"))\n\nskel_ci(c(\"[1, 2]\"), ci_width = 90)\n```\n\n\n## Formatting tables from lme4 models 🖇\n\nOne thing I've had to do a lot is summarize mixed effects models fit with lme4.\nThis package provides `pretty_lme4_ranefs()` which creates a dataframe random\neffect variances and covariances like those printed by `summary()`.\n\nFor example, we can fit the model.\n\n```{r}\nlibrary(lme4)\nmodel \u003c- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)\nsummary(model)\n```\n\n`pretty_lme4_ranefs()` creates the following dataframe.\n\n```{r}\npretty_lme4_ranefs(model)\n```\n\nWhich in markdown renders as\n\n```{r}\nknitr::kable(\n  pretty_lme4_ranefs(model), \n  align = c(\"l\", \"l\", \"r\", \"r\", \"r\")\n)\n```\n\nHere's a dumb model with a lot going on in the random effects.\n\n```{r, warning = FALSE}\nmodel \u003c- lmer(mpg ~ wt * hp + (drat | gear) + (hp * cyl | am), mtcars)\nmodel\n\nknitr::kable(\n  pretty_lme4_ranefs(model), \n  align = c(\"l\", \"l\", \"r\", \"r\", \"r\", \"r\", \"r\", \"r\", \"r\")\n)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftjmahr%2Fprinty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftjmahr%2Fprinty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftjmahr%2Fprinty/lists"}