Вычисление стандартного отклонения по строкам

Скажем, у меня есть следующие данные:

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 также отлично работает).

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
5 954
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Пакет magrittr трубы %>% не лучший способ обработки рядами.
Возможно, вам нужно следующее.

df %>% 
  select(-colA) %>%
  t() %>% as.data.frame() %>%
  summarise_all(sd)
#        V1       V2       V3
#1 5.507571 4.163332 9.165151

Спасибо, что указали на это. Я никогда не уверен, когда попробовать подход tidyverse, а когда придерживаться базы R. Вероятно, мне следовало упомянуть в OP, что я не обязательно искал конвейерное решение?

Dunois 24.03.2019 19:49

@Dunois Возможно, да, но вопрос помечен tidyverse, а каналы — действительно хороший способ обработки данных. Я упомянул об этом в основном потому, что пробовал rowwise() и не смог заставить его работать, поэтому прибегнул к t() %>% as.data.frame().

Rui Barradas 24.03.2019 20:02

Вот как заставить rowwise работать: df %>% rowwise() %>% summarize(sd = sd(c(colB,colC,colD)))

Moody_Mudskipper 25.03.2019 15:34

@Moody_Mudskipper Вы должны опубликовать это как ответ.

Rui Barradas 25.03.2019 16:10
Ответ принят как подходящий

Попробуйте это (с использованием), с 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 24.03.2019 22:42

@Dunois Мы захватываем все элементы строки с помощью ... и объединяем (c) в вектор

akrun 25.03.2019 04:54

Вы можете использовать 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. Есть мысли по этому поводу?

Fourier 17.12.2020 08:50

Я не уверен, что вы пытались, но вы, возможно, неправильно поняли правила вставки точек в magrittr. df %>% sd(select(.,-colA)) эквивалентно df %>% sd(., select(.,-colA))

Moody_Mudskipper 17.12.2020 10:33

Итак, какой синтаксис будет правильным в случае selecting столбцов (с rowwise() или без select) перед операцией?

Fourier 17.12.2020 10:44

Что-то вроде df %>% select(-colA) %>% mutate(sd = pmap(., ~sd(c(...))))?

Moody_Mudskipper 17.12.2020 11:20

Да спасибо. pmap это должно быть здесь. Это работает как шарм!

Fourier 17.12.2020 11:38

Я вижу, что этот пост немного устарел, но есть несколько довольно сложных ответов, поэтому я решил предложить более простой (и быстрый) подход.

Вычисление средних значений строк тривиально, просто используйте 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 года.

Dunois 03.03.2021 15:22

Я оценил этот ответ для варианта использования со многими столбцами, которые я хотел использовать в вычислении по строкам. Спас меня от того, чтобы печатать их все.

Andrew Jackson 05.03.2021 22:55

Другие вопросы по теме