Вычислить среднее и стандартное отклонение по группе для нескольких переменных в data.frame

r aggregate reshape reshape2

88405 просмотра

5 ответа

126 Репутация автора

Редактировать - Этот вопрос был первоначально озаглавлен «Длинное и широкое изменение формы данных в R»


Я просто изучаю R и пытаюсь найти способы применить его, чтобы помочь другим в моей жизни. В качестве тестового примера я работаю над изменением формы некоторых данных, и у меня возникают проблемы при использовании примеров, которые я нашел в Интернете. То, с чего я начинаю, выглядит так:

ID  Obs 1   Obs 2   Obs 3
1   43      48      37
1   27      29      22
1   36      32      40
2   33      38      36
2   29      32      27
2   32      31      35
2   25      28      24
3   45      47      42
3   38      40      36

И то, что я хочу закончить, будет выглядеть так:

ID  Obs 1 mean  Obs 1 std dev   Obs 2 mean  Obs 2 std dev
1   x           x               x           x
2   x           x               x           x
3   x           x               x           x

И так далее. В чем я не уверен, так это в том, нужна ли мне дополнительная информация в моих полных данных, или как. Я полагаю, что математическая часть (поиск среднего значения и стандартных отклонений) будет легкой, но мне не удалось найти способ, который, по-видимому, работает, чтобы правильно изменить форму данных, чтобы начать этот процесс.

Большое спасибо за любую помощь.

Автор: user2348358 Источник Размещён: 03.05.2013 08:57

Ответы (5)


16 плюса

42621 Репутация автора

Есть несколько разных способов сделать это. reshape2это полезный пакет Лично мне нравится использоватьdata.table

Ниже приведен пошаговый

Если myDFэто ваш data.frame:

library(data.table)
DT <- data.table(myDF)

DT

# this will get you your mean and SD's for each column
DT[, sapply(.SD, function(x) list(mean=mean(x), sd=sd(x)))]

# adding a `by` argument will give you the groupings
DT[, sapply(.SD, function(x) list(mean=mean(x), sd=sd(x))), by=ID]

# If you would like to round the values: 
DT[, sapply(.SD, function(x) list(mean=round(mean(x), 3), sd=round(sd(x), 3))), by=ID]

# If we want to add names to the columns 
wide <- setnames(DT[, sapply(.SD, function(x) list(mean=round(mean(x), 3), sd=round(sd(x), 3))), by=ID], c("ID", sapply(names(DT)[-1], paste0, c(".men", ".SD"))))

wide

   ID Obs.1.men Obs.1.SD Obs.2.men Obs.2.SD Obs.3.men Obs.3.SD
1:  1    35.333    8.021    36.333   10.214      33.0    9.644
2:  2    29.750    3.594    32.250    4.193      30.5    5.916
3:  3    41.500    4.950    43.500    4.950      39.0    4.243

Кроме того, это может или не может быть полезным

> DT[, sapply(.SD, summary), .SDcols=names(DT)[-1]]
        Obs.1 Obs.2 Obs.3
Min.    25.00 28.00 22.00
1st Qu. 29.00 31.00 27.00
Median  33.00 32.00 36.00
Mean    34.22 36.11 33.22
3rd Qu. 38.00 40.00 37.00
Max.    45.00 48.00 42.00
Автор: Ricardo Saporta Размещён: 03.05.2013 09:08

18 плюса

1522 Репутация автора

Вот, пожалуй, самый простой способ сделать это (с воспроизводимым примером ):

library(plyr)
df <- data.frame(ID=rep(1:3, 3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9))
ddply(df, .(ID), summarize, Obs_1_mean=mean(Obs_1), Obs_1_std_dev=sd(Obs_1),
  Obs_2_mean=mean(Obs_2), Obs_2_std_dev=sd(Obs_2))

   ID  Obs_1_mean Obs_1_std_dev  Obs_2_mean Obs_2_std_dev
1  1 -0.13994642     0.8258445 -0.15186380     0.4251405
2  2  1.49982393     0.2282299  0.50816036     0.5812907
3  3 -0.09269806     0.6115075 -0.01943867     1.3348792

РЕДАКТИРОВАТЬ: следующий подход экономит вам много печатать при работе со многими столбцами.

ddply(df, .(ID), colwise(mean))

  ID      Obs_1      Obs_2      Obs_3
1  1 -0.3748831  0.1787371  1.0749142
2  2 -1.0363973  0.0157575 -0.8826969
3  3  1.0721708 -1.1339571 -0.5983944

ddply(df, .(ID), colwise(sd))

  ID     Obs_1     Obs_2     Obs_3
1  1 0.8732498 0.4853133 0.5945867
2  2 0.2978193 1.0451626 0.5235572
3  3 0.4796820 0.7563216 1.4404602
Автор: Carson Размещён: 03.05.2013 09:16

8 плюса

43904 Репутация автора

Вот еще один подход к data.tableответам, используя данные @ Carson, который немного более читабелен (а также немного быстрее, благодаря использованию lapplyвместо sapply):

library(data.table)
set.seed(1)
dt = data.table(ID=c(1:3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9))

dt[, c(mean = lapply(.SD, mean), sd = lapply(.SD, sd)), by = ID]
#   ID mean.Obs_1 mean.Obs_2 mean.Obs_3  sd.Obs_1  sd.Obs_2  sd.Obs_3
#1:  1  0.4854187 -0.3238542  0.7410611 1.1108687 0.2885969 0.1067961
#2:  2  0.4171586 -0.2397030  0.2041125 0.2875411 1.8732682 0.3438338
#3:  3 -0.3601052  0.8195368 -0.4087233 0.8105370 0.3829833 1.4705692
Автор: eddi Размещён: 03.05.2013 09:31

31 плюса

172238 Репутация автора

Это проблема агрегации, а не проблема изменения формы, как изначально предлагался вопрос - мы хотим агрегировать каждый столбец в среднее и стандартное отклонение по идентификатору. Есть много пакетов, которые решают такие проблемы. В основе R это можно сделать aggregateследующим образом (при условии, DFчто это кадр входных данных):

ag <- aggregate(. ~ ID, DF, function(x) c(mean = mean(x), sd = sd(x)))

Примечание 1: Комментатор указал, что agэто фрейм данных, для которого некоторые столбцы являются матрицами. Хотя изначально это может показаться странным, на самом деле это упрощает доступ. agимеет то же количество столбцов, что и входные данные DF. Его первый столбец ag[[1]]- это, IDа i-й столбец остатка ag[[i+1]](или эквивалентно ag[-1][[i]]) - это матрица статистики для i-го входного столбца наблюдения. Если кто-то хочет получить доступ к j-ой статистике i-го наблюдения, то он ag[[i+1]][, j]также может быть записан как ag[-1][[i]][, j].

С другой стороны, предположим, что есть kстатистические столбцы для каждого наблюдения во входных данных (где k = 2 в вопросе). Затем, если мы сгладим вывод, то для доступа к j-й статистике i-го столбца наблюдения мы должны использовать более сложный ag[[k*(i-1)+j+1]]или эквивалентный метод ag[-1][[k*(i-1)+j]].

Например, сравните простоту первого выражения со вторым:

ag[-1][[2]]
##        mean      sd
## [1,] 36.333 10.2144
## [2,] 32.250  4.1932
## [3,] 43.500  4.9497

ag_flat <- do.call("data.frame", ag) # flatten
ag_flat[-1][, 2 * (2-1) + 1:2]
##   Obs_2.mean Obs_2.sd
## 1     36.333  10.2144
## 2     32.250   4.1932
## 3     43.500   4.9497

Примечание 2: входные данные в воспроизводимой форме:

Lines <- "ID  Obs_1   Obs_2   Obs_3
1   43      48      37
1   27      29      22
1   36      32      40
2   33      38      36
2   29      32      27
2   32      31      35
2   25      28      24
3   45      47      42
3   38      40      36"
DF <- read.table(text = Lines, header = TRUE)
Автор: G. Grothendieck Размещён: 03.05.2013 09:32

9 плюса

3987 Репутация автора

Я добавляю dplyrрешение.

set.seed(1)
df <- data.frame(ID=rep(1:3, 3), Obs_1=rnorm(9), Obs_2=rnorm(9), Obs_3=rnorm(9))

library(dplyr)
df %>% group_by(ID) %>% summarise_each(funs(mean, sd))

#      ID Obs_1_mean Obs_2_mean Obs_3_mean  Obs_1_sd  Obs_2_sd  Obs_3_sd
#   (int)      (dbl)      (dbl)      (dbl)     (dbl)     (dbl)     (dbl)
# 1     1  0.4854187 -0.3238542  0.7410611 1.1108687 0.2885969 0.1067961
# 2     2  0.4171586 -0.2397030  0.2041125 0.2875411 1.8732682 0.3438338
# 3     3 -0.3601052  0.8195368 -0.4087233 0.8105370 0.3829833 1.4705692
Автор: Joe Размещён: 14.04.2016 09:13
Вопросы из категории :
32x32