【问题标题】:How can I split a data frame, apply some function, and put it back together?如何拆分数据框,应用某些功能,然后将其重新组合在一起?
【发布时间】:2018-07-03 22:36:53
【问题描述】:

我经常有一个数据框,里面包含一个数值变量和一个分类变量,我想根据分类变量对数值变量进行拆分,进行一些操作,然后以数据框的形式重新组合起来。该操作取决于一个类别中数值向量的整个部分,并且有时会返回一个不同长度的向量。我知道如何以一种丑陋的方式做到这一点(见 下面的示例),但这似乎是一种常见的操作,所以我想知道是否有一种我不知道的更简单的方法。我特别想知道是否有使用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呢?

【问题讨论】:

    标签: r dataframe tidyverse


    【解决方案1】:

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

    【讨论】:

    • 而且可能更快! =)
    • 这也是唯一允许转换后的向量具有不同长度的方法。我被卖了!
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      这是经典的拆分-应用-组合方法。您按分类变量进行分组,将某些功能应用于各个组,然后重新组合在一起。在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 年 7 月 3 日创建。

      【讨论】:

      • 如果需要缩小尺寸怎么办?例如,如果要删除每个组中的最大值怎么办?我尝试了df %&gt;% group_by(g) %&gt;% transmute(y2=y[-which.max(y)]) 并得到了一个错误。
      • mutate 要求结果的长度为一(在这种情况下它们被复制)或相同的长度,summarise 要求长度为一。您可以使用do 进行任意计算,但在这种情况下您只想使用top_n
      • 好吧,有趣。让我们坚持我删除每个组的最高值的示例。我想使用do 来了解它是如何工作的。如果我这样做df %&gt;% group_by(g) %&gt;% do(y=y[-which.max(y)]),我会得到一个数据框,其中分组变量的每个级别都有一行,转换后的向量作为列表。有没有tidyverse 的方法可以将其转回原始格式的数据框?
      • 是的,tidyr::unnest 除非我把它和记忆中的其他东西混在一起。 do 返回 list-columns 因为它不知道你打算为每个组返回什么,所以它把它放在一个可以处理所有事情的列表中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 2019-05-19
      • 2015-08-09
      相关资源
      最近更新 更多