根据查找表替换数据框中的值

时间:2016-02-25 19:16:58

标签: r dataframe lookup

我在替换数据框中的值时遇到了一些麻烦。我想基于单独的表替换值。以下是我想要做的一个例子。

我有一张桌子,每排都是顾客,每列都是他们购买的动物。让我们调用此数据框|data startTag tag|. data := '123456778' startTag := false. tag := ''. data asArray do: [:char | tag := tag,char]

table

我还有一个名为> table # P1 P2 P3 # 1 cat lizard parrot # 2 lizard parrot cat # 3 parrot cat lizard 的表格。

lookUp

我想要做的是创建一个名为> lookUp # pet class # 1 cat mammal # 2 lizard reptile # 3 parrot bird 的新表,其中一个函数将new中的所有值替换为table中的class列。我自己尝试使用lookUp函数,但是我收到了以下警告。

lapply

有关如何使这项工作的任何想法?

7 个答案:

答案 0 :(得分:29)

你在问题​​中发布了一个不错的方法。这是一种熟悉的方法:

new <- df  # create a copy of df
# using lapply, loop over columns and match values to the look up table. store in "new".
new[] <- lapply(df, function(x) look$class[match(x, look$pet)])

更快的另一种方法是:

new <- df
new[] <- look$class[match(unlist(df), look$pet)]

请注意,我在两种情况下都使用空括号([])来保持new的结构(data.frame)。

(我在答案中使用df代替tablelook代替lookup

答案 1 :(得分:19)

其他选项是tidyrdplyr

的组合
library(dplyr)
library(tidyr)
table %>%
   gather(key = "pet") %>%
   left_join(lookup, by = "pet") %>%
   spread(key = pet, value = class)

答案 2 :(得分:11)

如果您有两个单独的data.frame并且正在尝试将信息从一个传递到另一个,那么答案就是 merge

每个人都有自己喜欢的合并方法。我的是data.table

此外,由于您要对许多列执行此操作,meltdcast的速度会更快 - 而不是循环遍历列,将其应用到重新整形的表中,然后重新整形试。

library(data.table)

#the row names will be our ID variable for melting
setDT(table, keep.rownames = TRUE) 
setDT(lookUp)

#now melt, merge, recast
# melting (reshape wide to long)
table[ , melt(.SD, id.vars = 'rn')     
       # merging
       ][lookup, new_value := i.class, on = c(value = 'pet') 
         #reform back to original shape
         ][ , dcast(.SD, rn ~ variable, value.var = 'new_value')]
#    rn      P1      P2      P3
# 1:  1  mammal reptile    bird
# 2:  2 reptile    bird  mammal
# 3:  3    bird  mammal reptile

如果您发现dcast / melt位有点令人生畏,这里的方法只是循环遍历列; dcast / melt只是回避了这个问题的循环。

setDT(table) #don't need row names this time
setDT(lookUp)

sapply(names(table), #(or to whichever are the relevant columns)
       function(cc) table[lookUp, (cc) := #merge, replace
                            #need to pass a _named_ vector to 'on', so use setNames
                            i.class, on = setNames("pet", cc)])

答案 3 :(得分:5)

创建一个命名向量,并循环遍历每一列并匹配,请参阅:

# make lookup vector with names
lookUp1 <- setNames(as.character(lookUp$class), lookUp$pet)
lookUp1    
#      cat    lizard    parrot 
# "mammal" "reptile"    "bird" 

# match on names get values from lookup vector
res <- data.frame(lapply(df1, function(i) lookUp1[i]))
# reset rownames
rownames(res) <- NULL

# res
#        P1      P2      P3
# 1  mammal reptile    bird
# 2 reptile    bird  mammal
# 3    bird  mammal reptile

数据

df1 <- read.table(text = "
       P1     P2     P3
 1    cat lizard parrot
 2 lizard parrot    cat
 3 parrot    cat lizard", header = TRUE)

lookUp <- read.table(text = "
      pet   class
 1    cat  mammal
 2 lizard reptile
 3 parrot    bird", header = TRUE)

答案 4 :(得分:1)

我是使用内置的 factor 完成的。

table$P1 <- factor(table$P1, levels=lookUp$pet, labels=lookUp$class)
table$P2 <- factor(table$P2, levels=lookUp$pet, labels=lookUp$class)
table$P3 <- factor(table$P3, levels=lookUp$pet, labels=lookUp$class)

答案 5 :(得分:0)

在dplyr中显示如何执行此操作的答案above无法回答问题,表中填充了NA。这有效,我将不胜感激任何评论显示更好的方式:

# Add a customer column so that we can put things back in the right order
table$customer = seq(nrow(table))
classTable <- table %>% 
    # put in long format, naming column filled with P1, P2, P3 "petCount"
    gather(key="petCount", value="pet", -customer) %>% 
    # add a new column based on the pet's class in data frame "lookup"
    left_join(lookup, by="pet") %>%
    # since you wanted to replace the values in "table" with their
    # "class", remove the pet column
    select(-pet) %>% 
    # put data back into wide format
    spread(key="petCount", value="class")

请注意,保留包含客户,宠物,宠物物种(?)及其类别的长桌可能很有用。此示例只是将中间保存添加到变量:

table$customer = seq(nrow(table))
petClasses <- table %>% 
    gather(key="petCount", value="pet", -customer) %>% 
    left_join(lookup, by="pet")

custPetClasses <- petClasses %>%
    select(-pet) %>% 
    spread(key="petCount", value="class")

答案 6 :(得分:0)

我尝试了其他方法,但是我的大型数据集花了很长时间。我改为使用以下内容:

    # make table "new" using ifelse. See data below to avoid re-typing it
    new <- ifelse(table1 =="cat", "mammal",
                        ifelse(table1 == "lizard", "reptile",
                               ifelse(table1 =="parrot", "bird", NA)))

此方法要求您为代码编写更多文本,但是ifelse的矢量化使其运行更快。您必须根据数据决定是否要花费更多时间编写代码或等待计算机运行。如果要确保它有效(iflese命令中没有任何错字),可以使用apply(new, 2, function(x) mean(is.na(x)))

数据

    # create the data table
    table1 <- read.table(text = "
       P1     P2     P3
     1    cat lizard parrot
     2 lizard parrot    cat
     3 parrot    cat lizard", header = TRUE)