2012-03-20 50 views
3

我正在處理從數據庫檢索到的分類變量,並且想要使用因子來維護數據的「豐滿度」。哈希或列表支持的因子水平

例如,我有存儲的顏色和它們相關聯的數字ID的表

 
    ID | Color 
------+------- 
    1 | Black 
1805 | Red 
3704 | White 

所以我想使用的一個因素來存儲在數據幀中,例如這樣的信息:

 
Car Model | Color 
----------+------- 
Civic  | Black 
Accord | White 
Sentra | Red 

其中顏色列是一個因子,存儲的基礎數據而不是字符串實際上是c(1,3704,1805) - 與每種顏色相關的ID。

因此,我可以通過修改因子類的對象的levels屬性來創建自定義因子以實現此效果。

不幸的是,正如您在示例中所看到的,我的ID不會遞增。在我的申請中,我有30個關卡,一個關卡的最大ID是9,000。因爲級別存儲在一個數組中的因子,這意味着我正在存儲一個長度爲9,000的整數向量,其中只有30個元素。

是否有任何方法可以使用散列或列表更有效地完成這種效果?即如果我要在因子的levels屬性中使用散列,我可以將所有30個元素與我喜歡的任何索引一起存儲,而無需創建大小爲max(ID)的數組。

在此先感謝!

回答

2

嗯,我敢肯定,你不能改變因素如何工作。一個因子始終具有級別ID,它們是整數1..n,其中n是級別數。

...但你可以輕鬆擁有一個平移向量去你的顏色標識:

# The translation vector... 
colorIds <- c(Black=1,Red=1805,White=3704) 

# Create a factor with the correct levels 
# (but with level ids that are 1,2,3...) 
f <- factor(c('Red','Black','Red','White'), levels=names(colorIds)) 
as.integer(f) # 2 1 2 3 

# Translate level ids to your color ids 
colorIds[f] # 1805 1 1805 3704 

技術上,colorIds並不需要定義顏色的名稱,但它可以更容易有因爲在創建因子級別時使用名稱。你希望明確指定這些級別,以便它們的編號匹配,即使這些級別不是按字母順序排列的(就像你的名字一樣)。

編輯然而,可以創建一個從具有代碼作爲屬性的因子派生的類。讓我們把這種新的光榮級foo

foo <- function(x = character(), levels, codes) { 
    f <- factor(x, levels) 
    attr(f, 'codes') <- codes 
    class(f) <- c('foo', class(f)) 
    f 
} 

`[.foo` <- function(x, ...) { 
    y <- NextMethod('[') 
    attr(y, 'codes') <- attr(x, 'codes') 
    y 
} 

as.integer.foo <- function(x, ...) attr(x,'codes')[unclass(x)] 

# Try it out 
set.seed(42) 
f <- foo(sample(LETTERS[1:5], 10, replace=TRUE), levels=LETTERS[1:5], codes=101:105) 

d <- data.frame(i=11:15, f=f) 

# Try subsetting it... 
d2 <- d[2:5,] 

# Gets the codes, not the level ids... 
as.integer(d2$f) # 105 102 105 104 

然後,您可以修復還等print.foo ...

+0

謝謝,湯米。我希望有一些事情可以避免必須做第二步(查找實際ID),但是你所建議的最終可能會達到最佳效果。 – 2012-03-21 17:49:14

+0

...我用一種可能的解決方案更新了答案... – Tommy 2012-03-21 20:39:53

+0

這個解決方案似乎比其他基於散列的答案稍微不太優雅(儘管可能更容易處理),但是 - 令人驚訝的是 - 我使用此代碼獲得更好的性能比我使用哈希。它看起來像是,雖然不變,但從哈希環境中檢索元素的時間足夠長,以至於當數量級別<〜4000時,基於數組的代碼性能會更好(散列需要恆定的.25s來格式化10,000行df ,你需要.17 + .0000212L其中L是級別的數量)。 – 2012-03-22 16:33:23

0

在考慮這個問題,唯一的功能,一個「水平」需要實現以有有效因子是[訪問器。因此,任何實現[訪問器的對象都可以從任何接口函數的角度看作是一個向量。

我查看了hash類,但看到它使用了正常的R行爲(正如在列表中看到的那樣),當僅使用單個括號時返回原始散列片段(同時提取實際值時使用雙支架)。但是,我是用setMethod()來覆蓋它,我實際上能夠獲得所需的行爲。

library(hash) 

setMethod( 
    '[' , 
    signature(x="hash", i="ANY", j="missing", drop = "missing") , 
    function( 
     x,i,j, ... ,   
     drop 
     ) {  

     if (class(i) == "factor"){ 
      #presumably trying to lookup the values associated with the ordered keys in this hash 
      toReturn <- NULL 
      for (k in make.keys(as.integer(i))){ 
       toReturn <- c(toReturn, get(k, [email protected])) 
      } 
      return(toReturn) 
     } 

     #default, just make keys and get from the environment 
     toReturn <- NULL 
     for (k in make.keys(i)){ 
      toReturn <- c(toReturn, get(k, [email protected])) 
     } 
     return(toReturn)   
    } 
    ) 

as.character.hash <- function(h){ 
    as.character(values(h)) 
} 

print.hash <- function(h){ 
    print(as.character(h)) 
} 

h <- hash(1:26, letters) 

df <- data.frame(ID=1:26, letter=26:1, stringsAsFactors=FALSE) 

attributes(df$letter)$class <- "factor" 
attributes(df$letter)$levels <- h 

> df 
    ID letter 
1 1  z 
2 2  y 
3 3  x 
4 4  w 
5 5  v 
6 6  u 
7 7  t 
8 8  s 
9 9  r 
10 10  q 
11 11  p 
12 12  o 
13 13  n 
14 14  m 
15 15  l 
16 16  k 
17 17  j 
18 18  i 
19 19  h 
20 20  g 
21 21  f 
22 22  e 
23 23  d 
24 24  c 
25 25  b 
26 26  a 
> attributes(df$letter)$levels 
<hash> containing 26 key-value pair(s). 
    1 : a 
    10 : j 
    11 : k 
    12 : l 
    13 : m 
    14 : n 
    15 : o 
    16 : p 
    17 : q 
    18 : r 
    19 : s 
    2 : b 
    20 : t 
    21 : u 
    22 : v 
    23 : w 
    24 : x 
    25 : y 
    26 : z 
    3 : c 
    4 : d 
    5 : e 
    6 : f 
    7 : g 
    8 : h 
    9 : i 
> 
> df[1,2] 
[1] z 
Levels: a j k l m n o p q r s b t u v w x y z c d e f g h i 
> as.integer(df$letter) 
[1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 
[26] 1 

對此的評論?盡我所能,一切正常。它看起來就像打印一樣正常工作,而實際data.frame中存儲的底層數據是未觸及的,所以我不覺得我在那裏會危害任何東西。我甚至可以在我的包中添加一個新的類來實現這個訪問器,以避免必須添加對哈希類的依賴。

任何反饋或對我所忽略的點將非常感激。