• About Me
  • Research
  • Talks and Tutorials
  • Projects
    • Water Integrity Risk Index (WIRI)
    • Data Viz Book
    • Fintech Sandboxes (FIRSA)
    • easytable (R package)
  • Consilience Notes

easytable 2.1.0

Design-first regression tables for Word, HTML, and PDF

Tutorials
A short tutorial for easytable 2.1.0, an R package for creating regression tables with strong defaults.
Author

Alfredo Hernández Sánchez

Published

February 17, 2026

Doi

https://doi.org/10.5281/zenodo.18673550


About easytable

easytable is a small R package for producing regression tables with stable defaults. The aim is to make it easy to get a clean table that looks consistent across outputs and is comfortable to read. It prioritizes ease-of-use and design over flexibility and for now only support lm and glm models.

Installation

easytable is installed from this GitHub repository. You may need to install the devtools library first!

# If you do not have devtools installed run:
install.packages("devtools")
# Otherwise, run:
devtools::install_github("alfredo-hs/easytable")

A simple table

The simplest workflow is to fit a model and pass it to easytable().

library(easytable)

model <- lm(mpg ~ wt, data = mtcars)

easytable(model)

term

Model 1

(Intercept)

37.29 ***
(1.88)

wt

-5.34 ***
(0.56)

N

32

R sq.

0.75

Adj. R sq.

0.74

Significance: ***p < .01; **p < .05; *p < .1

Multiple models side by side

Below is a slightly richer example using the famous palmerpenguins dataset. We estimate a small sequence of models and then render them in a single table.

library(palmerpenguins)

m1 <- lm(body_mass_g ~ flipper_length_mm, data = penguins)
m2 <- lm(body_mass_g ~ flipper_length_mm + species, data = penguins)
m3 <- lm(body_mass_g ~ flipper_length_mm + species + island, data = penguins)
easytable(m1, m2, m3)

term

Model 1

Model 2

Model 3

(Intercept)

-5780.83 ***
(305.81)

-4031.48 ***
(584.15)

-4047.52 ***
(585.52)

flipper_length_mm

49.69 ***
(1.52)

40.71 ***
(3.07)

41.09 ***
(3.09)

species:Chinstrap

-206.51 ***
(57.73)

-205.59 ***
(70.35)

species:Gentoo

266.81 ***
(95.26)

199.81 *
(109.71)

island:Dream

-59.75
(75.74)

island:Torgersen

-101.92
(77.65)

N

342

342

342

R sq.

0.76

0.78

0.78

Adj. R sq.

0.76

0.78

0.78

Significance: ***p < .01; **p < .05; *p < .1

You can provide model labels to keep the table readable.

easytable(
  m1, m2, m3,
  model.names = c("Baseline", "With species", "Full model")
)

term

Baseline

With species

Full model

(Intercept)

-5780.83 ***
(305.81)

-4031.48 ***
(584.15)

-4047.52 ***
(585.52)

flipper_length_mm

49.69 ***
(1.52)

40.71 ***
(3.07)

41.09 ***
(3.09)

species:Chinstrap

-206.51 ***
(57.73)

-205.59 ***
(70.35)

species:Gentoo

266.81 ***
(95.26)

199.81 *
(109.71)

island:Dream

-59.75
(75.74)

island:Torgersen

-101.92
(77.65)

N

342

342

342

R sq.

0.76

0.78

0.78

Adj. R sq.

0.76

0.78

0.78

Significance: ***p < .01; **p < .05; *p < .1

Highlighting and readability

easytable is opinionated about readability. The coefficient block uses zebra shading by default, and highlight = TRUE can emphasize statistically significant results in a way that stays consistent across outputs.

easytable(
  m1, m2, m3,
  model.names = c("Baseline", "With species", "Full model"),
  highlight = TRUE
)

term

Baseline

With species

Full model

(Intercept)

-5780.83 ***
(305.81)

-4031.48 ***
(584.15)

-4047.52 ***
(585.52)

flipper_length_mm

49.69 ***
(1.52)

40.71 ***
(3.07)

41.09 ***
(3.09)

species:Chinstrap

-206.51 ***
(57.73)

-205.59 ***
(70.35)

species:Gentoo

266.81 ***
(95.26)

199.81 *
(109.71)

island:Dream

-59.75
(75.74)

island:Torgersen

-101.92
(77.65)

N

342

342

342

R sq.

0.76

0.78

0.78

Adj. R sq.

0.76

0.78

0.78

Significance: ***p < .01; **p < .05; *p < .1

Control variables and fixed effects

When models include many levels from a factor, tables can become hard to scan. control.var is intended to help by grouping or compactly representing a named set of controls.

easytable(
  m1, m2, m3,
  model.names = c("Baseline", "With species", "Full model"),
  control.var = "island",
  highlight = TRUE
)

term

Baseline

With species

Full model

(Intercept)

-5780.83 ***
(305.81)

-4031.48 ***
(584.15)

-4047.52 ***
(585.52)

flipper_length_mm

49.69 ***
(1.52)

40.71 ***
(3.07)

41.09 ***
(3.09)

species:Chinstrap

-206.51 ***
(57.73)

-205.59 ***
(70.35)

species:Gentoo

266.81 ***
(95.26)

199.81 *
(109.71)

island

Y

N

342

342

342

R sq.

0.76

0.78

0.78

Adj. R sq.

0.76

0.78

0.78

Significance: ***p < .01; **p < .05; *p < .1

Exporting to Word and CSV

If you are working in Word, you can export a .docx directly. You can also export a .csv for quick inspection or for downstream workflows.

easytable(
  m1, m2, m3,
  model.names = c("Baseline", "With species", "Full model"),
  highlight = TRUE,
  export.word = "penguins_table.docx",
  export.csv = "penguins_table.csv"
)

Robust standard errors

easytable can compute robust standard errors when the required packages are installed.

library(lmtest)
library(sandwich)

easytable(
  m1, m2, m3,
  model.names = c("Baseline", "With species", "Full model"),
  robust.se = TRUE
)

term

Baseline

With species

Full model

(Intercept)

-5780.83 ***
(292.56)

-4031.48 ***
(542.24)

-4047.52 ***
(536.56)

flipper_length_mm

49.69 ***
(1.44)

40.71 ***
(2.86)

41.09 ***
(2.84)

species:Chinstrap

-206.51 ***
(52.21)

-205.59 ***
(66.68)

species:Gentoo

266.81 ***
(95.08)

199.81 *
(112.03)

island:Dream

-59.75
(82.45)

island:Torgersen

-101.92
(83.82)

N

342

342

342

R sq.

0.76

0.78

0.78

Adj. R sq.

0.76

0.78

0.78

Significance: ***p < .01; **p < .05; *p < .1

Note: Robust Standard Errors

What to test and how to report issues

If you would like to contribute, opening an issue on GitHub with a minimal example is enough. If you are experimenting with AI-assisted changes, feel free to fork the repo and propose a small, focused pull request.

  • © 2026 - CC BY-NC 4.0 License

Powered by: Logo