获取以下示例数据:
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的滥用?)。
答案 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")]
形式的显式nest
,fac
过滤器。然后直接应用mahalanobis
。
要回复您的评论,请使用purrr
选项。由于很容易掌握正在发生的事情,因此,请逐步进行操作:
使用另外一列生成样本数据。
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)))
我们现在在list
cols <- list(cols1 = c("x", "y"), cols2 = c("y", "z"))
因此,我们将为列fac
+ x
中的数据子集计算马氏距离(每y
),然后分别计算y
+ {{1} }。 z
的名称将用作两个距离向量的列名称。
现在可以使用实际的cols
命令:
purrr
简而言之,我们
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
中的条目cols
中nest
中的foo
数据,fac
,生成与嵌套数据一样多的距离列,就像我们在cols
(即子集)中的条目一样,并且mahalanobis
距离数据并将其与原始cols
数据绑定。 答案 2 :(得分:0)
还有使用 group_modify
的 dplyr
函数的方法可以缩短代码。
我使用马氏距离来识别多元异常值。在这里添加应该考虑的变量名称是有意义的(这也是对@TKraft 的回答)。您现在可以指定应该由 cols=c("x","y")
此外,距离本身不能直接用于过滤掉可能的异常值,因为需要一个阈值。该阈值可由卡方分布确定,其中自由度等于要考虑的变量数。
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