$$~$$

My previous workflow was pretty tedious, as after preparing tables in R (data frames), I would export them to Excel, then copy from Excel into Word, and finally format the table in Word. Whenever I would make minor changes, it would take quite a bit of time to repeat these steps.

Fortunately, I found a package that suits my needs nicely, flextable. However, I only really need my tables in APA style (Times New Roman size 12, only some horizontal lines, header row in bold, double-spaced, right number of decimals, etc.), so I made a function just with the default settings I like to simplify my life.

### Getting Started

Let’s first load the demo data. This data set comes with base R (meaning you have it too and can directly type this command into your R console).

data("mtcars")
# Let's take a smaller sample of this data for demonstration purposes.
(dataset <- head(mtcars[1:6]))
##                    mpg cyl disp  hp drat    wt
## Mazda RX4         21.0   6  160 110 3.90 2.620
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875
## Datsun 710        22.8   4  108  93 3.85 2.320
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215
## Hornet Sportabout 18.7   8  360 175 3.15 3.440
## Valiant           18.1   6  225 105 2.76 3.460

Source the function from my github:

eval(parse("https://raw.githubusercontent.com/RemPsyc/niceplots/master/niceTableFunction.R",
encoding = 'UTF-8'))

Note: Here we cannot use the regular source() function because it involves special UTF-8 characters (i.e., greek letters for statistical symbols, such as Beta β, eta η, etc.)

$$~$$

### Make the basic table

This will open the table in your RStudio viewer.

*Warning:* running the function below for the first time will install and load the following
Note: This will run many lines of code on your console and could take a few minutes.
niceTable(dataset)
 mpg cyl disp hp drat wt 21.00 6.00 160.00 110.00 3.90 2.62 21.00 6.00 160.00 110.00 3.90 2.88 22.80 4.00 108.00 93.00 3.85 2.32 21.40 6.00 258.00 110.00 3.08 3.21 18.70 8.00 360.00 175.00 3.15 3.44 18.10 6.00 225.00 105.00 2.76 3.46

$$~$$

### Save as object

We can save the flextable as an object, which we can later further edit, or export to another software (e.g., Microsoft Word).

my_table <- niceTable(dataset)

### Save table to Word

save_as_docx(my_table, path = "D:/R treasures/nicetablehere.docx")
# Change the path to where you would like to save it.
# If you copy-paste your path name, remember to use "R" slashes ('/' rather than '\').
# Also remember to specify the .docx extension of the file.

That’s it! Simple eh?

$$~$$

$$~$$

Let’s setup a more ‘credible’ table with actual statistics for demonstration.

We first need a bit of complicated code to create a regression model and extract some relevant statistical information.

# Standardize variables to get standardized coefficients
mtcars.std <- lapply(mtcars, scale)
# Create a simple linear model
model <- lm(mpg ~ cyl + wt * hp, mtcars.std)
# Gather summary statistics
stats.table <- as.data.frame(summary(model)\$coefficients)
# Add confidence interval of the regression coefficient
CI <- confint(model)
# Add a row for variables names and CI
stats.table <- cbind(row.names(stats.table), stats.table, CI)
# Rename the columns appropriately
names(stats.table) <- c("Term", "B", "SE", "t", "p", "CI_lower", "CI_upper")

The dataframe looks like this (notice the large number of decimals):

stats.table
##                    Term          B         SE          t            p
## (Intercept) (Intercept) -0.1835269 0.08532112 -2.1510135 4.058431e-02
## cyl                 cyl -0.1082286 0.15071576 -0.7180977 4.788652e-01
## wt                   wt -0.6230206 0.10927573 -5.7013627 4.663587e-06
## hp                   hp -0.2874898 0.11955935 -2.4045781 2.331865e-02
## wt:hp             wt:hp  0.2875867 0.08895462  3.2329593 3.221753e-03
##               CI_lower     CI_upper
## (Intercept) -0.3585914 -0.008462403
## cyl         -0.4174718  0.201014550
## wt          -0.8472358 -0.398805290
## hp          -0.5328053 -0.042174267
## wt:hp        0.1050669  0.470106453

Now we can apply our function!

niceTable(stats.table)
 Term β SE t p 95% CI (Intercept) -0.18 0.09 -2.15 .041 [-0.36, -0.01] cyl -0.11 0.15 -0.72 .479 [-0.42, 0.2] wt -0.62 0.11 -5.70 < .001 [-0.85, -0.4] hp -0.29 0.12 -2.40 .023 [-0.53, -0.04] wt:hp 0.29 0.09 3.23 .003 [0.11, 0.47]

You can even add an argument to highlight significant results for better visual discrimination, if you wish so.

niceTable(stats.table, highlight = TRUE)
 Term β SE t p 95% CI (Intercept) -0.18 0.09 -2.15 .041 [-0.36, -0.01] cyl -0.11 0.15 -0.72 .479 [-0.42, 0.2] wt -0.62 0.11 -5.70 < .001 [-0.85, -0.4] hp -0.29 0.12 -2.40 .023 [-0.53, -0.04] wt:hp 0.29 0.09 3.23 .003 [0.11, 0.47]

Notice first that if you provide the CI_lower and CI_upper column names, it will automatically and properly format your confidence interval column and remove the lower and upper bound columns.

You can also see that it automatically formatted the p, t, and SE values to italic, and the beta to β. It also correctly rounded each row, and formatted p values as < .001 and stripped the leading zeros (it will do the same for correlations r, R2, sr2).

Note: in order for this to work automatically, your columns must be named correctly. Currently the function will make the following conversions: p, t, SE, SD, M, F, b, r, and df to italic; R2 and sr2 to italic squared, dR to italic R subscript, np2 to italic η subscript-p squared, and B to β. Not seeing a symbol that should be there? Contact me and I’ll add it!

Let’s test this by simply changing our dataframe names for the exercise.

test <- head(mtcars)
names(test) <- c("dR", "df", "M", "SD", "b", "np2", "B", "p", "r", "R2", "sr2")
test[, 10:11] <- test[, 10:11]/10
niceTable(test)
 dR df M SD b ηp2 β p r R2 sr2 21.00 6 160.00 110.00 3.90 2.62 16.46 < .001 1.0 .40 .40 21.00 6 160.00 110.00 3.90 2.88 17.02 < .001 1.0 .40 .40 22.80 4 108.00 93.00 3.85 2.32 18.61 1.00 1.0 .40 .10 21.40 6 258.00 110.00 3.08 3.21 19.44 1.00 .00 .30 .10 18.70 8 360.00 175.00 3.15 3.44 17.02 < .001 .00 .30 .20 18.10 6 225.00 105.00 2.76 3.46 20.22 1.00 .00 .30 .10

$$~$$

$$~$$

### Further editing

Often, one will need to tweak a table for a particular situation. Have no fear. This function outputs a flextable object, which can be ‘easily’ edited via the regular flextable functions. For an intro to flextable functions, see: https://cran.r-project.org/web/packages/flextable/vignettes/overview.html.

But just for a few quick formatting examples:

my_table %>%
italic(j = 1, part = "body") %>%
bg(bg = "gray", part = "header") %>%
color(color = "blue", part = "header") %>%
color(~ drat > 3.5, ~ drat + disp, color = "red") %>%
bold(~ drat > 3.5, ~ wt + hp, bold = TRUE) %>%
cyl = "Number of cylinders",
wt = "Weight (1000 lbs)")
 Miles/(US) gallon Number of cylinders disp hp drat Weight (1000 lbs) 21.00 6.00 160.00 110.00 3.90 2.62 21.00 6.00 160.00 110.00 3.90 2.88 22.80 4.00 108.00 93.00 3.85 2.32 21.40 6.00 258.00 110.00 3.08 3.21 18.70 8.00 360.00 175.00 3.15 3.44 18.10 6.00 225.00 105.00 2.76 3.46

$$~$$

$$~$$

### Concluding Statement

Make sure to check out this page again if you use the code after a time or if you encounter errors, as I periodically update or improve the code.

You can always edit the function to suit your purposes, or contact me for questions or requests to modify this function at https://remi-theriault.com/contact! Thanks for reading my guide! :) $$~$$

$$~$$

$$~$$

$$~$$

$$~$$

Updated 2021-02-21 (added automatic 95% CI column)

$$~$$

$$~$$

$$~$$

$$~$$

$$~$$

$$~$$