通常,我有一个包含数值变量和分类变量的数据框架,我希望根据分类变量拆分数值变量,执行一些操作,然后以数据框架的形式将其重新组合在一起。该操作依赖于类别中数值向量的整个部分,有时会返回不同长度的向量。我知道如何以一种丑陋的方式来做这件事(见下面的例子),但这似乎是一种常见的操作,所以我想知道是否有一种我不知道的更简单的方法。我特别想知道是否有使用tidyverse
的解决方案。
这是我所说的一个例子。
df = data.frame(y=1:10, g=rep(c("a", "b"), each=5))
假设我想标准化变量y
,使其在分类变量的每个级别上都在0到1之间。以下是完成此操作的一般方法:
do.call(
rbind,
lapply(unique(df$g),
function(level) {
y.current = df$y[df$g==level]
## perform some operation
y.new = (y.current-min(y.current))/
(max(y.current)-min(y.current))
return(data.frame(y=y.new,
g=level))
}
)
)
这需要大量的输入,而且可读性不是很好。有没有更好的方法?
编辑:感谢你的精彩回答。我唯一感兴趣的是使用tidyverse
实现这一点的完全通用的方法。如果我们将示例更改为数值向量的大小减小但大于1的操作,则group_by
/mutate
/summarize
组合将不起作用。例如,假设我想删除每个组中的最大值。我能做到
library(dplyr)
df = data.frame(y=1:10, g=rep(c("a", "b"), each=5))
trans_df = df %>%
group_by(g) %>%
do(y=.$y[-which.max(.$y)])
变换后的数据帧trans_df
具有每个级别具有一个观察值的分组变量,并且变换后的变量作为分组变量的每个级别的列表。我可以使用base R将其放入原始格式
data.frame(g=rep(trans_df$g, times=sapply(trans_df$y, length)),
y=do.call(c, trans_df$y))
但是我如何使用tidyverse
来完成这项工作呢
发布于 2018-07-04 06:55:11
使用data.table:
library(data.table)
df=as.data.table(df)
df[,(y-min(y))/(max(y)-min(y)),by=g]
g V1
1: a 0.00
2: a 0.25
3: a 0.50
4: a 0.75
5: a 1.00
6: b 0.00
7: b 0.25
8: b 0.50
9: b 0.75
10: b 1.00
发布于 2018-07-04 06:53:33
这是经典的拆分-应用-合并方法。您通过分类变量进行分组,将某些函数应用于各个组,然后重新组合在一起。在dplyr
中,这是由group_by
处理的。
df <- data.frame(y=1:10, g=rep(c("a", "b"), each=5))
library(dplyr)
df %>%
group_by(g) %>%
mutate(y2 = (y - min(y)) / (max(y) - min(y)))
#> # A tibble: 10 x 3
#> # Groups: g [2]
#> y g y2
#> <int> <fct> <dbl>
#> 1 1 a 0
#> 2 2 a 0.25
#> 3 3 a 0.5
#> 4 4 a 0.75
#> 5 5 a 1
#> 6 6 b 0
#> 7 7 b 0.25
#> 8 8 b 0.5
#> 9 9 b 0.75
#> 10 10 b 1
df %>%
group_by(g) %>%
top_n(-4, y)
#> # A tibble: 8 x 2
#> # Groups: g [2]
#> y g
#> <int> <fct>
#> 1 1 a
#> 2 2 a
#> 3 3 a
#> 4 4 a
#> 5 6 b
#> 6 7 b
#> 7 8 b
#> 8 9 b
由reprex package创建于2018-07-03 (v0.2.0)。
发布于 2018-07-04 06:57:00
在base R中,您可以这样做:
df$y <- ave(df$y,df$g, FUN = function(y) (y - min(y))/(max(y) - min(y)))
# y g
# 1 0.00 a
# 2 0.25 a
# 3 0.50 a
# 4 0.75 a
# 5 1.00 a
# 6 0.00 b
# 7 0.25 b
# 8 0.50 b
# 9 0.75 b
# 10 1.00 b
或者这个也有同样的效果:
split(df$y,df$g) <- tapply(df$y, df$g, function(y) (y - min(y))/(max(y) - min(y)))
如果您需要处理data.frame的其他变量,则会更加灵活:
by_ <- by(df, df$g, function(x) transform(x, y = (y - min(y))/(max(y) - min(y))))
do.call(rbind, by_)
# y g
# a.1 0.00 a
# a.2 0.25 a
# a.3 0.50 a
# a.4 0.75 a
# a.5 1.00 a
# b.6 0.00 b
# b.7 0.25 b
# b.8 0.50 b
# b.9 0.75 b
# b.10 1.00 b
https://stackoverflow.com/questions/51163629
复制相似问题