17

私はこれによく出くわすので、それには良いイディオムが必要だと思います。「製品」を含む一連の属性を持つ data.frame があるとします。また、製品をブランド + サイズに変換するキーもあります。製品コード 1 ~ 3 はタイレノール、4 ~ 6 はアドビル、7 ~ 9 はバイエル、10 ~ 12 はジェネリックです。

これをコード化する最速の (人間の時間の観点から) 方法は何ですか?

カテゴリが 3 つ以下の場合はネストされた を使用する傾向がifelseあり、3 つを超える場合はデータ テーブルを入力してマージします。より良いアイデアはありますか? Stata には、この種のことに対してかなり気の利いたrecodeコマンドがありますが、データとコードの混合を少し促進しすぎていると思います。

dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L, 
7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, 
-20L), class = "data.frame")
4

13 に答える 13

19

変数を因子に変換し、そのレベルをlevels<-関数ごとに変更できます。1 つのコマンドでは、次のようになります。

`levels<-`(
    factor(dat$product),
    list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)

手順:

brands <- factor(dat$product)
levels(brands) <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
于 2012-05-03T13:19:39.420 に答える
14

リストを連想配列として使用して、brand -> product codeマッピングを定義できます。つまり、次のようになります。

brands <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)

これを取得したら、これを反転してproduct code -> brandリストを作成するか (大量のメモリを消費する可能性があります)、検索機能を使用することができます。

find.key <- function(x, li, default=NA) {
    ret <- rep.int(default, length(x))
    for (key in names(li)) {
        ret[x %in% li[[key]]] <- key
    }
    return(ret)
}

この関数を記述するより良い方法があると確信しています (forループは私を悩ませます!) が、少なくともベクトル化されているため、リストを 1 回通過するだけで済みます。

それを使用すると、次のようになります。

> dat$brand <- find.key(dat$product, brands)
> dat
   product   brand
1       11 Generic
2       11 Generic
3        9   Bayer
4        9   Bayer
5        6   Advil
6        1 Tylenol
7       11 Generic
8        5   Advil
9        7   Bayer
10      11 Generic
11       5   Advil
12      11 Generic
13       4   Advil
14       3 Tylenol
15      10 Generic
16       7   Bayer
17      10 Generic
18       5   Advil
19       9   Bayer
20       8   Bayer

recodeとのlevels<-ソリューションは非常に優れていますが、これよりも大幅に遅くなります (一度find.keyこれを使用すると、 よりも人間にとって簡単でrecodeあり、同等levels<-です)。

> microbenchmark(
     recode=recode(dat$product,recodes="1:3='Tylenol';4:6='Advil';7:9='Bayer';10:12='Generic'"), 
     find.key=find.key(dat$product, brands),
     levels=`levels<-`(factor(dat$product),brands))
Unit: microseconds
      expr      min        lq    median        uq      max
1 find.key   64.325   69.9815   76.8950   83.8445  221.748
2   levels  240.535  248.1470  274.7565  306.8490 1477.707
3   recode 1636.039 1683.4275 1730.8170 1855.8320 3095.938

(バージョンを適切にベンチマークすることはできませんがswitch、上記のすべてよりも高速であるように見えますが、recodeソリューションよりも人間にとってさらに悪いです。)

于 2012-05-03T12:53:41.910 に答える
13

パッケージのrecode機能が気に入っています:car

library(car)

dat$brand <- recode(dat$product,
  recodes="1:3='Tylenol';4:6='Advil';7:9='Bayer';10:12='Generic'")

# > dat
#    product   brand
# 1       11 Generic
# 2       11 Generic
# 3        9   Bayer
# 4        9   Bayer
# 5        6   Advil
# 6        1 Tylenol
# 7       11 Generic
# 8        5   Advil
# 9        7   Bayer
# 10      11 Generic
# 11       5   Advil
# 12      11 Generic
# 13       4   Advil
# 14       3 Tylenol
# 15      10 Generic
# 16       7   Bayer
# 17      10 Generic
# 18       5   Advil
# 19       9   Bayer
# 20       8   Bayer
于 2012-05-03T13:09:25.983 に答える
8

私はよく以下のテクニックを使用します。

key <- c()
key[1:3] <- "Tylenol"
key[4:6] <- "Advil"
key[7:9] <- "Bayer"
key[10:12] <- "Generic"

それで、

> key[dat$product]
 [1] "Generic" "Generic" "Bayer"   "Bayer"   "Advil"   "Tylenol" "Generic" "Advil"   "Bayer"   "Generic"
[11] "Advil"   "Generic" "Advil"   "Tylenol" "Generic" "Bayer"   "Generic" "Advil"   "Bayer"   "Bayer"  
于 2012-05-03T16:48:00.237 に答える
7

「データベース アプローチ」とは、プロダクト キーの定義用に別のテーブル (data.frame) を保持することです。プロダクト キーはブランドだけでなくサイズにも変換されると言うので、さらに理にかなっています。

product.keys <- read.table(textConnection("

product brand   size
1       Tylenol small
2       Tylenol medium
3       Tylenol large
4       Advil   small
5       Advil   medium
6       Advil   large
7       Bayer   small
8       Bayer   medium
9       Bayer   large
10      Generic small
11      Generic medium
12      Generic large

"), header = TRUE)

次に、次を使用してデータを結合できますmerge

merge(dat, product.keys, by = "product")
#    product   brand   size
# 1        1 Tylenol  small
# 2        3 Tylenol  large
# 3        4   Advil  small
# 4        5   Advil medium
# 5        5   Advil medium
# 6        5   Advil medium
# 7        6   Advil  large
# 8        7   Bayer  small
# 9        7   Bayer  small
# 10       8   Bayer medium
# 11       9   Bayer  large
# 12       9   Bayer  large
# 13       9   Bayer  large
# 14      10 Generic  small
# 15      10 Generic  small
# 16      11 Generic medium
# 17      11 Generic medium
# 18      11 Generic medium
# 19      11 Generic medium
# 20      11 Generic medium

お気づきのように、行の順序は によって保持されませんmerge。これが問題になる場合、plyrパッケージにはjoin順序を保持する機能があります。

library(plyr)
join(dat, product.keys, by = "product")
#    product   brand   size
# 1       11 Generic medium
# 2       11 Generic medium
# 3        9   Bayer  large
# 4        9   Bayer  large
# 5        6   Advil  large
# 6        1 Tylenol  small
# 7       11 Generic medium
# 8        5   Advil medium
# 9        7   Bayer  small
# 10      11 Generic medium
# 11       5   Advil medium
# 12      11 Generic medium
# 13       4   Advil  small
# 14       3 Tylenol  large
# 15      10 Generic  small
# 16       7   Bayer  small
# 17      10 Generic  small
# 18       5   Advil medium
# 19       9   Bayer  large
# 20       8   Bayer medium

最後に、テーブルが大きくて速度が問題になる場合は、data.tabledata.frames の代わりに data.tables (パッケージから) を使用することを検討してください。

于 2012-05-04T01:00:58.063 に答える
6

これには多少の入力が必要ですが、本当に巨大なデータ セットがある場合は、これが最適な方法です。talkstats.com の Bryangoodrich と Dason が教えてくれました。ハッシュ テーブルを使用しているか、ルックアップ テーブルを含む環境を作成しています。私は実際にこれを.Rprofile (つまりハッシュ関数) に保存して、辞書型の検索に使用しています。

データを少し大きくするために、データを 1000 回複製しました。

#################################################
# THE HASH FUNCTION (CREATES A ENW ENVIRONMENT) #
#################################################
hash <- function(x, type = "character") {
    e <- new.env(hash = TRUE, size = nrow(x), parent = emptyenv())
    char <- function(col) assign(col[1], as.character(col[2]), envir = e)
    num <- function(col) assign(col[1], as.numeric(col[2]), envir = e)
    FUN <- if(type=="character") char else num
    apply(x, 1, FUN)
    return(e)
}
###################################
# YOUR DATA REPLICATED 1000 TIMES #
###################################
dat <- dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L, 
    7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, 
    -20L), class = "data.frame")
dat <- dat[rep(seq_len(nrow(dat)), 1000), , drop=FALSE]
rownames(dat) <-NULL
dat
#########################
# CREATE A LOOKUP TABLE #
#########################
med.lookup <- data.frame(val=as.character(1:12), 
    med=rep(c('Tylenol', 'Advil', 'Bayer', 'Generic'), each=3))  

########################################
# USE hash TO CREATE A ENW ENVIRONMENT #
########################################  
meds <- hash(med.lookup)  

##############################
# CREATE A RECODING FUNCTION #
##############################          
recoder <- function(x){
    x <- as.character(x) #turn the numbers to character
    rc <- function(x){
       if(exists(x, env = meds))get(x, e = meds) else NA 
    }  
    sapply(x, rc, USE.NAMES = FALSE) 
}
#############
# HASH AWAY #
#############
recoder(dat[, 1])    

この場合、ハッシュは遅くなりますが、再コード化するレベルが増えると、他のものよりも速度が上がります。

于 2012-05-03T13:40:34.043 に答える
3

ifelseネストされた'sよりもやや読みやすい:

unlist(lapply(as.character(dat$product), switch,
              `1`=,`2`=,`3`='tylenol',
              `4`=,`5`=,`6`='advil',
              `7`=,`8`=,`9`='bayer',
              `10`=,`11`=,`12`='generic'))

警告: あまり効率的ではありません。

于 2012-05-03T12:56:48.540 に答える
2

私はこの機能を使用する傾向があります:

recoder <- function (x, from = c(), to = c()) {
  missing.levels <- unique(x)
  missing.levels <- missing.levels[!missing.levels %in% from]
  if (length(missing.levels) > 0) {
    from <- append(x = from, values = missing.levels)
    to <- append(x = to, values = missing.levels)
  }
  to[match(x, from)]
}

次のように:

recoder(x = dat$product, from = 1:12, to = c(rep("Product1", 3), rep("Product2", 3), rep("Product3", 3), rep("Product4", 3)))
于 2012-09-05T19:35:07.420 に答える
1

例のように連続したグループにコードがある場合、これはcutマスタードかもしれません:

cut(dat$product,seq(0,12,by=3),labels=c("Tylenol","Advil","Bayer","Generic"))
 [1] Generic Generic Bayer   Bayer   Advil   Tylenol Generic Advil   Bayer  
[10] Generic Advil   Generic Advil   Tylenol Generic Bayer   Generic Advil  
[19] Bayer   Bayer  
Levels: Tylenol Advil Bayer Generic
于 2012-09-05T16:51:08.433 に答える
0

もありarules:discretizeますが、ラベルを値の範囲から分離する必要があるため、あまり好きではありません。

library(arules)
discretize( dat$product, method = "fixed", categories = c( 1,3,6,9,12 ), labels = c("Tylenol","Advil","Bayer","Generic") )

[1] Generic Generic Generic Generic Bayer   Tylenol Generic Advil   Bayer   Generic Advil   Generic Advil   Advil   Generic Bayer   Generic Advil   Generic Bayer  
Levels: Tylenol Advil Bayer Generic
于 2014-08-18T10:25:23.097 に答える