2017-08-20 42 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 
相關問題