2017-08-20 18 views
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)) 

有没有人有一个更整洁解?请注意,我的原始数据框有数百个列,我需要对其中大部分的不同级别/标签进行重新分类,因此该过程必须实现自动化。

+0

哈德利有一个'forcats'包,如果这听起来很有趣。 – lmo

+0

谢谢,我看了一下,它是相当不错的 - 但它不直接提供功能,以我想要的方式操纵数据帧... – Krizbi

回答

0

我很感兴趣,看看有什么其他的解决方案都提出了这一点。我唯一的建议是稍微更改建议的解决方案,以便更清楚地知道frame将以某种方式进行修改,而不是将其留在map2所使用的函数的主体中。

例如,通过frame在调用map2一个额外的参数:

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), .) } 
+0

感谢您的建议,我会将您的帖子标记为已回答,看来这是迄今为止的最佳方法。 – Krizbi

0

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

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功能全,我错了。

这里有一些基准:

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

感谢@David提供非常详细的答案!看起来,虽然我提出的原始解决方案速度相当快... – Krizbi

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" 
1

这是另一种解决方案。我不确定它是多么“优雅”。希望有人可以改进。

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 
相关问题