Getting Started with apa7

Tables

The apa7 package provides functions to create APA-style tables, including correlation matrices and regression tables. The functions return flextable objects that can be processed further using the flextable package. Although there are other fantastic packages for creating tables (e.g., gt, tinytable, and kableExtra), the flextable package has the fullest support for the .docx format, which is essential for anyone working in APA style. Thanks to the tireless efforts of David Gohel, flextable can handle almost anything that can be done in a .docx table.

Load Packages and Set Defaults

 library(apa7)
 library(flextable)
 library(ftExtra)
 library(dplyr)
 library(tibble)
 library(tidyr)
 library(stringr)
 library(psych)
 set_flextable_defaults(theme_fun = theme_apa, 
 font.family = "Times New Roman", 
 text.align = "center", 
 table_align = "left")

Make Data

Suppose we have a table we want to format. As a raw tibble, it looks like this:

d <- tibble(
 Model = paste("Model", c(rep(1,2), rep(2, 3))),
 Predictor = c(
 "Constant", "Socioeconomic status", 
 "Constant", "Socioeconomic status", "Age"),
 b = c(-4.5, 1.23, 
 -5.1, 1.45, -.23),
 beta = c(NA, .24, 
 NA, .31, .031),
 t = c(-18.457, 2.345, 
 -22.457, 2.114, .854),
 df = c(85,85, 
 84, 84, 84),
 p = c(.0001, .0245, 
 .0001, .0341, .544))
 
d
 #> # A tibble: 5 ×ばつ 7
 #> Model Predictor b beta t df p
 #> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
 #> 1 Model 1 Constant -4.5 NA -18.5 85 0.0001
 #> 2 Model 1 Socioeconomic status 1.23 0.24 2.35 85 0.0245
 #> 3 Model 2 Constant -5.1 NA -22.5 84 0.0001
 #> 4 Model 2 Socioeconomic status 1.45 0.31 2.11 84 0.0341
 #> 5 Model 2 Age -0.23 0.031 0.854 84 0.544

Initial Results with flextable

If we use flextable::flextable with the flextable::thema_apa function as the default theme, we get something close to what we want:

 set_flextable_defaults(
 theme_fun = theme_apa, 
 font.family = "Times New Roman", 
 text.align = "center",
 table_align = "left")
 
 flextable(d) 

Model

Predictor

b

beta

t

df

p

Model 1

Constant

-4.50

-18.46

85.00

0.00

Model 1

Socioeconomic status

1.23

0.24

2.35

85.00

0.02

Model 2

Constant

-5.10

-22.46

84.00

0.00

Model 2

Socioeconomic status

1.45

0.31

2.11

84.00

0.03

Model 2

Age

-0.23

0.03

0.85

84.00

0.54

Unfortunately, the journey from "close to what we want" to "exactly what we want" often requires a series of polishing moves that can take a long time and/or specialized knowledge to get right. Some steps along this path might include:

Of course, there are flextable functions that can do most of these things, but applying them repeatedly is tedious. This is not a criticism of flextable. As general-purpose package for table creation, flextable should not be expected to anticipate the complex rules of specific style guides. The fact that it has the theme_apa function is more than generous already.

The apa_flextable tries to take care of all of these APA-Style polishing moves with minimal fuss.

Polished table with apa_flextable

 apa_flextable(d, row_title_column = Model) 

Predictor

b

β

t

df

p

Model1

Constant

−4.50

−18.46

85

<.001

Socioeconomicstatus

1.23

.24

2.35

85

.02

Model2

Constant

−5.10

−22.46

84

<.001

Socioeconomicstatus

1.45

.31

2.11

84

.03

Age

−0.23

.03

0.85

84

.54

The row titles can be aligned to the left, center, or right.

 apa_flextable(d, 
 row_title_column = Model, 
 row_title_align = "center") 

Predictor

b

β

t

df

p

Model1

Constant

−4.50

−18.46

85

<.001

Socioeconomicstatus

1.23

.24

2.35

85

.02

Model2

Constant

−5.10

−22.46

84

<.001

Socioeconomicstatus

1.45

.31

2.11

84

.03

Age

−0.23

.03

0.85

84

.54

Without a row_title_column specified, the Model column can be vertically merged

d |> 
 mutate(Model = str_remove(Model, "Model ")) |> 
 apa_flextable() |> 
 merge_v() |>
 align(j = "Predictor", part = "all") |>
 align(j = "Model", align = "center") |>
 valign(j = "Model", valign = "middle") |>
 surround(i = 2, border.bottom = flextable::fp_border_default()) |>
 width(width = c(.8, 1.75, rep(.8, 5)))

Model

Predictor

b

β

t

df

p

1

Constant

−4.50

−18.46

85

<.001

Socioeconomicstatus

1.23

.24

2.35

.02

2

Constant

−5.10

−22.46

84

<.001

Socioeconomicstatus

1.45

.31

2.11

.03

Age

−0.23

.03

0.85

.54

Conditional Formatting

A common problem with table formatting functions is that they try to do too much in one function, making it difficult to customize the output. Like the entire flextable ecosystem, the apa_flextable function is designed to be flexible in terms of its inputs and allows for further customization afterwards.

The apa_flextable function returns a flextable object that can be further processed with flextable functions, if needed. For example, suppose we wanted to bold the beta coefficient for the first predictor:

 apa_flextable(d, row_title_column = Model) |> 
 bold(i = 3, j = 3)

Predictor

b

β

t

df

p

Model1

Constant

−4.50

−18.46

85

<.001

Socioeconomicstatus

1.23

.24

2.35

85

.02

Model2

Constant

−5.10

−22.46

84

<.001

Socioeconomicstatus

1.45

.31

2.11

84

.03

Age

−0.23

.03

0.85

84

.54

Some flextable functions that take care of common formatting problems. All of these can be applied to specific column names/positions or row positions. The row positions can be selected conditionally based on data in each row.

Function

Purpose

Text

bold

Boldtext

color

Colortext

font

Fontfamily

fontsize

Fontsize

highlight

Highlightcolor

italic

Italicizetext

Cell

align

Horizontalalignment

bg

Backgroundcolor

line_spacing

Linespacing

padding

Cellpadding

rotate

Rotatetext

surround

Cellborders

valign

Verticalalignment

width

Columnwidth

Automatic formatting

The apa_flextable function formats the headers and columns of any headings it recognizes. This feature can be turned off:

 apa_flextable(d, 
 row_title_column = Model, 
 auto_format_columns = FALSE)

Predictor

b

beta

t

df

p

Model1

Constant

-4.50

-18.46

85

0.00

Socioeconomicstatus

1.23

0.24

2.35

85

0.02

Model2

Constant

-5.10

-22.46

84

0.00

Socioeconomicstatus

1.45

0.31

2.11

84

0.03

Age

-0.23

0.03

0.85

84

0.54

It is also possible to modify the automatic formatting. For example, suppose we want any column called "Predictor" to be renamed to "Variable" and to make all variables to be upper case (i.e., capital letters).

The column_format function creates an object for a single column.

cf_predictor <- column_format(
 name = "Predictor", 
 header = "Variable",
 latex = "Variable",
 formatter = stringr::str_to_upper)
 
cf_predictor
 #> 
 #> ── column_format ──
 #> 
 #> # A tibble: 1 ×ばつ 4
 #> name header latex formatter
 #> <chr> <chr> <chr> <list> 
 #> 1 Predictor Variable Variable <fn>

The column_formats function creates a default list of column_format objects.

We can also set the rounding accuracy of all columns to .001 instead of the default of .01.

 # Make new formatter object with default accuracy of .001
my_formats <- column_formats(accuracy = .001)
 
 # Add Predictor column formatter
my_formats$Predictor <- cf_predictor
 
 # Remove formatter for beta column
my_formats$beta <- NULL
 
 apa_flextable(d, 
 row_title_column = Model, 
 column_formats = my_formats)

Variable

b

beta

t

df

p

Model1

CONSTANT

−4.500

−18.457

85

<.001

SOCIOECONOMICSTATUS

1.230

0.24

2.345

85

.024

Model2

CONSTANT

−5.100

−22.457

84

<.001

SOCIOECONOMICSTATUS

1.450

0.31

2.114

84

.034

AGE

−0.230

0.03

0.854

84

.544

The my_formats object is a list of column_format objects. Each column_format object can specify the name, header, formatter, and other options for a column. The column_formats function creates a list of column_format objects with default settings that can be modified as needed.

my_formats@get_tibble |> 
 select(-formatter) |>
 dplyr::arrange(name, .locale = "en") |> 
 apa_flextable(markdown_body = F)

name

header

latex

AIC

*AIC*

$AIC$

AIC_wt

*AIC* weight

$AIC$ weight

AICc

*AICc*

$AICc$

AICc_wt

*AICc* weight

$AICc$ weight

alpha

&alpha;

$\alpha$

b

*b*

$b$

B

*B*

$B$

BIC

*BIC*

$BIC$

BIC_wt

*BIC* weight

$BIC$ weight

BICc

*BICc*

$BICc$

CFI

CFI

CFI

Chi2

*&chi;*^2^

$\chi^2$

Chi2 value

*&chi;*^2^

$\chi^2$

Chi2_df

*df*

$df$

chisq

*&chi;*^2^

$\chi^2$

Chisq

*&chi;*^2^

$\chi^2$

CI

{round(ci * 100)}% CI

{round(ci * 100)}\%

CI_high

UL

UL

CI_low

LL

LL

CI_percent

{p}% CI

{p}\% CI

Coefficient

*B*

$B$

cohens_d

Cohen's *d*

Cohen's $d$

Cohens_d

Cohen's *d*

Cohen's $d$

Cramers_v

Cramér's *V*

Cramér's $V$

cronbach

Cronbach's &alpha;

Cronbach's $\alpha$

deltaAIC

&Delta;*AIC*

$\Delta AIC$

deltaBIC

&Delta;*BIC*

$\Delta BIC$

deltachi2

&Delta;&chi;^2^

$\Delta \chi^2$

deltaR2

&Delta;*R*^2^

$\Delta R^2$

df

*df*

$df$

df_diff

&Delta;*df*

$\Delta df$

df_error

*df*

$df$

eta2

*&eta;*^2^

$\eta^2$

Eta2

*&eta;*^2^

$\eta^2$

Eta2_partial

*&eta;*^2^

$\eta^2$

F

*F*

$F$

m

*m*

$m$

M

*M*

$M$

Mean

*Mean*

$Mean$

n

*n*

$n$

N

*N*

$N$

NFI

NFI

NFI

omega

&omega;

$\omega$

p

*p*

$p$

p_Chi2

*p*

$p$

Parameter

Variable

Variable

phi

&phi;

$\phi$

Phi

&Phi;

$\Phi$

Predictor

Variable

Variable

r

*r*

$r$

R2

*R*^2^

$R^2$

R2_adjusted

adj*R*^2^

$\text{adj}R^2$

RMSE

*RMSE*

$RMSE$

RMSEA

RMSEA

RMSEA

s

*s*

$s$

SD

*SD*

$SD$

SE

*SE*

$SE$

SE_B

*SE_B*

$SE~B$

Sigma

&sigma;~*e*~

$\sigma_{e}$

Std_Coefficient

&beta;

$\beta$

t

*t*

$t$

t_df

*t*({df})

$t$({df})

tau

&tau;

$\tau$

Variable

Variable

Variable

z

*z*

$z$

The apa_flextable function performs a number of formatting operations on the data before and after the data are sent to flextable. Roughly speaking, apa_flextable, by default, performs these operations.

  1. Add space between adjacent column spanners.
  2. Apply as_grouped_data and restructure row titles, if row_title is specified.
  3. Format data with apa_format_columns if auto_format_columns = TRUE
  4. Separate headers into multiple rows if separate_headers = TRUE
  5. Apply flextable
  6. Apply surround to make borders to separate row groups, if any.
  7. Apply apa_style To style table and convert markdown if apa_style = TRUE
  8. Apply pretty_widths if pretty_widths = TRUE

For the intrepid, these steps can be applied sequentially without apa_flextable. Here is what that might look like (column spanners added for illustration, not because the table needs them).

d |> 
 # Create column spanners
 rename_with(.cols = c(b, beta), 
 \(x) paste0("Coefficients_", x)) |> 
 rename_with(.cols = c(t, df, p), 
 .fn = \(x) paste0("Significance Test_", x)) |>
 # Step 1: Space between column spanners
 add_break_columns(ends_with("beta")) |> 
 # Step 2: Make row titles
 flextable::as_grouped_data("Model") |> 
 mutate(row_title = Model, .before = 1) |>
 fill(Model) |>
 # Step 3: Format data
 apa_format_columns() %>% 
 # Step 4: Convert to flextable
 flextable(col_keys = colnames(
 select(., -Model, -row_title))) |> 
 mk_par(i = ~ !is.na(row_title), 
 value = as_paragraph(row_title)) |>
 merge_h(i = ~ !is.na(row_title)) |>
 # Step 5: Separate headers into column spanners and deckered heads
 flextable::separate_header() |> 
 # Step 6: Make borders between row groups
 surround(
 i = ~ !is.na(row_title),
 border.top = list(
 color = "gray20",
 style = "solid",
 width = 1
 )
 ) |> 
 # Step 7: Style table and convert markdown
 apa_style() |> 
 align(j = 1, i = ~is.na(row_title)) |> 
 align(i = ~!is.na(row_title), align = "center") |> 
 # Step 8: Pretty widths
 pretty_widths() 

Predictor

Coefficients

SignificanceTest

b

β

t

df

p

Model1

Constant

−4.50

−18.46

85.00

<.001

Socioeconomicstatus

1.23

.24

2.35

85.00

.02

Model2

Constant

−5.10

−22.46

84.00

<.001

Socioeconomicstatus

1.45

.31

2.11

84.00

.03

Age

−0.23

.03

0.85

84.00

.54

Helper functions

Break columns

Groups of related variables can be separated by adding break columns. Here we take the diamonds data set and calculate the means and standard deviations of several variables, separated by Cut

d_diamonds <- ggplot2::diamonds %>% 
 select(cut, carat, depth, table) %>% 
 arrange(cut) %>% 
 rename_with(str_to_title) %>% 
 pivot_longer(where(is.numeric), names_to = "Variable") %>% 
 summarise(
 M = mean(value, na.rm = TRUE),
 SD = sd(value, na.rm = TRUE),
 .by = c(Variable, Cut)) %>% 
 pivot_longer(c(M, SD)) %>% 
 unite(Variable, Variable, name) %>%
 pivot_wider(names_from = Variable) 
d_diamonds
 #> # A tibble: 5 ×ばつ 7
 #> Cut Carat_M Carat_SD Depth_M Depth_SD Table_M Table_SD
 #> <ord> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 #> 1 Fair 1.05 0.516 64.0 3.64 59.1 3.95
 #> 2 Good 0.849 0.454 62.4 2.17 58.7 2.85
 #> 3 Very Good 0.806 0.459 61.8 1.38 58.0 2.12
 #> 4 Premium 0.892 0.515 61.3 1.16 58.7 1.48
 #> 5 Ideal 0.703 0.433 61.7 0.719 56.0 1.25

The apa_flextable function, by default, assumes that separated headers are desired when column names have underscores. Under the hood, it calls flextable::separate_header and inserts interior borders. This feature can be turned off by setting separate_headers = FALSE.

 apa_flextable(d_diamonds)

Cut

Carat

Depth

Table

M

SD

M

SD

M

SD

Fair

1.05

0.52

64.04

3.64

59.05

3.95

Good

0.85

0.45

62.37

2.17

58.69

2.85

VeryGood

0.81

0.46

61.82

1.38

57.96

2.12

Premium

0.89

0.52

61.26

1.16

58.75

1.48

Ideal

0.70

0.43

61.71

0.72

55.95

1.25

By default, small blank columns are inserted between column spanner groups. If you want to insert them yourself, the add_break_columns function insert break columns before or after any variable. As input, it can take any quoted or unquoted variable name, or any tidyselect function (e.g., starts_with, ends_with, contains,where, `everything).

We can insert breaks after Carat_SD and Depth_SD directly like so:

d_diamonds |> 
 add_break_columns(Carat_SD, Depth_SD)
 #> # A tibble: 5 ×ばつ 9
 #> Cut Carat_M Carat_SD apa7breakcolumn1 Depth_M Depth_SD apa7breakcolumn2
 #> <ord> <dbl> <dbl> <lgl> <dbl> <dbl> <lgl> 
 #> 1 Fair 1.05 0.516 NA 64.0 3.64 NA 
 #> 2 Good 0.849 0.454 NA 62.4 2.17 NA 
 #> 3 Very Good 0.806 0.459 NA 61.8 1.38 NA 
 #> 4 Premium 0.892 0.515 NA 61.3 1.16 NA 
 #> 5 Ideal 0.703 0.433 NA 61.7 0.719 NA 
 #> # i 2 more variables: Table_M <dbl>, Table_SD <dbl>

Alternately, we can add a break column after each variable ending with "SD" except for the last one. The apa_flextable function knows to treat any column beginning with apa7breakcolumn as a break column.

d_diamonds |> 
 add_break_columns(ends_with("SD"), 
 omit_last = TRUE) |> 
 apa_flextable()

Cut

Carat

Depth

Table

M

SD

M

SD

M

SD

Fair

1.05

0.52

64.04

3.64

59.05

3.95

Good

0.85

0.45

62.37

2.17

58.69

2.85

VeryGood

0.81

0.46

61.82

1.38

57.96

2.12

Premium

0.89

0.52

61.26

1.16

58.75

1.48

Ideal

0.70

0.43

61.71

0.72

55.95

1.25

Column Spanners and Deckered Heads

The flextable package has functions like add_header_row and add_header for adding header rows after a flextable has been made. It also has the separate_headers function for creating header rows from the variable names. Both approaches are needed at times, but I like using separate_headers because it is usually easier to manipulate the column names before the table is made than it is afterwards.

By default, separate_headers converts names with underscores into column spanners (header labels that span multiple columns, usually at higher rows in the header) and deckered heads (single-column labels under the column spanners). Each underscore separates labels in separate header rows.

One can make column spanner labels by hand, with custom functions, or with the column_spanner function. It adds the same spanner label to multiple columns, using quoted or unquoted variable names (combined in a vector with c) or tidyselect functions like starts_with, ends_with, or contains. Any selected variables will be relocated after the first selected variable, in the order selected. The relocation can be prevented by setting relocate = FALSE.

d |> 
 column_spanner_label("Significance test", c(t,df,p)) |> 
 column_spanner_label("Coefficients", starts_with("b")) |> 
 apa_flextable(row_title_column = Model)

Predictor

Coefficients

Significancetest

b

β

t

df

p

Model1

Constant

−4.50

−18.46

85

<.001

Socioeconomicstatus

1.23

.24

2.35

85

.02

Model2

Constant

−5.10

−22.46

84

<.001

Socioeconomicstatus

1.45

.31

2.11

84

.03

Age

−0.23

.03

0.85

84

.54

Decimal/Character alignment

The align_chr function does three things:

  1. Rounds to a desired accuracy (default = .01) via scales:number
  2. Replaces minus signs with a true text minus sign via signs::signs
  3. Pads numbers (via apa7::num_pad) with figure spaces (\u2007) on both sides of the decimal (or any other character) so that all numbers in the column have the same width.
 tibble(x = align_chr(c(2.431, -0.4, -10, 101))) |> 
 apa_flextable(table_width = .2) |> 
 align(align = "center")

x

2.43

−0.40

−10.00

101.00

Trailing zeros can be dropped, and leading zeros can be trimmed.

 tibble(x = align_chr(c(2.431, -0.4, -10, 101),
 drop0trailing = TRUE,
 trim_leading_zeros = TRUE)) |>
 apa_flextable(table_width = .2) |>
 align(align = "center")

x

2.43

−.4

−10

101

Hanging indent

The hanging_indent function is a hack, but a necessary one. Sometimes we need paragraphs to be indented in one way or another, and flextable does not have exactly what we need. So hanging_indent splits the text (via stringr::str_wrap) and indents it as specified with figure spaces \u2007.

Note that align_chr aligns the text on the decimal, but pads the left side only so that the right side of the text can be of variable width.

d_quote <- tibble(
 Quote = c(
 "Believe those who are seeking the truth. Doubt those who find it.",
 "Resentment is like drinking poison and waiting for the other person to die.",
 "What you read when you don’t have to, determines what you will be when you can’t help it.",
 "Advice is what we ask for when we already know the answer but wish we didn’t.",
 "Do not ask whether a statement is true until you know what it means.",
 "Tact is the art of making a point without making an enemy.",
 "Short cuts make long delays.",
 "The price one pays for pursuing any profession or calling is an intimate knowledge of its ugly side.",
 "There is a stubbornness about me that never can bear to be frightened at the will of others. My courage always rises at every attempt to intimidate me",
 "There is a crack in everything, that’s how the light gets in.",
 "If you choose to dig a rather deep hole, someday you will have no choice but to keep on digging, even with tears.",
 "We long for self-confidence, till we look at the people who have it.",
 "Writing is a way to end up thinking something you couldn’t have started out thinking.",
 "A little inaccuracy sometimes saves tons of explanation.",
 "Each snowflake in an avalanche pleads not guilty.",
 "What I write is smarter than I am. Because I can rewrite it."
 ),
 Attribution = c(
 "Andre Gide",
 "Carrie Fisher",
 "Charles Francis Potter",
 "Erica Jong",
 "Errett Bishop",
 "Howard W. Newton",
 "J.R.R. Tolkien",
 "James Baldwin",
 "Jane Austin",
 "Leonard Cohen",
 "Liyun Chen",
 "Mignon McLaughlin",
 "Peter Elbow",
 "Saki",
 "Stanislaw J. Lec",
 "Susan Sontag"
 )
) |> 
 arrange(nchar(Quote))
 
d_quote |> 
 mutate(Quote = paste0(seq_along(Quote), 
 ".\u2007",
 Quote) |> 
 align_chr(side = "left") |> 
 hanging_indent(width = 55, indent = 7)) |> 
 apa_flextable() |> 
 align(j = "Attribution", part = "all") |> 
 width(width = c(4.5, 2))

Quote

Attribution

1. Shortcutsmakelongdelays.

J.R.R.Tolkien

2. Eachsnowflakeinanavalanchepleadsnotguilty.

StanislawJ.Lec

3. Alittleinaccuracysometimessavestonsof
explanation.

Saki

4. Tactistheartofmakingapointwithoutmakingan
enemy.

HowardW.Newton

5. WhatIwriteissmarterthanIam.BecauseIcan
rewriteit.

SusanSontag

6. Thereisacrackineverything,that’showthe
lightgetsin.

LeonardCohen

7. Believethosewhoareseekingthetruth.Doubt
thosewhofindit.

AndreGide

8. Donotaskwhetherastatementistrueuntilyou
knowwhatitmeans.

ErrettBishop

9. Welongforself-confidence,tillwelookatthe
peoplewhohaveit.

MignonMcLaughlin

10. Resentmentislikedrinkingpoisonandwaitingfor
theotherpersontodie.

CarrieFisher

11. Adviceiswhatweaskforwhenwealreadyknowthe
answerbutwishwedidn’t.

EricaJong

12. Writingisawaytoendupthinkingsomethingyou
couldn’thavestartedoutthinking.

PeterElbow

13. Whatyoureadwhenyoudon’thaveto,determines
whatyouwillbewhenyoucan’thelpit.

CharlesFrancisPotter

14. Thepriceonepaysforpursuinganyprofessionor
callingisanintimateknowledgeofitsuglyside.

JamesBaldwin

15. Ifyouchoosetodigaratherdeephole,someday
youwillhavenochoicebuttokeepondigging,even
withtears.

LiyunChen

16. Thereisastubbornnessaboutmethatnevercan
beartobefrightenedatthewillofothers.Mycourage
alwaysrisesateveryattempttointimidateme

JaneAustin

Creating a numbered list

d_quote |> 
 
 mutate(linechar = purrr::map_int(Quote, \(x) {
 stringr::str_split(x, "\\\\\n") |> 
 purrr::map(str_trim) |> 
 purrr::map(nchar) |> 
 purrr::map_int(max)
 })) |> 
 arrange(linechar) |> 
 select(-linechar) |> 
 add_list_column(Quote) |> 
 apa_flextable() |> 
 align(j = "Attribution", part = "all") |>
 width(width = c(.3, 4.2, 2))

Quote

Attribution

1.

Shortcutsmakelongdelays.

J.R.R.Tolkien

2.

Eachsnowflakeinanavalanchepleadsnotguilty.

StanislawJ.Lec

3.

Alittleinaccuracysometimessavestonsofexplanation.

Saki

4.

Tactistheartofmakingapointwithoutmakinganenemy.

HowardW.Newton

5.

WhatIwriteissmarterthanIam.BecauseIcanrewriteit.

SusanSontag

6.

Thereisacrackineverything,that’showthelightgetsin.

LeonardCohen

7.

Believethosewhoareseekingthetruth.Doubtthosewhofindit.

AndreGide

8.

Donotaskwhetherastatementistrueuntilyouknowwhatitmeans.

ErrettBishop

9.

Welongforself-confidence,tillwelookatthepeoplewhohaveit.

MignonMcLaughlin

10.

Resentmentislikedrinkingpoisonandwaitingfortheotherpersontodie.

CarrieFisher

11.

Adviceiswhatweaskforwhenwealreadyknowtheanswerbutwishwedidn’t.

EricaJong

12.

Writingisawaytoendupthinkingsomethingyoucouldn’thavestartedoutthinking.

PeterElbow

13.

Whatyoureadwhenyoudon’thaveto,determineswhatyouwillbewhenyoucan’thelpit.

CharlesFrancisPotter

14.

Thepriceonepaysforpursuinganyprofessionorcallingisanintimateknowledgeofitsuglyside.

JamesBaldwin

15.

Ifyouchoosetodigaratherdeephole,somedayyouwillhavenochoicebuttokeepondigging,evenwithtears.

LiyunChen

16.

Thereisastubbornnessaboutmethatnevercanbeartobefrightenedatthewillofothers.Mycouragealwaysrisesateveryattempttointimidateme

JaneAustin

It is also possible to make the list lettered (upper or lowercase) or with Roman numerals (upper or lowercase). Set the type argument to "A", "a", "I", or "i".

d_quote |> 
 add_list_column(Quote, type = "A", sep = ") ") |> 
 apa_flextable() |> 
 align(j = "Attribution", part = "all") |>
 width(width = c(.3, 4.2, 2))

Quote

Attribution

A)

Shortcutsmakelongdelays.

J.R.R.Tolkien

B)

Eachsnowflakeinanavalanchepleadsnotguilty.

StanislawJ.Lec

C)

Alittleinaccuracysometimessavestonsofexplanation.

Saki

D)

Tactistheartofmakingapointwithoutmakinganenemy.

HowardW.Newton

E)

WhatIwriteissmarterthanIam.BecauseIcanrewriteit.

SusanSontag

F)

Thereisacrackineverything,that’showthelightgetsin.

LeonardCohen

G)

Believethosewhoareseekingthetruth.Doubtthosewhofindit.

AndreGide

H)

Donotaskwhetherastatementistrueuntilyouknowwhatitmeans.

ErrettBishop

I)

Welongforself-confidence,tillwelookatthepeoplewhohaveit.

MignonMcLaughlin

J)

Resentmentislikedrinkingpoisonandwaitingfortheotherpersontodie.

CarrieFisher

K)

Adviceiswhatweaskforwhenwealreadyknowtheanswerbutwishwedidn’t.

EricaJong

L)

Writingisawaytoendupthinkingsomethingyoucouldn’thavestartedoutthinking.

PeterElbow

M)

Whatyoureadwhenyoudon’thaveto,determineswhatyouwillbewhenyoucan’thelpit.

CharlesFrancisPotter

N)

Thepriceonepaysforpursuinganyprofessionorcallingisanintimateknowledgeofitsuglyside.

JamesBaldwin

O)

Ifyouchoosetodigaratherdeephole,somedayyouwillhavenochoicebuttokeepondigging,evenwithtears.

LiyunChen

P)

Thereisastubbornnessaboutmethatnevercanbeartobefrightenedatthewillofothers.Mycouragealwaysrisesateveryattempttointimidateme

JaneAustin

Stars

When data is supplied to apa_flextable, any variable that ends with apa7starcolumn will be left aligned, and the variable to its immediate left will be right aligned. Here we convert the p column to stars, placing baba7starcolumn after column b.

d_star <- tibble(
 Predictor = c("Constant", "Socioeconomic status"),
 b = c(.45,.55),
 p = c(.02, .0002)) |> 
 add_star_column(b, p = p) 
 
d_star
 #> # A tibble: 2 ×ばつ 4
 #> Predictor b bapa7starcolumn p
 #> <chr> <dbl> <chr> <dbl>
 #> 1 Constant 0.45 "^\\*^" 0.02 
 #> 2 Socioeconomic status 0.55 "^\\*\\*\\*^" 0.0002
 
 apa_flextable(d_star)

Predictor

b

p

Constant

0.45

*

.02

Socioeconomicstatus

0.55

***

<.001

Suppose that the stars are already appended to some numbers. We can separate them into a new apa7starcolumn using separate_star_column.

d_star <- tibble(Predictor = c("Constant", "Socioeconomic status"),
 b = c("1.10***", "2.32*"),
 beta = c(NA, .34)) |> 
 separate_star_column(b)
 
d_star
 #> # A tibble: 2 ×ばつ 4
 #> Predictor b bapa7starcolumn beta
 #> <chr> <chr> <chr> <dbl>
 #> 1 Constant 1.10 "^\\*\\*\\*^" NA 
 #> 2 Socioeconomic status 2.32 "^\\*^" 0.34
 
 apa_flextable(d_star)

Predictor

b

β

Constant

1.10

***

Socioeconomicstatus

2.32

*

.34

APA format with full control

Sometimes you want a table to be particular way, and no package can anticipate the exact structure and formatting required. With a combination of tidyverse, flextable, and apa7 functions, it is possible to get flextable to output almost any kind of APA table you need.

Here I would like the unstandardized and standardized regression coefficients with the two models side by side. I want the p-values converted to stars and appended to the unstandardized coefficients.

d |> 
 # # decimal align b and append p-value stars
 mutate(b = paste0(
 align_chr(b), 
 p2stars(p))) |> 
 # deselect t, df, and p
 select(-c(t,df, p)) |> 
 # restructure data
 pivot_wider_name_first(names_from = Model, 
 values_from = c(b, beta)) |> 
 # convert to flextable
 apa_flextable() |> 
 # add footnotes
 add_footer_lines(
 values = as_paragraph_md(
 c(paste(
 "*Note*. *b* = unstandardized regression coefficient.",
 "&beta; = standardized regression coefficient."),
 apa_p_star_note()))) |> 
 # align footnote
 align(part = "footer", align = "left") |> 
 # Make column widths even
 width(width = c(2.05, 1.1, 1.1, .05, 1.1, 1.1))

Predictor

Model1

Model2

b

β

b

β

Constant

−4.50***

−5.10***

Socioeconomicstatus

1.23*

.24

1.45*

.31

Age

−0.23

.03

Note.b=unstandardizedregressioncoefficient.β=standardizedregressioncoefficient.

* p < .05.** p < .01.*** p < .001

Specialized tables

Regression

Single model (via parameters::parameters)

fit <- lm(price ~ carat, data = ggplot2::diamonds) 
 
fit |> 
 apa_parameters() |> 
 apa_flextable()

Variable

B

SE

β

t(53,938)

p

Constant

−2,256.36

13.06

.00

−172.83

<.001

Carat

7,756.43

14.07

.92

551.41

<.001

Performance (via performance:performance)

By default, just R2 (Coefficient of variation) and Sigma (standard error of the estimate) are displayed.

 apa_performance(fit) |> 
 apa_flextable()

R2

σe

.85

1,548.56

One can request additional metrics (from AIC, AICc, BIC, R2, R2_adjusted, RMSE, and Sigma):

 apa_performance(fit, metrics = c("R2", "Sigma", "AIC", "BIC")) |>
 apa_flextable()

R2

σe

AIC

BIC

.85

1,548.56

945,466.53

945,493.22

One can request them all:

 apa_performance(fit, metrics = "all") |> 
 apa_flextable() 

AIC

AICc

BIC

R2

adjR2

RMSE

σe

945,466.53

945,466.53

945,493.22

.85

.85

1,548.53

1,548.56

Multiple models in a list

fit_3 <- list(
 lm(price ~ cut, data = ggplot2::diamonds),
 lm(price ~ cut + table, data = ggplot2::diamonds),
 lm(price ~ cut + table + carat, data = ggplot2::diamonds)
)
 
fit_3 |> 
 apa_parameters() |>
 apa_flextable(row_title_column = Model, 
 row_title_align = "center")

Variable

B

SE

β

t

df

p

Model1

Constant

4,062.24

25.40

.00

159.92

53,935

<.001

Cut

−362.73

68.04

−.03

−5.33

53,935

<.001

Cut2

−225.58

60.65

−.03

−3.72

53,935

<.001

Cut3

−699.50

52.78

−.07

−13.25

53,935

<.001

Cut4

−280.36

42.56

−.03

−6.59

53,935

<.001

Model2

Constant

−6,340.26

537.01

.00

−11.81

53,934

<.001

Cut

−14.24

70.15

.00

−0.20

53,934

.84

Cut2

−65.60

61.00

−.01

−1.08

53,934

.28

Cut3

−517.97

53.42

−.06

−9.70

53,934

<.001

Cut4

−130.07

43.11

−.01

−3.02

53,934

.003

Table

179.10

9.24

.10

19.39

53,934

<.001

Model3

Constant

−1,555.61

205.60

.00

−7.57

53,933

<.001

Cut

1,202.79

26.92

.11

44.68

53,933

<.001

Cut2

−546.62

23.35

−.06

−23.41

53,933

<.001

Cut3

348.86

20.49

.04

17.02

53,933

<.001

Cut4

58.30

16.49

.01

3.53

53,933

<.001

Table

−19.84

3.55

−.01

−5.59

53,933

<.001

Carat

7,878.92

14.05

.94

560.94

53,933

<.001

Performance comparison (via performance::compare_performance)

Available metrics: AIC, AIC_wt, AICc, AICc_wt, BIC, BIC_wt, deltaR2, F, p, R2, R2_adjusted, RMSE, and Sigma

fit_3 |> 
 apa_performance_comparison() |> 
 apa_flextable()

Model

R2

ΔR2

F

p

Model1

.01

.01

Model2

.02

.01

2,570.13

<.001

Model3

.86

.84

314,652.29

<.001

Correlation

ggplot2::diamonds |> 
 select(table, carat, length = x, width = y , depth = z) |> 
 apa_cor() 

Variable

M

SD

1

2

3

4

5

1.

table

57.46

2.23

2.

carat

0.80

0.47

.18

***

3.

length

5.73

1.12

.20

***

.98

***

4.

width

5.73

1.14

.18

***

.95

***

.97

***

5.

depth

3.54

0.71

.15

***

.95

***

.97

***

.95

***

*** p < .001

Cross-tabulation with Chi-square Test of Independence

ggplot2::diamonds |> 
 select(Cut = cut, Color = color ) |> 
 apa_chisq()

Color

Fair

Good

VeryGood

Premium

Ideal

n

%

n

%

n

%

n

%

n

%

D

163

10.1%

662

13.5%

1,513

12.5%

1,603

11.6%

2,834

13.2%

E

224

13.9%

933

19.0%

2,400

19.9%

2,337

16.9%

3,903

18.1%

F

312

19.4%

909

18.5%

2,164

17.9%

2,331

16.9%

3,826

17.8%

G

314

19.5%

871

17.8%

2,299

19.0%

2,924

21.2%

4,884

22.7%

H

303

18.8%

702

14.3%

1,824

15.1%

2,360

17.1%

3,115

14.5%

I

175

10.9%

522

10.6%

1,204

10.0%

1,428

10.4%

2,093

9.7%

J

119

7.4%

307

6.3%

678

5.6%

808

5.9%

896

4.2%

Note.χ2 (24)=310.32,p < .001,Adj.Cramer’sV=.04

It is not a bad table for so little effort, but the pattern is not easily visible. A plot reveals the direction of the effect such that stones with better cuts tend to have less color.

 library(ggplot2)
 #> 
 #> Attaching package: 'ggplot2'
 #> The following objects are masked from 'package:psych':
 #> 
 #> %+%, alpha
ggplot2::diamonds |>
 select(Cut = cut, Color = color) |>
 count(Cut, Color) |>
 mutate(p = scales::percent(n / sum(n), accuracy = .1), .by = Cut) |>
 ggplot(aes(Cut, n, fill = Color)) +
 geom_col(position = position_fill(),
 alpha = .6,
 width = .96) +
 geom_text(
 aes(label = paste0(p, " (", n, ")")),
 position = position_fill(vjust = .5),
 size.unit = "pt",
 size = 14 * .8,
 color = "gray10"
 ) +
 theme_minimal(base_family = "Roboto Condensed", base_size = 14) +
 scale_y_continuous(
 "Cumulative Proportion",
 expand = expansion(c(0, .025)),
 labels = \(x) scales::percent(x, accuracy = 1)
 ) +
 scale_x_discrete(expand = expansion()) +
 theme(panel.grid.major.x = element_blank())

Factor Analysis

 # Get variable names
rename_items <- psych::bfi.dictionary |>
 tibble::rownames_to_column("variable") |> 
 mutate(Item = str_remove(Item, "\\.$")) |> 
 select(Item, variable) |> 
 deframe()
 
 # Make data
d <- psych::bfi |> 
 select(-gender:-age) |> 
 rename(any_of(rename_items))
 
 # Analysis
fit <- fa(d, nfactors = 5, fm = "pa", )
 #> Loading required namespace: GPArotation
 
 
 # Make table
fit |>
 apa_loadings() |> 
 rename(Extraversion = PA1,
 Neuroticism = PA2,
 Conscientiousness = PA3,
 Openness = PA4,
 Agreeableness = PA5) |> 
 apa_flextable(no_format_columns = Variable) 

Variable

Neuroticism

Extraversion

Conscientiousness

Agreeableness

Openness

Getangryeasily

.81

Getirritatedeasily

.78

Havefrequentmoodswings

.71

Paniceasily

.49

−.20

.21

Oftenfeelblue

.47

−.39

Finditdifficulttoapproachothers

−.68

Makefriendseasily

.59

.29

Don’ttalkalot

−.56

Takecharge

.42

.27

.21

Knowhowtocaptivatepeople

.42

.25

.28

Continueuntileverythingisperfect

.67

Dothingsinahalf-waymanner

−.61

Dothingsaccordingtoaplan

.57

Wastemytime

−.55

Amexactinginmywork

.55

Knowhowtocomfortothers

.66

Inquireaboutothers’well-being

.64

Makepeoplefeelatease

.23

.53

Lovechildren

.43

Amindifferenttothefeelingsofothers

.21

−.41

Carrytheconversationtoahigherlevel

.61

Willnotprobedeeplyintoasubject

−.54

Amfullofideas

.51

Avoiddifficultreadingmaterial

−.46

Spendtimereflectingonthings

−.32

.37

Limitations

By default, apa_flextable calls ftExtra:col_format_md on the entire table. This makes formatting easy and consistent, but the process is a little slow. It is not so bad with only a few tables, but a document with many tables can take a while to render. If possible, setting markdown = FALSE will speed things up, if needed. It is possible to prevent markdown formatting selectively with markdown_body = FALSE or markdown_header. I usually just live with it or cache code chunks with finished tables so that I do not have to wait every time I render the document.

AltStyle によって変換されたページ (->オリジナル) /