R-在dplyr中使用group_by()和mutate()来应用函数,该向量返回组长度的向量

时间:2018-12-06 22:48:00

标签: r dplyr mutate

获取以下示例数据:

set.seed(1)

foo <- data.frame(x=rnorm(10, 0, 10), y=rnorm(10, 0, 10), fac = c(rep("A", 5), rep("B", 5)))

我想通过变量“ fac”将数据帧“ foo”分成A和B,应用一个函数(马哈拉诺比斯距离)返回每个子组长度的向量,然后将输出变异回原始数据框。例如:

auto.mahalanobis <- function(x) {
  temp <- x[, c("x", "y")]
  return(mahalanobis(temp, center = colMeans(temp, na.rm=T), cov = cov(temp, 
use="pairwise.complete.obs")))
}

foo %>% group_by(fac) %>%
  mutate(mahal = auto.mahalanobis(.))

给出错误。显然,可以通过拆分数据集,应用函数并在将输出重新组合在一起之前将输出添加为列来手动完成此过程。但是必须有一种更有效的方法来执行此操作(也许这是对dplyr的滥用?)。

3 个答案:

答案 0 :(得分:0)

您可以简单地做-

foo %>% group_by(fac) %>%
  mutate(mahal = auto.mahalanobis(data.frame(x, y)))

# A tibble: 10 x 4
# Groups:   fac [2]
        x       y fac   mahal
    <dbl>   <dbl> <fct> <dbl>
 1 - 6.26  15.1   A     1.02 
 2   1.84   3.90  A     0.120
 3 - 8.36 - 6.21  A     2.81 
 4  16.0  -22.1   A     2.84 
 5   3.30  11.2   A     1.21 
 6 - 8.20 - 0.449 B     2.15 
 7   4.87 - 0.162 B     2.86 
 8   7.38   9.44  B     1.23 
 9   5.76   8.21  B     0.675
10 - 3.05   5.94  B     1.08

您可以从函数中删除temp <- x[, c("x", "y")],而只需使用temp代替x作为函数参数。

清理功能-

auto.mahalanobis <- function(temp) {
  mahalanobis(temp,
              center = colMeans(temp, na.rm=T),
              cov = cov(temp, use="pairwise.complete.obs")
              )
}

顺便说一句,您的第一篇文章做得很好!

答案 1 :(得分:0)

如何使用nest代替:

foo %>%
    group_by(fac) %>%
    nest() %>%
    mutate(mahal = map(data, ~mahalanobis(
        .x,
        center = colMeans(.x, na.rm = T),
        cov = cov(.x, use = "pairwise.complete.obs")))) %>%
    unnest()
## A tibble: 10 x 4
#   fac   mahal      x       y
#   <fct> <dbl>  <dbl>   <dbl>
# 1 A     1.02   -6.26  15.1
# 2 A     0.120   1.84   3.90
# 3 A     2.81   -8.36  -6.21
# 4 A     2.84   16.0  -22.1
# 5 A     1.21    3.30  11.2
# 6 B     2.15   -8.20  -0.449
# 7 B     2.86    4.87  -0.162
# 8 B     1.23    7.38   9.44
# 9 B     0.675   5.76   8.21
#10 B     1.08   -3.05   5.94

这里,您避免了"x"分组后的"y"相关列,从而避免了temp <- x[, c("x", "y")]形式的显式nestfac过滤器。然后直接应用mahalanobis


更新

要回复您的评论,请使用purrr选项。由于很容易掌握正在发生的事情,因此,请逐步进行操作:

  1. 使用另外一列生成样本数据。

    set.seed(1)
    foo <- data.frame(
        x = rnorm(10, 0, 10),
        y = rnorm(10, 0, 10),
        z = rnorm(10, 0, 10),
        fac = c(rep("A", 5), rep("B", 5)))
    
  2. 我们现在在list

    中存储定义用于计算马氏距离的数据子集的列。
    cols <- list(cols1 = c("x", "y"), cols2 = c("y", "z"))
    

    因此,我们将为列fac + x中的数据子集计算马氏距离(每y),然后分别计算y + {{1} }。 z的名称将用作两个距离向量的列名称。

  3. 现在可以使用实际的cols命令:

    purrr

    简而言之,我们

    1. 循环访问imap_dfc(cols, ~nest(foo %>% group_by(fac), .x, .key = !!.y) %>% select(!!.y)) %>% mutate_all(function(lst) map(lst, ~mahalanobis( .x, center = colMeans(.x, na.rm = T), cov = cov(., use = "pairwise.complete.obs")))) %>% unnest() %>% bind_cols(foo, .) # x y z fac cols1 cols2 #1 -6.264538 15.1178117 9.1897737 A 1.0197542 1.3608052 #2 1.836433 3.8984324 7.8213630 A 0.1199607 1.1141352 #3 -8.356286 -6.2124058 0.7456498 A 2.8059562 1.5099574 #4 15.952808 -22.1469989 -19.8935170 A 2.8401953 3.0675228 #5 3.295078 11.2493092 6.1982575 A 1.2141337 0.9475794 #6 -8.204684 -0.4493361 -0.5612874 B 2.1517055 1.2284793 #7 4.874291 -0.1619026 -1.5579551 B 2.8626501 1.1724828 #8 7.383247 9.4383621 -14.7075238 B 1.2271316 2.5723023 #9 5.757814 8.2122120 -4.7815006 B 0.6746788 0.6939081 #10 -3.053884 5.9390132 4.1794156 B 1.0838341 2.3328276 中的条目
    2. 根据{{​​1}}中定义的列,每个colsnest中的
    3. foo数据,
    4. 在嵌套和分组数据上应用fac,生成与嵌套数据一样多的距离列,就像我们在cols(即子集)中的条目一样,并且
    5. 最终mahalanobis距离数据并将其与原始cols数据绑定。

答案 2 :(得分:0)

替代方案:group_modify

还有使用 group_modifydplyr 函数的方法可以缩短代码。

扩展 1:选择特定列

我使用马氏距离来识别多元异常值。在这里添加应该考虑的变量名称是有意义的(这也是对@TKraft 的回答)。您现在可以指定应该由 cols=c("x","y")

组成的列

扩展 2:确定多元异常值的阈值

此外,距离本身不能直接用于过滤掉可能的异常值,因为需要一个阈值。该阈值可由卡方分布确定,其中自由度等于要考虑的变量数。

    auto.mahalanobis <- function(temp, cols, chisq.prob=0.95) {
        temp_sel <- temp %>% select(any_of(cols))
        temp$mah <- mahalanobis(temp_sel,
                  center = colMeans(temp_sel, na.rm=T),
                  cov = cov(temp_sel, use="pairwise.complete.obs")
                  )
        threshold <- qchisq(chisq.prob, length(cols))
        temp$mah_chisq <- temp$mah > threshold
      return(temp)}
      
      
    foo %>%
      group_by(fac) %>%
      group_modify(~ auto.mahalanobis(temp=.x, cols=c("x","y"), chisq.prob = .975))
# A tibble: 10 x 5
# Groups:   fac [2]
   fac       x       y   mah mah_chisq
   <chr> <dbl>   <dbl> <dbl> <lgl>    
 1 A     -6.26  15.1   1.02  FALSE    
 2 A      1.84   3.90  0.120 FALSE    
 3 A     -8.36  -6.21  2.81  FALSE    
 4 A     16.0  -22.1   2.84  FALSE    
 5 A      3.30  11.2   1.21  FALSE    
 6 B     -8.20  -0.449 2.15  FALSE    
 7 B      4.87  -0.162 2.86  FALSE    
 8 B      7.38   9.44  1.23  FALSE    
 9 B      5.76   8.21  0.675 FALSE    
10 B     -3.05   5.94  1.08  FALSE