首页 文章

以编程方式分解数据框中的选定列,整齐的方式?

提问于
浏览
2

这是一个简化的例子:

library(tidyverse)

frame <- tribble(
  ~a, ~b, ~c,
   1,  1,  2,
   5,  4,  7,
   2,  3,  4, 
   3,  1,  6
)

key <- tribble(
  ~col, ~name, ~type, ~labels,
     1,   "a",   "f",     c("one", "two", "three", "four", "five"),
     2,   "b",   "f",     c("uno", "dos", "tres", "cuatro"),
     3,   "c",   "f",     1:7
)

是否有一种优雅的方式以编程方式扫描 frame 中的列并根据 key 中的参数应用特定因子类?预期结果将是:

# A tibble: 4 x 3
       a      b      c
  <fctr> <fctr> <fctr>
1    one    uno      2
2   five cuatro      7
3    two   tres      4
4  three    uno      6

到目前为止,我所拥有的最佳解决方案是使用 purrrmap2() ,但IMO的分配不是最优雅的:

frame[key$col] <- map2(key$col, key$labels, 
        function(x, y) factor(frame[[x]], levels = 1:length(y), labels = y))

有没有人有更整洁的解决方案?请注意,我的原始数据框有数百列,我需要重新考虑大多数列的不同级别/标签,因此该过程必须自动化 .

4 回答

  • 0

    我很想知道为此提出了哪些其他解决方案 . 我唯一的建议是稍微更改建议的解决方案,以便更清楚 frame 将以某种方式进行修改,而不是将其留在 map2 使用的函数体内 .

    例如,在 map2 的调用中将 frame 作为附加参数传递:

    frame[key$col] <- map2(key$col, key$labels, 
                           function(x, y, z) factor(z[[x]], levels = 1:length(y), labels = y), 
                           frame)
    

    或者使用管道运算符 %>% 执行相同的操作:

    frame[key$col] <- frame %>%
      { map2(key$col, key$labels, 
             function(x, y, z) factor(z[[x]], levels = 1:length(y), labels = y), .) }
    
  • 1

    这是另一种解决方案 . 我不确定它是如何"elegant" . 希望有人可以改进 .

    suppressPackageStartupMessages(library(tidyverse))
    
    frame <- tribble(
      ~a, ~b, ~c,
      1,  1,  2,
      5,  4,  7,
      2,  3,  4, 
      3,  1,  6
    )
    
    key <- tribble(
      ~col, ~name, ~type, ~labels,
      1,   "a",   "f",     c("one", "two", "three", "four", "five"),
      2,   "b",   "f",     c("uno", "dos", "tres", "cuatro"),
      3,   "c",   "f",     1:7
    )
    
    colnames(frame) %>% 
      map(~ {
        factor(pull(frame, .x),
               levels = 1:length(pluck(key[key$name == .x, "labels"], 1, 1)),
               labels = pluck(key[key$name == .x, "labels"], 1, 1))
      }) %>% 
      set_names(colnames(frame)) %>% 
      as_tibble()
    #> # A tibble: 4 x 3
    #>        a      b      c
    #>   <fctr> <fctr> <fctr>
    #> 1    one    uno      2
    #> 2   five cuatro      7
    #> 3    two   tres      4
    #> 4  three    uno      6
    
  • 0

    我不知道这个答案是否满足你整洁的要求,因为它使用了一个普通的旧循环 . 但是它完成了这项工作,在我看来,它很容易阅读/理解,而且速度也相当快 .

    library(tidyverse)
    frame <- tribble(
     ~a, ~b, ~c,
     1,  1,  2,
     5,  4,  7,
     2,  3,  4, 
     3,  1,  6
    )
    
    key <- tribble(
     ~col, ~name, ~type, ~labels,
     1,   "a",   "f",     c("one", "two", "three", "four", "five"),
     2,   "b",   "f",     c("uno", "dos", "tres", "cuatro"),
     3,   "c",   "f",     1:7
    )
    
    for (i in 1:nrow(key)) {
     var <- key$name[[i]]
     x <- frame[[var]]
     labs <- key$labels[[i]]
     lvls <- 1:max(length(x), length(labs)) # make sure to have the right lengths
    
     frame <- frame %>% mutate(!! var := factor(x, levels = lvls, labels = labs))
    }
    
    frame
    #> # A tibble: 4 x 3
    #>        a      b      c
    #>   <fctr> <fctr> <fctr>
    #> 1    one    uno      2
    #> 2   five cuatro      7
    #> 3    two   tres      4
    #> 4  three    uno      6
    

    典型的整洁方法是重新整形数据以将所有变量放在一列中,然后将函数应用于该列,最后将其重新整形为原始格式 . 但是,因素并不是真的那样,因此我们需要使用其他方法 . 这些因素甚至被认为是整洁的吗?

    编辑

    关于我假设for循环类似于 map2 -function,我错了 .

    以下是一些基准测试:

    library(microbenchmark)
    
    frame1 <- frame
    frame2 <- frame
    
    microbenchmark(
     map2 = {
      frame1[key$col] <- map2(key$col, key$labels, 
                              function(x, y) factor(frame[[x]], 
                                                    levels = 1:max(frame[[x]],
                                                                   length(y)), 
                                                    labels = y))
     },
     forloop = {
      for (i in 1:nrow(key)) {
       var <- key$name[[i]]
       x <- frame2[[var]]
       labs <- key$labels[[i]]
       lvls <- 1:max(length(x), length(labs))
       frame2 <- frame2 %>% mutate(!! var := factor(x, levels = lvls, labels = labs))
      }
     }
    )
    
    # Unit: microseconds
    # expr         min         lq       mean    median         uq       max neval cld
    # map2      375.53   416.5805   514.3126   450.825   484.2175  3601.636   100  a 
    # forloop 11407.80 12110.0090 12816.6606 12564.176 13425.6840 16632.682   100   b
    
  • 0

    对于这个问题,您可以使用基本R代码:

    (A=`names<-`(data.frame(mapply(function(x,y)x[y],key$labels,frame)),key$name))
          a      b c
    1   one    uno 2
    2  five cuatro 7
    3   two   tres 4
    4 three    uno 6
    
     sapply(A,class)
       a        b        c 
    "factor" "factor" "factor"
    

相关问题