优化基础R中的重新编码

时间:2013-03-09 23:10:46

标签: r base

我是基于一些相当长的字符串重新编码变量,这里以字符串A,B,C,D,E和G为例。我想知道是否有办法重新编码而不必重复使用基数R引用df$foo 12次?也许有一些更智能更快的方式我可以探索?这是否真的是在R中最明智的方式?

df <- data.frame(
  foo = 1000:1010,
  bar = letters[1:11])
df  
    foo bar
1  1000   a
2  1001   b
3  1002   c
4  1003   d
5  1004   e
6  1005   f
7  1006   g
8  1007   h
9  1008   i
10 1009   j
11 1010   k

A  <- c(1002)
B  <- c(1007, 1008)
C  <- c(1001, 1003)
D  <- c(1004, 1006)
E  <- c(1000, 1005)
G  <- c(1010, 1009)

df$foo[df$foo %in% A] <- 1
df$foo[df$foo %in% B] <- 2
df$foo[df$foo %in% C] <- 3
df$foo[df$foo %in% D] <- 4
df$foo[df$foo %in% E] <- 5
df$foo[df$foo %in% G] <- 7
df
   foo bar
1    5   a
2    3   b
3    1   c
4    3   d
5    4   e
6    5   f
7    4   g
8    2   h
9    2   i
10   7   j
11   7   k

更新于2013-03-11 05:28:061Z,

我已经重写了五个功能解决方案,可以使用microbenchmark软件包对它们进行比较,结果是Tyler Rinker和flodel的解决方案是最快的解决方案(见下面的结果),并不是说这个问题是关于速度的单独。我也在寻找解决方案中的简洁和智能。出于好奇,我还使用汽车包中的Recode函数添加了一个解决方案。如果我能以更优化的方式重写解决方案,或者微基准测试包不是比较这些功能的最佳方式,请随时告诉我。

df <- data.frame(
  foo = sample(1000:1010, 1e5+22, replace = TRUE),
  bar = rep(letters, 3847))
str(df)

A  <- c(1002)
B  <- c(1007, 1008)
C  <- c(1001, 1003)
D  <- c(1004, 1006)
E  <- c(1000, 1005)
G  <- c(1010, 1009)

# juba's solution
juba <- function(df,foo) within(df, {foo[foo %in% A] <- 1; foo[foo %in% B] <- 2;foo[foo %in% C] <- 3;foo[foo %in% D] <- 4;foo[foo %in% E] <- 5;foo[foo %in% G] <- 7})
# Arun's solution
Arun <- function(df,x) factor(df[,x], levels=c(A,B,C,D,E,G), labels=c(1, rep(c(2:5, 7), each=2)))
# flodel's solution
flodel <- function(df,x) rep(c(1, 2, 3, 4, 5, 7), sapply(list(A, B, C, D, E, G), length))[match(df[,x], unlist(list(A, B, C, D, E, G)))]
# Tyler Rinker's solution
TylerRinker <- function(df,x)  data.frame(vals = unlist(list(A  = c(1002),B  = c(1007, 1008),C  = c(1001, 1003),D  = c(1004, 1006),E  = c(1000, 1005), G = c(1010, 1009))), labs = c(1, rep(c(2:5, 7), each=2)))[match(df[,x], unlist(list(A  = c(1002),B  = c(1007, 1008),C  = c(1001, 1003),D  = c(1004, 1006),E  = c(1000, 1005), G = c(1010, 1009)))), 2] 
# agstudy's solution
agstudy <- function(df,foo) merge(df,data.frame(foo=unlist(list(A, B, C, D, E, G)), val =rep((1:7)[-6],rapply(list(A, B, C, D, E, G), length))))
# Recode from the car package
ReINcar <- function(df,x) Recode(df[,x], "A='A'; B='B'; C='C'; D='D'; E='E'; G='G'")

# install.packages("microbenchmark", dependencies = TRUE)
require(microbenchmark)

# run test
res <- microbenchmark(juba(df, foo), Arun(df, 1), flodel(df, 1), TylerRinker(df,1) ,agstudy(df, foo), ReINcar(df, 1), times = 25)
There were 15 warnings (use warnings() to see them) # warning duo to x's solution

## Print results:
print(res)

数字,

   Unit: milliseconds
                   expr        min         lq     median         uq        max neval
          juba(df, foo)  37.944355  39.521603  41.987174  46.385974  79.559750    25
            Arun(df, 1)  23.833334  24.115776  24.648842  26.987431  55.466448    25
          flodel(df, 1)   3.586179   3.637024   3.956814   6.468735  28.404166    25
     TylerRinker(df, 1)   3.919563   4.115994   4.529926   5.532688   8.508956    25
       agstudy(df, foo) 301.487732 324.641734 334.801005 352.753496 415.421212    25
         ReINcar(df, 1)  73.655566  77.903088  81.745037 101.038791 125.158208    25


### Plot results:
boxplot(res)

微基准测试结果的框图,

Box Plot of microbenchmark results

6 个答案:

答案 0 :(得分:5)

这是一种通用(可扩展)方法,也非常快:

sets <- list(A, B, C, D, E, G)
vals <-    c(1, 2, 3, 4, 5, 7)

keys   <- unlist(sets)
values <- rep(vals, sapply(sets, length))
df$foo <- values[match(df$foo, keys)]

答案 1 :(得分:4)

使用within可以帮助您节省一些击键次数:

df <- within(df,
       {foo[foo %in% A] <- 1;
        foo[foo %in% B] <- 2;
        foo[foo %in% C] <- 3;
        foo[foo %in% D] <- 4;
        foo[foo %in% E] <- 5;
        foo[foo %in% G] <- 7})

答案 2 :(得分:3)

你也可以:(已编辑)

> df$foo <- factor(df$foo, levels=c(A,B,C,D,E,G), labels=c(1, rep(c(2:5, 7), each=2)))

# Warning message:
# In `levels<-`(`*tmp*`, value = if (nl == nL) as.character(labels) else paste0(labels,  :
#   duplicated levels will not be allowed in factors anymore

#    foo bar
# 1    5   a
# 2    3   b
# 3    1   c
# 4    3   d
# 5    4   e
# 6    5   f
# 7    4   g
# 8    2   h
# 9    2   i
# 10   7   j
# 11   7   k

答案 3 :(得分:3)

我的方法(输掉A,B,C ......但是我看到flodel的非常相似)。

keyL <- list(
    A  = c(1002),
    B  = c(1007, 1008),
    C  = c(1001, 1003),
    D  = c(1004, 1006),
    E  = c(1000, 1005),
    G  = c(1010, 1009)
)

key <- data.frame(vals = unlist(keyL), labs = c(1, rep(c(2:5, 7), each=2)))

df$foo2 <- key[match(df$foo, key$vals), 2] 

我不喜欢写旧列,所以创建了一个新列。我还将密钥存储为命名列表。

答案 4 :(得分:2)

另一种选择是使用merge,非常类似于@flodel和@Tyler方法

sets <- list(A, B, C, D, E, G)
df.code = data.frame(foo=unlist(sets),
                     val =rep((1:7)[-6],rapply(sets, length)))
> merge(df,df.code)
    foo bar val
1  1000   a   5
2  1001   b   3
3  1002   c   1
4  1003   d   3
5  1004   e   4
6  1005   f   5
7  1006   g   4
8  1007   h   2
9  1008   i   2
10 1009   j   7
11 1010   k   7 

答案 5 :(得分:1)

我认为这可以做你想要的,尽管使用的格式略有不同。它可能是最快的方法。

library(data.table)

## Create the sample data:
dt <- data.table(foo=sample(1000:1010, 1e5+22, replace = TRUE), bar=rep(letters, 3847), key="foo")

## Create the table that maps the old value of foo to the new one:
dt.recode<-data.table(foo_old=1000:1010, foo_new=c(5L, 3L, 1L, 3L, 4L, 5L, 4L, 2L, 2L, 7L, 7L), key="foo_old")

## Show the result of the join/merge between the original and recoded table:
## (not necesary if you only want to update the original table)
dt[dt.recode]
##       foo bar foo_new
##  1: 1000   a       5
##  2: 1001   b       3
##  3: 1002   c       1
##  4: 1003   d       3
##  5: 1004   e       4
##  6: 1005   f       5
##  7: 1006   g       4
##  8: 1007   h       2
##  9: 1008   i       2
## 10: 1009   j       7
## 11: 1010   k       7

## Same as above, but updates the value of foo in the original table:
dt[dt.recode,foo:=foo_new][]
##     foo bar
##  1:   5   a
##  2:   3   b
##  3:   1   c
##  4:   3   d
##  5:   4   e
##  6:   5   f
##  7:   4   g
##  8:   2   h
##  9:   2   i
## 10:   7   j
## 11:   7   k

以下是如何将数据框转换为数据表(并添加以后加入所需的密钥),如果您愿意,而不是从头创建数据表:

dt <- as.data.table(df)
setkey(dt,foo)

我不确定你想用这种方法计算时间,但是假设dt和dt.recode已经存在且已被键入,那么运行更新表的单行显示我的系统已经过了0个时间。

此外,如果您的A,B,C,D,E,G组具有任何内在含义,我会将它们作为列添加到原始表中。然后你可以加入这个字段,而dt.recode只需要6行(假设你有6个组)。