Скажем, у меня есть следующие данные:
colA <- c("SampA", "SampB", "SampC")
colB <- c(21, 20, 30)
colC <- c(15, 14, 12)
colD <- c(10, 22, 18)
df <- data.frame(colA, colB, colC, colD)
df
# colA colB colC colD
# 1 SampA 21 15 10
# 2 SampB 20 14 22
# 3 SampC 30 12 18
Я хочу получить средние значения строк и стандартные отклонения для значений в столбцах B-D.
Я могу рассчитать rowMeans следующим образом:
library(dplyr)
df %>% select(., matches("colB|colC|colD")) %>% mutate(rmeans = rowMeans(.))
# colB colC colD rmeans
# 1 21 15 10 15.33333
# 2 20 14 22 18.66667
# 3 30 12 18 20.00000
Но когда я пытаюсь рассчитать стандартное отклонение с помощью sd(), выдает ошибку.
df %>% select(., matches("colB|colC|colD")) %>% mutate(rsds = sapply(., sd(.)))
Error in is.data.frame(x) :
(list) object cannot be coerced to type 'double'
Итак, мой вопрос: как мне рассчитать стандартные отклонения здесь?
Обновлено: я попробовал sapply() с sd(), прочитав первый ответ здесь.
Дополнительное редактирование: не обязательно искать «аккуратное» решение (базовый R также отлично работает).





Пакет magrittr трубы %>% не лучший способ обработки рядами.
Возможно, вам нужно следующее.
df %>%
select(-colA) %>%
t() %>% as.data.frame() %>%
summarise_all(sd)
# V1 V2 V3
#1 5.507571 4.163332 9.165151
@Dunois Возможно, да, но вопрос помечен tidyverse, а каналы — действительно хороший способ обработки данных. Я упомянул об этом в основном потому, что пробовал rowwise() и не смог заставить его работать, поэтому прибегнул к t() %>% as.data.frame().
Вот как заставить rowwise работать: df %>% rowwise() %>% summarize(sd = sd(c(colB,colC,colD)))
@Moody_Mudskipper Вы должны опубликовать это как ответ.
Попробуйте это (с использованием), с rowSds из пакета matrixStats,
library(dplyr)
library(matrixStats)
columns <- c('colB', 'colC', 'colD')
df %>%
mutate(Mean= rowMeans(.[columns]), stdev=rowSds(as.matrix(.[columns])))
Возвращает
colA colB colC colD Mean stdev
1 SampA 21 15 10 15.33333 5.507571
2 SampB 20 14 22 18.66667 4.163332
3 SampC 30 12 18 20.00000 9.165151
Ваши данные
colA <- c("SampA", "SampB", "SampC")
colB <- c(21, 20, 30)
colC <- c(15, 14, 12)
colD <- c(10, 22, 18)
df <- data.frame(colA, colB, colC, colD)
df
Другой подход dplyr и tidyr может быть:
df %>%
pivot_longer(-1) %>%
group_by(colA) %>%
mutate(rsds = sd(value)) %>%
pivot_wider(names_from = "name",
values_from = "value")
colA rsds colB colC colD
<fct> <dbl> <dbl> <dbl> <dbl>
1 SampA 5.51 21 15 10
2 SampB 4.16 20 14 22
3 SampC 9.17 30 12 18
Или, альтернативно, используя rowwise() и do():
df %>%
rowwise() %>%
do(data.frame(., rsds = sd(unlist(.[2:length(.)]))))
colA colB colC colD rsds
* <fct> <dbl> <dbl> <dbl> <dbl>
1 SampA 21 15 10 5.51
2 SampB 20 14 22 4.16
3 SampC 30 12 18 9.17
Или вариант так как dplyr 1.0.0:
df %>%
rowwise() %>%
mutate(rsds = sd(c_across(-1)))
Вот еще один способ использования pmap для получения строк mean и sd
library(purrr)
library(dplyr)
library(tidur_
f1 <- function(x) tibble(Mean = mean(x), SD = sd(x))
df %>%
# select the numeric columns
select_if (is.numeric) %>%
# apply the f1 rowwise to get the mean and sd in transmute
transmute(out = pmap(., ~ f1(c(...)))) %>%
# unnest the list column
unnest %>%
# bind with the original dataset
bind_cols(df, .)
# colA colB colC colD Mean SD
#1 SampA 21 15 10 15.33333 5.507571
#2 SampB 20 14 22 18.66667 4.163332
#3 SampC 30 12 18 20.00000 9.165151
Я уверен, что это, вероятно, где-то спрашивали (и я не могу получить ответ от быстрого поиска в Google), но каково значение c(...)?
@Dunois Мы захватываем все элементы строки с помощью ... и объединяем (c) в вектор
Вы можете использовать pmap или rowwise (или сгруппировать по colA) вместе с mutate :
library(tidyverse)
df %>% mutate(sd = pmap(.[-1], ~sd(c(...)))) # same as transform(df, sd = apply(df[-1],1,sd))
#> colA colB colC colD sd
#> 1 SampA 21 15 10 5.507571
#> 2 SampB 20 14 22 4.163332
#> 3 SampC 30 12 18 9.165151
df %>% rowwise() %>% mutate(sd = sd(c(colB,colC,colD)))
#> Source: local data frame [3 x 5]
#> Groups: <by row>
#>
#> # A tibble: 3 x 5
#> colA colB colC colD sd
#> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 SampA 21 15 10 5.51
#> 2 SampB 20 14 22 4.16
#> 3 SampC 30 12 18 9.17
df %>% group_by(colA) %>% mutate(sd = sd(c(colB,colC,colD)))
#> # A tibble: 3 x 5
#> # Groups: colA [3]
#> colA colB colC colD sd
#> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 SampA 21 15 10 5.51
#> 2 SampB 20 14 22 4.16
#> 3 SampC 30 12 18 9.17
Я заметил, что для dplyr 0.8.3 и tidyverse 1.2.1 ни одно из этих решений не работает с select, например с sd(select(.,-colA)), независимо от использования group_by или rowwise. Есть мысли по этому поводу?
Я не уверен, что вы пытались, но вы, возможно, неправильно поняли правила вставки точек в magrittr. df %>% sd(select(.,-colA)) эквивалентно df %>% sd(., select(.,-colA))
Итак, какой синтаксис будет правильным в случае selecting столбцов (с rowwise() или без select) перед операцией?
Что-то вроде df %>% select(-colA) %>% mutate(sd = pmap(., ~sd(c(...))))?
Да спасибо. pmap это должно быть здесь. Это работает как шарм!
Я вижу, что этот пост немного устарел, но есть несколько довольно сложных ответов, поэтому я решил предложить более простой (и быстрый) подход.
Вычисление средних значений строк тривиально, просто используйте rowMeans:
rowMeans(df[, c('colB', 'colC', 'colD')])
Это векторизовано и очень быстро.
Функции rowSd нет, но написать ее несложно. Вот мои «rowVars», которые я использую.
rowVars <- function(x, na.rm=F) {
# Vectorised version of variance filter
rowSums((x - rowMeans(x, na.rm=na.rm))^2, na.rm=na.rm) / (ncol(x) - 1)
}
Для расчета сд:
sqrt(rowVars(df[, c('colB', 'colC', 'colD')]))
Опять же, векторизованный и быстрый, что может быть важно, если входная матрица большая.
Я не уверен, насколько старая/новая функциональность dplyrc_across соотносится с предыдущими ответами на этой странице, но вот решение, которое почти напрямую вырезано и вставлено из документации для dplyr::c_across:
df %>%
rowwise() %>%
mutate(
mean = mean(c_across(colB:colD)),
sd = sd(c_across(colB:colD))
)
# A tibble: 3 x 6
# Rowwise:
colA colB colC colD mean sd
<fct> <dbl> <dbl> <dbl> <dbl> <dbl>
1 SampA 21 15 10 15.3 5.51
2 SampB 20 14 22 18.7 4.16
3 SampC 30 12 18 20 9.17
Это определенно то, как я бы сделал это сейчас. И, наверное, c_across вышла намного позже? Этот сообщение Хэдли Уикхэма 2020 года.
Я оценил этот ответ для варианта использования со многими столбцами, которые я хотел использовать в вычислении по строкам. Спас меня от того, чтобы печатать их все.
Спасибо, что указали на это. Я никогда не уверен, когда попробовать подход
tidyverse, а когда придерживаться базы R. Вероятно, мне следовало упомянуть в OP, что я не обязательно искал конвейерное решение?