如何拆分数据框,应用某些功能并将其放回原处?

时间:2018-07-03 22:36:54

标签: r dataframe tidyverse

通常,我有一个包含数字变量和分类变量的数据框,我想根据分类变量拆分数字变量,执行一些操作,然后以数据框的形式将其放回原处。该运算取决于类别中数值向量的整个部分,有时会返回不同长度的向量。我知道如何以丑陋的方式进行此操作(请参阅 下面的示例),但它似乎是一种常见的操作,因此我想知道是否有一种我不知道的简单方法。我特别想知道是否有使用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来做到这一点?

3 个答案:

答案 0 :(得分:2)

使用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

答案 1 :(得分:0)

这是经典的拆分合并方法。您可以按类别变量分组,将一些功能应用于各个分组,然后再组合在一起。在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(v0.2.0)于2018-07-03创建。

答案 2 :(得分:0)

在基数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