Can dplyr summarise over several variables without listing each one?
The data.table
idiom is lapply(.SD, mean)
, which is
DT <- data.table(df)
DT[, lapply(.SD, mean), by = sex]
# sex age bmi chol
# 1: boy 55 24 203.5
# 2: girl 51 28 197.0
I'm not sure of a dplyr
idiom for the same thing, but you can do something like
dg <- group_by(df, sex)
# the names of the columns you want to summarize
cols <- names(dg)[-1]
# the dots component of your call to summarise
dots <- sapply(cols ,function(x) substitute(mean(x), list(x=as.name(x))))
do.call(summarise, c(list(.data=dg), dots))
# Source: local data frame [2 x 4]
# sex age bmi chol
# 1 boy 55 24 203.5
# 2 girl 51 28 197.0
Note that there is a github issue #178 to efficienctly implement the plyr
idiom colwise
in dplyr
.
Summarizing multiple columns with dplyr?
In dplyr
(>=1.00) you may use across(everything()
in summarise
to apply a function to all variables:
library(dplyr)
df %>% group_by(grp) %>% summarise(across(everything(), list(mean)))
#> # A tibble: 3 x 5
#> grp a b c d
#> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 1 3.08 2.98 2.98 2.91
#> 2 2 3.03 3.04 2.97 2.87
#> 3 3 2.85 2.95 2.95 3.06
Alternatively, the purrrlyr
package provides the same functionality:
library(purrrlyr)
df %>% slice_rows("grp") %>% dmap(mean)
#> # A tibble: 3 x 5
#> grp a b c d
#> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 1 3.08 2.98 2.98 2.91
#> 2 2 3.03 3.04 2.97 2.87
#> 3 3 2.85 2.95 2.95 3.06
Also don't forget about data.table
(use keyby
to sort sort groups):
library(data.table)
setDT(df)[, lapply(.SD, mean), keyby = grp]
#> grp a b c d
#> 1: 1 3.079412 2.979412 2.979412 2.914706
#> 2: 2 3.029126 3.038835 2.967638 2.873786
#> 3: 3 2.854701 2.948718 2.951567 3.062678
Let's try to compare performance.
library(dplyr)
library(purrrlyr)
library(data.table)
library(bench)
set.seed(123)
n <- 10000
df <- data.frame(
a = sample(1:5, n, replace = TRUE),
b = sample(1:5, n, replace = TRUE),
c = sample(1:5, n, replace = TRUE),
d = sample(1:5, n, replace = TRUE),
grp = sample(1:3, n, replace = TRUE)
)
dt <- setDT(df)
mark(
dplyr = df %>% group_by(grp) %>% summarise(across(everything(), list(mean))),
purrrlyr = df %>% slice_rows("grp") %>% dmap(mean),
data.table = dt[, lapply(.SD, mean), keyby = grp],
check = FALSE
)
#> # A tibble: 3 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 dplyr 2.81ms 2.85ms 328. NA 17.3
#> 2 purrrlyr 7.96ms 8.04ms 123. NA 24.5
#> 3 data.table 596.33µs 707.91µs 1409. NA 10.3
When using `dplyr::summarise()` with the `across()` function can I mix the list and formula syntax
To apply multiple functions in the same across
statement you can use the list
syntax as :
library(dplyr)
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), list(sum = ~sum(!is.na(.)),
mean = mean, sd = sd)))
dplyr summarise : Group by multiple variables in a loop and add results in the same dataframe
library(questionr)
library(tidyverse)
data(hdv2003)
list("trav.satisf", "cuisine", "sexe") %>%
map(~ {
hdv2003 %>%
group_by_at(.x) %>%
summarise(
n = n(),
percent = round((n() / nrow(hdv2003)) * 100, digits = 1),
femmes = round((sum(sexe == "Femme", na.rm = TRUE) / sum(!is.na(sexe))) * 100, digits = 1),
age = round(mean(age, na.rm = TRUE), digits = 1)
) %>%
rename_at(1, ~"group") %>%
mutate(grouping = .x)
}) %>%
bind_rows() %>%
select(grouping, group, everything())
#> # A tibble: 8 x 6
#> grouping group n percent femmes age
#> <chr> <fct> <int> <dbl> <dbl> <dbl>
#> 1 trav.satisf Satisfaction 480 24 51.5 41.4
#> 2 trav.satisf Insatisfaction 117 5.9 47.9 40.3
#> 3 trav.satisf Equilibre 451 22.6 49.9 40.9
#> 4 trav.satisf <NA> 952 47.6 60.2 56
#> 5 cuisine Non 1119 56 43.8 50.1
#> 6 cuisine Oui 881 44 69.4 45.6
#> 7 sexe Homme 899 45 0 48.2
#> 8 sexe Femme 1101 55 100 48.2
Created on 2021-11-12 by the reprex package (v2.0.1)
dplyr: add new variables in summarise for each variable name in a vector
There is a summarise_at
just for this:
iris
%>% group_by(Species)
%>% summarise_at(c("Sepal.Length", "Sepal.Width"), funs(mean, sd), na.rm=TRUE)
# A tibble: 3 x 5
Species Sepal.Length_mean Sepal.Width_mean Sepal.Length_sd Sepal.Width_sd
<fct> <dbl> <dbl> <dbl> <dbl>
1 setosa 5.01 3.43 0.352 0.379
2 versicolor 5.94 2.77 0.516 0.314
3 virginica 6.59 2.97 0.636 0.322
Summarise multiple columns that have to be grouped tidyverse
Get the data in long format and count
:
library(dplyr)
library(tidyr)
df %>% pivot_longer(cols = one:three) %>% count(group1, group2, value)
# group1 group2 value n
# <chr> <chr> <chr> <int>
#1 High female no 1
#2 High female yes 2
#3 High male no 3
#4 High male yes 3
#5 Low female no 2
#6 Low female yes 4
#7 Low male no 1
#8 Low male yes 2
Using dplyr summarize with different operations for multiple columns
As other people have mentioned, this is normally done by calling summarize_each
/ summarize_at
/ summarize_if
for every group of columns that you want to apply the summarizing function to. As far as I know, you would have to create a custom function that performs summarizations to each subset. You can for example set the colnames in such way that you can use the select helpers (e.g. contains()
) to filter just the columns that you want to apply the function to. If not, then you can set the specific column numbers that you want to summarize.
For the example you mentioned, you could try the following:
summarizer <- function(tb, colsone, colstwo, colsthree,
funsone, funstwo, funsthree, group_name) {
return(bind_cols(
summarize_all(select(tb, colsone), .funs = funsone),
summarize_all(select(tb, colstwo), .funs = funstwo) %>%
ungroup() %>% select(-matches(group_name)),
summarize_all(select(tb, colsthree), .funs = funsthree) %>%
ungroup() %>% select(-matches(group_name))
))
}
#With colnames
iris %>% as.tibble() %>%
group_by(Species) %>%
summarizer(colsone = contains("Sepal"),
colstwo = matches("Petal.Length"),
colsthree = c(-contains("Sepal"), -matches("Petal.Length")),
funsone = "sum",
funstwo = "mean",
funsthree = "first",
group_name = "Species")
#With indexes
iris %>% as.tibble() %>%
group_by(Species) %>%
summarizer(colsone = 1:2,
colstwo = 3,
colsthree = 4,
funsone = "sum",
funstwo = "mean",
funsthree = "first",
group_name = "Species")
Related Topics
Finding Rows Containing a Value (Or Values) in Any Column
Increment by 1 For Every Change in Column
Rcpp Pass by Reference Vs. by Value
As.Date With Dates in Format M/D/Y in R
All Levels of a Factor in a Model Matrix in R
How to Put Labels Over Geom_Bar For Each Bar in R With Ggplot2
Custom Legend For Multiple Layer Ggplot
How to Get Week Numbers from Dates
Find How Many Times Duplicated Rows Repeat in R Data Frame
Count the Number of All Words in a String
R: Gsub, Pattern = Vector and Replacement = Vector
How to Read in Numbers With a Comma as Decimal Separator
Convert the Values in a Column into Row Names in an Existing Data Frame
Extract Month and Year from a Zoo::Yearmon Object
Get "Embedded Nul(S) Found in Input" When Reading a CSV Using Read.Csv()