Frequently used R tips and tricks (updated regularly)

When using R for data wrangling, visualization, and analysis, I sometimes forget code I wrote previously and end up on the exact same web pages every time I search for it. Here, I will regularly post handy tips that I have had to look up more than a few times, wondering, "Wait, how did that go again?"

Data wrangling

Read and combine all .csv files in a folder at once

csv_list <- fs::dir_ls(path = "../data/", 
                    glob = "*.csv", # .csvを検索
                    recurse = TRUE # サブディレクトリも検索したい場合はTRUE
                    )

dat <- map(csv_list, ~ readr::read_csv(.x)) |> bind_rows()Code language: PHP (php)

As explained in the readr 2.0.0 documentation https://t.co/4KEXiHSNsZ, you can use fs::dir_ls() instead of list.files(). In this case, you do not need to attach the path using str_c().
csv_list <- fs::dir_ls(path = "../data/",
glob = "*.csv",
recurse = TRUE
) https://t.co/jdn8fFAR29

— Shoei (@shoei05) July 22, 2021

Convert kanji numerals to Arabic numerals

pacman::p_load(zipangu)
dat$x <- dat$x %>% kansuji2arabic()Code language: PHP (php)

Convert full-width characters to half-width

pacman::p_load(stringi)
dat$x <- dat$x %>% stri_trans_nfkc()Code language: PHP (php)

Convert kanji numerals to Arabic numerals → zipangu::kansuji2arabic() https://t.co/Dv0IMMqVdh Convert full-width alphanumeric characters to half-width → stringi::stri_trans_nfkc() https://t.co/rn0kVzPBN5

— Shoei (@shoei05) September 26, 2020

Pad numbers to a specified number of digits (9 → 09)

dat %>% mutate(x_pad = str_pad(x, 2, pad=0))

str_pad() from {stringr} seems the easiest to remember. Example: str_pad(9, 2, pad=0) → "09" "Padding 9 as '09' in R" https://t.co/WoFSLh8564

— Shoei (@shoei05) November 17, 2020

Replace NAs with 0 in numeric columns

dat <- dat %>% 
  mutate_if(is.numeric, ~ replace_na(.x, 0))Code language: CSS (css)
dat <- dat %>%
  mutate(across(where(is.numeric), ~ replace_na(.x, 0))) Code language: CSS (css)

When using mutate(across()), note that the function must be placed inside across().

If you write mutate(across(where(is.numeric)), ~ replace_na(.x, 0)),

Error: Problem with `mutate()` input `..2`. x Input `..2` must be a vector, not a `formula` object. ℹ Input `..2` is `~replace_na(.x, 0)`.Code language: JavaScript (javascript)

you will get this error.

mutate_if(is.numeric, ~ replace_na(.x, 0)) and mutate(across(where(is.numeric), ~ replace_na(.x, 0))) perform the same column operation. When using across(), remember to put the function ( ~ … ) inside across(). https://t.co/CbrTiS5ijR

— Shoei (@shoei05) January 3, 2021

Select rows without NA

dat %>% filter(!(is.na(x)))Code language: CSS (css)

Replace 0 with NA using dplyr::if_else

dat %>% mutate(x = if_else(x == 0, NA_real_, x))

Note that dplyr::if_else requires matching data types. If you just use NA,

`false` must be a logical vector, not a double vector.Code language: JavaScript (javascript)

it returns an error like this. Use the double-type NA_real_ instead.

Concatenate strings (str_c()) in data containing columns with NAs

To use str_c() including a column that contains NAs, wrap the target column (column Y in this example) in str_replace_na(). Because NA is generated as the string "NA", you then perform a string replacement to remove the ":NA" generated alongside the ":" specified in sep.

dat <- dat %>%
  mutate( Z = str_c(X,str_replace_na(Y),sep = ":")) %>% 
  mutate(across(Z, ~ gsub(":NA","",.x))) Code language: JavaScript (javascript)

To use str_c() including a column with NAs, wrap the target column (column Y in the example) in str_replace_na(). After that, just replace the generated ":NA" string. Example: dat <- dat %>% mutate( Z = str_c(X,str_replace_na(Y),sep = ":")) %>% mutate(across(Z, ~ gsub(":NA","",.x))) https://t.co/fmvi6j15mA pic.twitter.com/Xzt9037mfx

— Shoei (@shoei05) January 31, 2021

Batch convert data types

factor_list <- c("x","y","z")
dat <- dat %>% mutate_at(vars(all_of(factor_list)),as.factor)Code language: JavaScript (javascript)

Using across(), it looks like this:

factor_list <- c("x","y","z")
dat <- dat %>% mutate(across(all_of(factor_list),as.factor))Code language: JavaScript (javascript)

Or explicitly define the function using ~ and .x:

dat <- dat %>% mutate(across(all_of(factor_list), ~ as.factor(.x)))Code language: CSS (css)

Batch rename columns

name_vector <- c("apple(変更前)"="1(変更後)", 
                 "banana"="2",
                 "orange"="3")

dat %>% 
  mutate(across(label, ~ str_replace_all(.x, name_vector)))Code language: JavaScript (javascript)

Rename factor levels

pacman::p_load(forcats)
fct_list <- c("全くそう思う(変更後)" = "1(変更前)","少しそう思う" = "2",
              "あまりそう思わない" = "3", "全くそう思わない" = "4")
dat <- dat %>% mutate(x = fct_recode(x, !!!fct_list))Code language: PHP (php)

You can also rename factor levels with the method below, but note that it does not change their order. Explicitly mapping them to numbers as shown above is less prone to mistakes.

levels(dat$x) <- c("全くそう思う","少しそう思う","あまりそう思わない","そう思わない")Code language: PHP (php)

"{forcats} Use '!!!' when passing a vector to rename factor levels"
x <- factor(c("apple", "bear", "banana", "dear"))
levels <- c(fruit = "apple", fruit = "banana")
fct_recode(x, !!!levels)https://t.co/aiW1P81SY8 pic.twitter.com/4PJqyBjSpr

— Shoei (@shoei05) December 27, 2020

Collapse and regroup factor levels

dat <- dat %>%  mutate(
  x2 = fct_recode(x,
    "fruits" = "apple",
    "fruits" = "banana",
    "fruits" = "orange",
    "vehicle" = "car",
    "vehicle" = "bike"))Code language: JavaScript (javascript)

Change the order of factor levels

fct_levels <- c("a","b","c")
dat$x <- dat$x %>% factor(levels = fct_levels) Code language: PHP (php)

You can also write this in mutate() using across as follows:

dat <- dat %>% mutate(across(x, ~ factor(.x, levels = fct_levels)))Code language: HTML, XML (xml)

Using fct_relevel() from {forcats}, it looks like this. Note, however, that this will issue a warning if you specify a value that does not exist.

pacman::p_load(forcats)
fct_levels <- c("a","b","c")
dat <- dat %>% mutate(across(x, ~ fct_relevel(.x, levels = fct_levels)))Code language: PHP (php)

To reverse the order, fct_rev() from {forcats} is useful.

pacman::p_load(forcats)
dat <- dat %>% mutate(across(x, ~ fct_rev(.x)))Code language: CSS (css)

Batch rename with dplyr::rename using a character vector

To rename multiple columns at once, rename(!!! named_vector) is convenient. Alternatively, you can use dput() (described below).

named_vector <- c("id" = "x",
                  "fruits" = "y")

dat <- dat %>% rename(!!! named_vector)Code language: JavaScript (javascript)

To create a character vector and then batch rename with dplyr::rename, use dat %>% rename(!!! named_vector) or dat %>% rename_mp(named_vector). https://t.co/iys69m6XT8 pic.twitter.com/2AB1AOL2hm

— Shoei (@shoei05) February 16, 2021

You can also create namelist using setNames():
namelist <- dat2$post %>% setNames(dat2$pre)

Alternatively, you can create a pattern vector.
Reference: https://t.co/mHq6e0ZioJ

— Shoei (@shoei05) July 27, 2021

Get column names as a vector

For example, if you want to rename columns in a batch, running

dat %>% names() %>% dput()

gives you

c("ID", "果物", "個数")Code language: JavaScript (javascript)

a character vector like this, which you can copy and edit.

# c("ID", "果物", "個数") コピペした後に書き換えて setNames()を利用して一括で列名を変更

dat  <- dat %>% setNames(c("ID", "fruits", "n"))Code language: PHP (php)

I used to use colnames(x) to check column names, but getting them in c() format with dput(names(x)) is much handier because you can copy and paste the output directly when renaming columns with setNames() later. pic.twitter.com/xUywNUbda2

— Shoei (@shoei05) September 18, 2020

Count occurrences of a specific value (X) in a vector

sum(dat$x == X)Code language: PHP (php)

Specify a string separately and pass it to a function

cat <- "x"
dat %>% filter(!is.na(.data[[cat]]))Code language: CSS (css)

Writing is.na(cat) causes an error. In this case, use .data[[ column_name ]].

You can use the same technique inside ggplot's aes() (another option is to use aes_string()).

Specify columns by index rather than string in dplyr::filter

df %>% dplyr::filter(.[1] == 1)Code language: PHP (php)

However, for reasons explained below, enclosing the index in double brackets like .[[column_index]] is less confusing and preferable.

df %>% dplyr::filter(.[[1]] == 1)Code language: PHP (php)

You can specify columns with .[column_index] without even using filter_ (e.g., df %>% dplyr::filter(.[1] == 1); see the comments): "Specifying columns in filter by column index instead of typing out column names as strings" https://t.co/JvjIMvSNpD

— Shoei (@shoei05) January 30, 2021

Use partial string matching in dplyr::filter (+ specify by column index)

dat %>% filter(str_detect(.[[1]], "文字"))Code language: JavaScript (javascript)

Or

dat %>% filter(grep("文字", .[[1]]))Code language: JavaScript (javascript)

.[column_index] does not work here, whereas .[[column_index]] does. For that reason, using [[ from the start is less error-prone.

When you want to use partial matching (grepl, str_detect) in filter, .[column_index] fails, but .[[column_index]] works fine. Since .[[column_index]] also works in the previous tweet's case, it is less error-prone to just use [[ from the start. Example: dat %>% filter(str_detect(.[[1]], "郡$")) https://t.co/UkwZg0zkbM

— Shoei (@shoei05) January 31, 2021

Separate a column containing combined numbers and text

For example, with data like "0100札幌市保健所" (0100 Sapporo City Public Health Center):

dat <- dat %>% 
  mutate(code = str_extract(code_area,"^(\\d)+")) %>%
  mutate(area = str_extract(code_area, "(\\D)+$"))Code language: JavaScript (javascript)
  • \\d : digit
  • \\D : non-digit
  • + : sequence of characters
  • ^ : start of string
  • $ : end of string

are the regular expressions used here. (Reference: List of basic regular expressions)

Calculate age from two Date columns

pacman::p_load(lubridate)
dat <- dat %>% mutate_at(vars(d_birth, d_x), as.Date) 
dat <- dat %>% mutate( age = as.duration(d_x - d_birth)) 
dat <- dat %>% mutate( age = as.double(str_extract(age, "(?<=~).+(?=\s)"))) Code language: PHP (php)

(?<=~).+(?=\s) is a regular expression meaning "characters between ~ and a space".

  • ?<=~ : preceded by ~
  • .+ : sequence of any characters
  • ?=\s : followed by a space (\s)

"Used when adding conditions before and after a search term. The basic form is (?=regex); change = to ! for negation, and add < for lookbehind." https://t.co/KikmyRHxOd "If you try to write a regex matching digits and just use \d, R will complain. To represent a literal backslash, you need to double it." https://t.co/LGfdNfHFH0 pic.twitter.com/qhnXyoR3NG

— Shoei (@shoei05) November 27, 2020

Extract data for a specific period from date data

Extracting data from January 1, 2020 to December 31, 2020 from a date column (date)

dat %>% 
  filter(date >= as.Date("2020-01-01") & date <= as.Date("2020-12-31"))Code language: JavaScript (javascript)

Converting values calculated with {lubridate} as.duration() into days

pacman::p_load(lubridate)
dat <- dat %>%
    mutate(day = as.duration(x_2 - x_1 + ddays(1))/dseconds(86400))Code language: PHP (php)

Expanding list data into a data.frame

dat_list <- dat_list %>% reduce(rbind)Code language: HTML, XML (xml)

Removing duplicate rows

dat %>% distinct(x, y, .keep_all = TRUE)Code language: PHP (php)

[Note] "In R, you can easily remove duplicate rows with dplyr::distinct. You can also easily extract non-key columns." https://t.co/gKnQw2hbkm

— Shoei (@shoei05) March 22, 2020

Checking for duplicate rows

Use table() if you also want to know the number of duplicates.

dat %>% 
    pull(col_name) %>% 
    table() %>% 
    data.frame() %>% 
    filter(Freq >= 2) %>% 
    arrange(desc(Freq))

To get unique values from a column, you can use dat %>% pull(x) %>% unique(), but use table() if you also want the duplicate counts. For example, to find values duplicated two or more times: dat %>% pull(adress) %>% table() %>% data.frame() %>% filter(Freq >= 2) https://t.co/5dhZND5M7M

— Shoei (@shoei05) March 15, 2021

Replacing NAs in specific columns

dat <- dat %>%
   replace_na(list(x = 0,
                   y = 0,
                   z = 0))Code language: HTML, XML (xml)

Copying the upper triangle of a matrix to the lower triangle

M <- dat
for(i in 1:nrow(M)) {for(j in 1:i) {M[j,i]=M[i,j]}}Code language: HTML, XML (xml)

For example, when calculating ordinal correlation coefficients (such as polychoric or polyserial correlations) with hetcor() in {polycor}, the function returns values only in the lower triangle. To plot a correlation matrix using a package like {corrplot}, handle it as follows.

pacman::p_load(polycor)
ans <- dat %>% hetcor()
M <- ans$tests
for(i in 1:nrow(M)) {for(j in 1:i) {M[j,i]=M[i,j] }}Code language: HTML, XML (xml)

"Copy the upper triangle of a matrix (M) to the lower triangle": for(i in 1:nrow(M)) {for(j in 1:i) {M[i,j]=M[j,i] }} To do the reverse, swap i and j like M[j,i] = M[i,j] https://t.co/cNuO3HrEPe

— Shoei (@shoei05) January 1, 2021

Summing multiple selected columns in mutate()

Use rowSums or reduce.

dat %>%
  mutate(sum = rowSums(across(c(●, ●))))

Or

dat %>%
  mutate(sum = reduce(across(c(●, ●)), `+`))Code language: JavaScript (javascript)

Note to self. Two main methods to remember:
1. rowSums
dat %>%
mutate(sum = rowSums(across(c(●, ●))))

2. reduce (or base::Reduce)
dat %>%
mutate(sum = reduce(across(c(●, ●)), `+`))

There are many other ways. See the thread (thank you again, everyone). https://t.co/NeaFfUFZmZ

— Shoei (@shoei05) March 21, 2022

Running batch calculations across columns while keeping data in wide format

dat |> group_by(group) |> summarise(across(where(is.double), ~ first(.x) – last(.x)))

Getting data from subdirectories

To get file paths from multiple subdirectories as well, set recursive=TRUE inside list.files().

read_xlsx_list <- list.files(
  "../materials/folder",          # 検索するフォルダパスを指定
  "*.xlsx",                       # .xlsx ファイルを抽出
  recursive=TRUE) %>%             # サブフォルダ内も検索できるようになる
  str_c("../materials/folder/",.) # パスを結合しておけば後でread_excel()しやすいCode language: PHP (php)

To get file paths from multiple subdirectories as well, set recursive=TRUE inside list.files(). Example: list.files("test/folder", "*.csv", recursive=T) retrieves all .csv files across multiple subdirectories inside the test directory. Reference: https://t.co/JyZzuTItz2

— Shoei (@shoei05) July 1, 2021

Visualization

Using theme_set() in {ggplot2}

Define this at the beginning of a file to apply it to all subsequent ggplot plots.

theme_set(
  theme_classic() + 
    theme(
      strip.background = element_blank(),
      text = element_text(size = 10),
      axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1),
      axis.ticks.length.x = unit(0.1, "cm"),
      legend.position = 'none')
)Code language: JavaScript (javascript)

Lately, I fine-tune my ggplot themes like this using theme_set() (defining it at the start of the file applies it to all subsequent ggplots). pic.twitter.com/PFMSra2hau

— Shoei (@shoei05) September 26, 2021

Lately, I set the following, including theme_set(), as the default in my .md files.

---
title: "yyyymmdd_xxxxx"
output: html_document
date: "2024-xx-xx"
author: "xxxxxx"
output: 
  html_document:
    theme: cerulean
    toc: yes
    toc_depth: 3
    toc_float: yes
    df_print: "kable"
  powerpoint_presentation:
    reference_doc: template.pptx
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
knitr::opts_chunk$set(eval = TRUE)
knitr::opts_chunk$set(tidy = FALSE)
knitr::opts_chunk$set(warning = FALSE)
knitr::opts_chunk$set(error = FALSE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(dpi = 300,  fig.width = 10, fig.height = 10*0.618)
```

```{r}
theme_set(
  theme_classic() + 
    theme(
      strip.background = element_blank(),
      text = element_text(size = 10, family = "HiraginoSans-W3"),
      axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1),
      axis.ticks.length.x = unit(0.1, "cm"),
      legend.position = 'none')
)
```

```{r}
pacman::p_load(tidyverse)
```Code language: PHP (php)

Passing column names as strings to aes() in {ggplot2}

dat %>% ggplot(aes_string(x=x, y=y))

Use aes_string() instead of aes(). Alternatively, use the method below.

dat %>% ggplot(aes(x=.data[["x"]], y=.data[["y"]]))Code language: JavaScript (javascript)

Remembering .data[[“x”]] can be useful when you have trouble passing arguments while writing functions (e.g., with is.na(), arrange(), pull()).

Or aes(x=.data[["x"]], y=.data[["y"]]) works too → "Passing column names as strings causes an error. Where you would normally write aes(x=x, y=y), wrapping it in ggplot2::aes_string handles the column names passed as arguments properly." https://t.co/lRR7MD92hU pic.twitter.com/VSYfx6mVz4

— Shoei (@shoei05) January 5, 2021

Slightly tilting x-axis labels in {ggplot2}

dat %>% ggplot(aes(x)) +
    geom_count() +
    theme(axis.text.x = element_text(angle=30, hjust=1))

Adjusting legend position in {ggplot2}: Bottom and right-aligned

g <- ggplot() + ...

g + theme(legend.position = "bottom",
          legend.justification = c("right"))Code language: HTML, XML (xml)

"You can place the (ggplot2) legend anywhere. Use the legend.position option to specify top, right, bottom, or left. (To place it at the bottom and align it to the right, use theme(legend.position = "bottom", legend.justification = c("right")))" https://t.co/IK9tus45Ml pic.twitter.com/lzLaH6QPsl

— Shoei (@shoei05) February 11, 2021

Controlling the width of geom_jitter() by density distribution with {ggforce} geom_sina

Violin plots are easy to read, but geom_sina() plots individual dots in the shape of a violin plot, making the figure visually intuitive.

pacman::p_load(ggforce)

dat %>% ggplot(aes(X,Y)) +
  geom_sina(aes(coular = Z))Code language: PHP (php)

Personally, I prefer ggforce::geom_sina(), which bulges like a violin plot, over geom_jitter(). "The sina plot is an extension of the jitter plot where the width is controlled by the density distribution of the data within each class." https://t.co/4PghYE0ZHV "Overlaying boxplots and dots using geom_sina()" https://t.co/wJU6YyXRg1 pic.twitter.com/X3piiPdAZY

— Shoei (@shoei05) January 26, 2021

Adjusting label positions on a 100% stacked bar chart

ddat %>% 
  group_by(x,y) %>% 
  summarize(count=n()) %>% 
  arrange(x,desc(y)) %>% 
  ddply(x,transform,label_y=cumsum(count)) %>% 
  ggplot(aes(x = x, y = count, fill = y)) +
  geom_col()+
  geom_text ( aes ( y = label_y ,
                    label = count ), colour = "black", vjust = "inward") +
  scale_fill_brewer(palette = "Blues",
                    name="Y",
                    breaks=c("a", "b", "c"),
                    labels=c("A", "B", "C") ) +
  scale_x_continuous(breaks=c(1,2,3)) +
  xlab("X")+
  ylab("Count")+
  theme_minimal() Code language: JavaScript (javascript)

For a 100% stacked bar chart, in addition to the method described in this article, adding vjust = "inward" inside geom_text() keeps the numbers from spilling outside the plot area. https://t.co/LR4eUYLQWO

— Shoei (@shoei05) September 29, 2020

Choosing color schemes with ColorBrewer

Handy when deciding on colors for figures and graphs. [Note] "ColorBrewer: Color Advice for Maps" https://t.co/gWM9gHkQf4 pic.twitter.com/OHk0S3lVlc

— Shoei (@shoei05) September 16, 2019

Choosing color schemes with "Color Palette Cinema"

Found a useful search term: "Color Palette Cinema". I'll try using it when I'm stuck on color schemes (though there might be too many choices). Personally, I also add "Wes" to the search terms (for Wes Anderson). https://t.co/5lrGJDSA8r pic.twitter.com/kNRgrfZPuL

— Shoei (@shoei05) December 6, 2020

Colorblind-safe palettes (safe color)

palette("Okabe-Ito")Code language: JavaScript (javascript)

#RJpUsers Apparently, "In R 4.0.0 and later, running palette("Okabe-Ito") sets the standard color numbers to safe colors." References: 1. "A New palette() for R
" https://t.co/Ti2plswp55 2. "Color Universal Design (CUD) recommendations (Okabe and Ito, 2002, https://t.co/DSDvIQJhkH )" https://t.co/LPC5fe4ueS

— Shoei (@shoei05) December 19, 2020

Analysis

Steel-Dwass multiple comparison from tidy data

For the Steel-Dwass nonparametric multiple comparison test, Professor Aoki's article (Gunma University) is a helpful reference, and I wrote a custom function to run Steel-Dwass directly from tidy data.

pacman::p_load(tidyverse)

source("http://aoki2.si.gunma-u.ac.jp/R/src/Steel-Dwass.R", encoding="euc-jp")

steel_dwass <- function(df, data, group){
  data_vector <- df %>% pull(data)
  group_vector <- df %>% pull(group)
  result <- Steel.Dwass(data_vector, group_vector)
  result_tibble <- result %>% as_tibble()
  result_tibble <- cbind(steel_dwass = rownames(result),result_tibble)
  result_tibble <- result_tibble %>%
    mutate(star = if_else( p < 0.001, "**", if_else(p < 0.05, "*", "NS"))) %>%
    mutate(note = if_else(p < 0.001, "p<0.001", if_else(p < 0.05, "p<0.05", "NS"))) %>% 
    mutate(across(p, ~ round(.x, digits = 3))) 
  result_tibble
}Code language: PHP (php)

Specify the data column first, followed by the group column.

dat %>% steel_dwass("data", "group")Code language: JavaScript (javascript)

Speeding up {rstan} execution

rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())Code language: PHP (php)

"If you specify rstan_options(auto_write = TRUE) before running MCMC, R creates an RDS file with the .rds extension. It saves this RDS file so that the C++ code does not need to be recompiled. From the second run onward, compilation is unnecessary, making execution faster." https://t.co/2zJnZabvI8

— Shoei (@shoei05) January 10, 2021

"If you specify rstan_options(auto_write = TRUE) before running MCMC... from the second run onward, (C++) compilation is unnecessary, which speeds up execution. Specifying options(mc.cores = parallel::detectCores()) parallelizes the computation, further increasing execution speed." https://t.co/2zJnZabvI8

— Shoei (@shoei05) January 10, 2021

{gtsummary} Personal template (paired tests)

dat |> 
  tbl_summary(
    by = case_control, # 比較したいもの
    include = -ID, # 省きたいもの。でも対応ある検定で使いたい。
        type = list(all_continuous() ~ "continuous2",
                    c("count") ~ "continuous2"), # 列名を明示してもOK
    statistic = all_continuous() ~ c(
       "{mean} ({sd})",
       "{median} ({p25}, {p75})",
       "{min}, {max}"),
     digits = list(where(is.numeric) ~ c(1,1,1,1,1)) # 有効数字
  ) |> 
  add_p(
    # test = list(
    # all_continuous() ~ "t.test"), # t.testを行いたい場合はこちら
    group = ID, # 対応ある検定をする際はIDを明示
    test = list(
    all_continuous() ~ "wilcox.test",
    all_categorical() ~ "mcnemar.test"),
    pvalue_fun = function(x) style_pvalue(x, digits = 2)) |> # 有効数字
  bold_p() |> 
  bold_labels() |> 
  add_overall() Code language: PHP (php)

Note to self pic.twitter.com/JdebV0atoW

— Shoei (@shoei05) December 10, 2023

Specifying continuous and categorical variables all at once in {gtsummary}

pacman::p_load(gtsummary)

table_list <- c("x_1", "x_2", "y_1", "y_2", "z")
table_label_list <- c("X1", "X2", "Y1", "Y2", "Z")
cnt_list <- c("x_1", "x_2")
ctg_list <- c("y_1", "y_2")
dct_list <- c("z")

table_one <- dat %>% 
   select(all_of(table_list)) %>% 
   setNames(all_of(table_label_list)) %>% 
   tbl_summary(
       type = list(cnt_list ~ "continuous",
                   ctg_list ~ "categorical",
                   dct_list ~ "dichotomous"))Code language: PHP (php)

{gtsummary} Writing summaries of continuous variables across multiple lines

pacman::p_load(gtsummary)

table_one <- dat %>%
  select("x","y","z") %>%
   tbl_summary(
     type = all_continuous() ~ "continuous2",
     statistic = all_continuous() ~ c(
       "{N_nonmiss}",
       "{mean} ({sd})",
       "{median} ({p25}, {p75})",
       "{min}, {max}"),
     digits = list(where(is.numeric) ~ c(0,1,1,1,1,1,1,1)),
     missing = "no")Code language: PHP (php)

Note that you need to set type to “continuous2”. Group the decimal places into c() in order of appearance. Here, N is set to an integer, and the rest to 1 decimal place.

To display summaries for continuous variables across multiple lines in {gtsummary}, separate them with commas inside tbl_summary() like this: statistic = all_continuous() ~ c("{N_nonmiss}", "{median} ({p25}, {p75})", "{min}, {max}"). https://t.co/bgWKMDtNrp pic.twitter.com/SRbVAJLtbW

— Shoei (@shoei05) January 5, 2021

{gtsummary} Displaying row/column names and footnotes in Japanese

theme_gtsummary_language("ja")
Code language: JavaScript (javascript)

To revert to the default, run the code below.

reset_gtsummary_theme()

{gtsummary} {snakecase} Converting snake_case variable names to camelCase for display

pacman::p_load(gtsummary, snakecase)

table_one <- dat %>% 
  select("x","y","z") %>%
  set_variable_labels(
    .labels = to_title_case(names(.))) %>% 
  tbl_summary()Code language: PHP (php)

Sorting columns created with {gtsummary} by frequency

pacman::p_load(gtsummary)

table_one <- dat %>% 
  select("x","y","z") %>%
  tbl_summary(
     sort = all_categorical() ~ "frequency")Code language: PHP (php)

For specific columns only (such as A and B),

tbl_summary(sort = c("A","B") ~ "frequency")Code language: JavaScript (javascript)

use the above.

To sort the order of rows by frequency in tbl_summary() from {gtsummary}, set tbl_summary(sort = …). Example: tbl_summary(by = trt, sort = all_categorical() ~ "frequency"). For specific columns only (A and B), use sort = c("A","B") ~ "frequency". https://t.co/9ixslB8VCd pic.twitter.com/NibVxkwF1H

— Shoei (@shoei05) May 3, 2021

Transposing a table created with {gtsummary}

pacman::p_load(gtsummary)
table_one <- dat %>%
    select("x","y","z") %>%
    tbl_summary() %>%
    bold_labels() %>%
    add_n() %>%
    as_tibble() %>%
    t() %>%
    as.data.frame()Code language: PHP (php)

"{gtsummary} One key point here is that you can actually convert this table into a tibble. This means you can export it as a CSV file and also combine it with Rmarkdown." https://t.co/6mkBx74lJ1

— Shoei (@shoei05) September 14, 2020

Adding significance stars in {modelsummary}

They will appear if you write "{estimate} ({std.error}){stars}" instead of estimate = "{estimate} ({std.error})stars" as shown in the help documentation.

To customize the estimate and add stars in {modelsummary}, write "{estimate} ({std.error}){stars}" instead of estimate = "{estimate} ({std.error})stars" as shown in the help documentation. https://t.co/o46qp1hH8y pic.twitter.com/0qnNNxqHHY

— Shoei (@shoei05) February 2, 2021

Grouping with by = across two or more variables in {gtsummary}

In that case, use tbl_strata().

d |>
  tbl_strata(
    strata = group1, 
    ~.x %>% 
      tbl_summary(by = group2))

"You can create a stratified table by using the tbl_summary() function inside the tbl_strata() function and specifying the strata argument.
# minimalist example
d %>%
tbl_strata(
strata = grade,
~.x %>%
tbl_summary(by = trt))
"https://t.co/YU8a5XE8QC pic.twitter.com/WA1Z5tsCYA

— Shoei (@shoei05) May 26, 2023

{corrplot} Customizing a correlation matrix

pacman::p_load(corrplot)

col_list <- c("x","y","z")
labelname_list <- c("X","Y","Z")

dat_cor <- dat %>% 
   select(all_of(col_list)) %>% 
   setNames(all_of(labelname_list)) %>% 
   na.omit()

 p <- dat_cor %>% 
   cor.mtest(method = "spearman")

 corrplot(cor(dat_cor, method = "spearman"),
          method="number",
          tl.col="black",
          tl.cex=1,
          tl.srt=45,
          diag=FALSE)

 corrplot(cor(dat_cor, method = "spearman"),
          p.mat = p$p,
          sig.level=0,
          insig = "p-value",
          method="ellipse",
          type="lower",
          tl.pos = "n",
          cl.pos="n",
          add=TRUE,
          diag=FALSE)Code language: PHP (php)

When using add=TRUE in {corrplot}, you need to highlight and run both corrplot() calls at once. References: ① Image https://t.co/N3xiHN4UZU ② For p-values, calculate them with p <- cor.mtest() and set p.mat = p$p https://t.co/uwI2BIQ5o0 pic.twitter.com/zCeLbRZ5sZ

— Shoei (@shoei05) January 1, 2021

{polycor} Correlation matrix between categorical variables (polychoric correlation matrix)

pacman::p_load(polycor)
ans <- dat %>% hetcor()
ans$correlation
M <- ans$tests
for(i in 1:nrow(M)) {for(j in 1:i) {M[j,i]=M[i,j] }}
p$p <- MCode language: PHP (php)

Proceed as above, and modify part of the {corrplot} code shown earlier.

corrplot(ans$correlation,
         method="number",
         tl.col="black",
         tl.cex=1,
         tl.srt=45,
         diag=FALSE)

corrplot(ans$correlation,
         p.mat = p$p,
         sig.level=0,
         insig = "p-value",
         method="ellipse",
         type="lower",
         tl.pos = "n",
         cl.pos="n",
         add=TRUE,
         diag=FALSE)Code language: PHP (php)

"① The correlation coefficient between two continuous variables is 'Pearson's correlation coefficient'; ② the correlation coefficient between two <ordinal variables> is the 'polychoric correlation coefficient'; ③ the correlation coefficient between an <ordinal variable> and a continuous variable is the 'polyserial correlation coefficient'." https://t.co/4pvPr5nThB

— Shoei (@shoei05) January 1, 2021

{ggplot} Why you shouldn't use xlim and ylim to set the display range

"xlim and ylim turn out-of-range values into NA (missing). By contrast, coord_cartesian does nothing to out-of-range values (it just zooms in). With xlim and ylim, out-of-range values are treated as non-existent. This results in a regression line that differs from the original." https://t.co/svqflhxgWs pic.twitter.com/TJ8HGeCtmv

— Shoei (@shoei05) July 5, 2021

To specify a range, use coord_cartesian(xlim = c(xmin,xmax)).

g + coord_cartesian(xlim = c(0, 1000))

Adding significance bars with {ggsignif} to a plot created with {ggstatsplot}

pacman::p_load(ggsignif, ggstatsplot, wesanderson)
ggstatsplot::ggbetweenstats(
   data = dat,
   x = x,
   y = y,
   pairwise.comparisons = FALSE,
   k = 0, 
   xlab = "x",
   ylab = "y",
   ggstatsplot.layer = TRUE, 
   package = "wesanderson",
   palette = "GrandBudapest1"
 ) +
   geom_signif(comparisons = list(c("a", "b")),
               map_signif_level = c("<strong>* p<0.001"=0.001, "</strong> p<0.01"=0.01, "* p<0.05"=0.05)) Code language: PHP (php)

"{ggstatsplot} is an extension of the ggplot2 package for creating graphics with details from statistical tests included in the information-rich plots themselves." #Rstats https://t.co/ZzcZck1YNn pic.twitter.com/6h1mlCBggp

— Shoei (@shoei05) November 26, 2020

In {ggsignif}, you can now add asterisks using map_signif_level = c("***"=0.001, "**"=0.01, "*"=0.05). https://t.co/SFEzIDYlQ7

— Shoei (@shoei05) December 1, 2020

Communication and miscellaneous

Using pacman::p_load instead of library()

if (!require("pacman")) install.packages("pacman")
pacman::p_load(tidyverse, magrittr, package_n)Code language: PHP (php)
https://plaza.umin.ac.jp/shoei05/wp/index.php/2020/10/24/145

{tomwenseleers/export} {eoffice} Exporting figures and tables to PPT format

pacman::p_load(officer,rvg,openxlsx,ggplot2,flextable,xtable,rgl,stargazer,tikzDevice,xml2,broom,devtools)
pacman::p_load_gh("tomwenseleers/export")
graph2ppt(append=TRUE, upscale=TRUE) Code language: PHP (php)

If you get an error, try the installation method at the link first, and then run graph2ppt().

install.packages("officer")
install.packages("rvg")
install.packages("openxlsx")
install.packages("ggplot2")
install.packages("flextable")
install.packages("xtable")
install.packages("rgl")
install.packages("stargazer")
install.packages("tikzDevice")
install.packages("xml2")
install.packages("broom")
install.packages("devtools")
devtools::install_github("tomwenseleers/export")Code language: PHP (php)

If {export} doesn't work, use {eoffice}.

pacman::p_load(eoffice)
topptx(file="filename.pptx", left = 0, top = 0, width = 8, height = 6, append = TRUE)Code language: PHP (php)

When exporting figures and tables created in R to PPT, running something like graph2ppt(append=TRUE,upscale=TRUE) in {export} is convenient because it outputs them nicely in vector format. Among packages currently on CRAN, {eoffice} seems to have similar functionality (https://t.co/cYrY5G2JiZ ) https://t.co/Xc9nVCLSQR

— Shoei (@shoei05) September 6, 2020

Reducing image file size in ggsave()

ggsave("figure1.tiff", 
    width = ..., 
    height = ..., 
    dpi=300, 
    compression = "lzw")Code language: JavaScript (javascript)

When image files created in R are too large, using ggsave( … , compression = "lzw") can compress files from tens or hundreds of MB down to a few hundred B. This compression solves issues with file size limits during journal submission, or files being too large to attach in Gmail and having to be shared via Google Drive instead. https://t.co/zlRpmmhoXK

— Shoei (@shoei05) May 3, 2021

How to fix transparent backgrounds in plots saved with ggsave()

You can specify the background color with bg, as in ggsave(…, bg = “white“).

ggsave("figure.png", g, bg = "white")Code language: JavaScript (javascript)


Conversely, if you want to make the background transparent, specify bg = “transparent”.

ggsave("figure.png", g, bg = "transparent")Code language: JavaScript (javascript)

Creating necessary folders after launching a project file

Enter the following in the Terminal tab next to the Console:

mkdir {data,thesis,analysis,function,materials}Code language: JavaScript (javascript)
https://plaza.umin.ac.jp/shoei05/wp/index.php/2020/10/25/165

Changing Rmarkdown's default path to a path relative to the project file

knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file())Code language: PHP (php)

"By default, the working directory for R code chunks is the directory that contains the .Rmd file. This means that when you refer to external files within a chunk, you need to know that these paths are relative to the directory of the .Rmd file." https://t.co/hYYWofTnlt

— Shoei (@shoei05) November 1, 2020
https://plaza.umin.ac.jp/shoei05/wp/index.php/2020/11/02/239

Common initial settings in Rmarkdown

Here is an example of the initial setup I often use, along with the default path setting above. Output figures and tables are set to 300 dpi by default.

---
title: "…"
output:
  html_document:
    theme: cerulean
    toc: yes
    toc_depth: 3
    toc_float: yes
    df_print: "kable"
---Code language: JavaScript (javascript)
{r setup, include=FALSE}
knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file())
knitr::opts_chunk$set(echo = FALSE)
knitr::opts_chunk$set(eval = TRUE)
knitr::opts_chunk$set(tidy = FALSE)
knitr::opts_chunk$set(warning = FALSE)
knitr::opts_chunk$set(error = FALSE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(dpi = 300)Code language: PHP (php)
{r}
if (!require("pacman")) install.packages("pacman")
pacman::p_load(tidyverse,readxl,readr,magrittr,...)Code language: PHP (php)

Linking in-text citations to the bibliography with hyperlinks in Rmarkdown

By default, in-text references appear as superscript numbers like 1 that correspond to the numbered list at the end of the document, without hyperlinks. Specifying the “link-citations: true” option in the YAML header before knitting links them with hyperlinks.

link-citations: trueCode language: JavaScript (javascript)

To link in-text citations to the reference list at the end of an R Markdown document with hyperlinks, simply specify the "link-citations: true" option in the YAML header (requires pandoc v1.16 or later). Reference: https://t.co/wR0lrr5q65 pic.twitter.com/GJMeboIsI7

— Shoei (@shoei05) February 1, 2021

Making unconfirmed Japanese input visible while typing

For Japanese input in the RStudio editor, one workaround to display characters while typing is to open the file you want to edit in a second or subsequent editor tab, but the characters disappear if you pop out the window. I found a way to keep them visible even after popping out the window and wrote an article about it. https://t.co/Pafi9AOGHm pic.twitter.com/TArUDiK0Jq

— Shoei (@shoei05) January 3, 2021
Back to contents
English translation of the Japanese article.