如何使用dplyr计算R中的分组z分数?

时间:2017-09-12 21:47:21

标签: r dplyr data-science

使用iris数据集我正在尝试计算每个变量的z分数。我通过执行以下操作获得整齐格式的数据:

library(reshape2)
library(dplyr)
test <- iris
test <- melt(iris,id.vars = 'Species')

这给了我以下内容:

  Species     variable value
1  setosa Sepal.Length   5.1
2  setosa Sepal.Length   4.9
3  setosa Sepal.Length   4.7
4  setosa Sepal.Length   4.6
5  setosa Sepal.Length   5.0
6  setosa Sepal.Length   5.4

但是当我尝试使用以下内容为每个组创建一个z-score列(例如,Sepal.Length的z-score将不会与Sepal.Width的z-score相比):

test <- test %>% 
  group_by(Species, variable) %>% 
  mutate(z_score = (value - mean(value)) / sd(value))

生成的z分数尚未分组,并且基于所有数据。

使用dpylr按组返回z分数的最佳方法是什么?

非常感谢!

2 个答案:

答案 0 :(得分:4)

您的代码按组给您z分数。在我看来,这些z分数应该完全可比,因为您已经将每个组分别缩放为= 0和sd = 1,而不是根据平均值和sd对每个值进行缩放。完整的数据框架。例如:

library(tidyverse)

首先,设置融化的数据框:

dat = iris %>% 
  gather(variable, value, -Species) %>%
  group_by(Species, variable) %>% 
  mutate(z_score_group = (value - mean(value)) / sd(value)) %>%   # You can also use scale(value) as pointed out by @RuiBarradas
  ungroup %>% 
  mutate(z_score_ungrouped = (value - mean(value)) / sd(value)) 

现在查看前三行并与直接计算进行比较:

head(dat, 3)

#   Species     variable value z_score_group z_score_ungrouped
# 1  setosa Sepal.Length   5.1     0.2666745         0.8278959
# 2  setosa Sepal.Length   4.9    -0.3007180         0.7266552
# 3  setosa Sepal.Length   4.7    -0.8681105         0.6254145

# z-scores by group
with(dat, (value[1:3] - mean(value[Species=="setosa" & variable=="Sepal.Length"])) / sd(value[Species=="setosa" & variable=="Sepal.Length"]))

# [1]  0.2666745 -0.3007180 -0.8681105

# ungrouped z-scores
with(dat, (value[1:3] - mean(value)) / sd(value))

# [1] 0.8278959 0.7266552 0.6254145

现在可视化z分数:下面的第一个图是原始数据。第二个是未分组的z分数 - 我们刚刚将数据重新调整为整体均值= 0且SD = 1。第三个图是您的代码生成的。每个组已单独缩放为均值= 0且SD = 1。

gridExtra::grid.arrange(
  grobs=setNames(names(dat)[c(3,5,4)], names(dat)[c(3,5,4)]) %>% 
    map(~ ggplot(dat %>% mutate(group=paste(Species,variable,sep="_")), 
                 aes_string(.x, colour="group")) + geom_density()),
  ncol=1)

enter image description here

答案 1 :(得分:0)

我认为在使用mean/sd计算z分数时,您会感到很复杂。只需使用函数scale

test <- test %>% 
  group_by(Species, variable) %>% 
  mutate(z_score = scale(value))

test
## A tibble: 600 x 4
## Groups:   Species, variable [12]
#   Species     variable value     z_score
#    <fctr>       <fctr> <dbl>       <dbl>
# 1  setosa Sepal.Length   5.1  0.26667447
# 2  setosa Sepal.Length   4.9 -0.30071802
# 3  setosa Sepal.Length   4.7 -0.86811050
# 4  setosa Sepal.Length   4.6 -1.15180675
# 5  setosa Sepal.Length   5.0 -0.01702177
# 6  setosa Sepal.Length   5.4  1.11776320
# 7  setosa Sepal.Length   4.6 -1.15180675
# 8  setosa Sepal.Length   5.0 -0.01702177
# 9  setosa Sepal.Length   4.4 -1.71919923
#10  setosa Sepal.Length   4.9 -0.30071802
## ... with 590 more rows

修改
在OP的评论之后,我发布了一些代码来获取Petal.Width具有正z_score的行。

i1 <- which(test$variable == "Petal.Width" & test$z_score > 0)
test[i1, ]
## A tibble: 61 x 4
## Groups:   Species, variable [3]
#   Species    variable value  z_score
#    <fctr>      <fctr> <dbl>    <dbl>
# 1  setosa Petal.Width   0.4 1.461300
# 2  setosa Petal.Width   0.3 0.512404
# 3  setosa Petal.Width   0.4 1.461300
# 4  setosa Petal.Width   0.4 1.461300
# 5  setosa Petal.Width   0.3 0.512404
# 6  setosa Petal.Width   0.3 0.512404
# 7  setosa Petal.Width   0.3 0.512404
# 8  setosa Petal.Width   0.4 1.461300
# 9  setosa Petal.Width   0.5 2.410197
#10  setosa Petal.Width   0.4 1.461300
## ... with 51 more rows