web-dev-qa-db-fra.com

Remplacer les valeurs dans une image de données en fonction de la table de recherche

J'ai des difficultés à remplacer des valeurs dans une image. Je voudrais remplacer les valeurs basées sur un tableau séparé. Vous trouverez ci-dessous un exemple de ce que j'essaie de faire.

J'ai une table où chaque ligne est un client et chaque colonne est un animal qu'ils ont acheté. Permet d'appeler cette image de données table.

> table
#       P1     P2     P3
# 1    cat lizard parrot
# 2 lizard parrot    cat
# 3 parrot    cat lizard

J'ai aussi une table que je vais référencer appelée lookUp.

> lookUp
#      pet   class
# 1    cat  mammal
# 2 lizard reptile
# 3 parrot    bird

Ce que je veux faire, c'est créer une nouvelle table appelée new avec une fonction qui remplace toutes les valeurs dans table par la colonne class dans lookUp. Je l'ai essayé moi-même en utilisant une fonction lapply, mais j'ai reçu les avertissements suivants.

new <- as.data.frame(lapply(table, function(x) {
  gsub('.*', lookUp[match(x, lookUp$pet) ,2], x)}), stringsAsFactors = FALSE)

Warning messages:
1: In gsub(".*", lookUp[match(x, lookUp$pet), 2], x) :
  argument 'replacement' has length > 1 and only the first element will be used
2: In gsub(".*", lookUp[match(x, lookUp$pet), 2], x) :
  argument 'replacement' has length > 1 and only the first element will be used
3: In gsub(".*", lookUp[match(x, lookUp$pet), 2], x) :
  argument 'replacement' has length > 1 and only the first element will be used

Des idées sur la façon de faire fonctionner cela? 

25
jbunk

Vous avez posté une approche dans votre question qui n'était pas mauvaise. Voici une approche smiliar:

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)])

Une approche alternative qui sera plus rapide est:

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

Notez que j'utilise des crochets vides ([]) dans les deux cas pour conserver la structure de new telle quelle (un data.frame).

(J'utilise df au lieu de table et look au lieu de lookup dans ma réponse)

26
docendo discimus

Une autre option est une combinaison de tidyr et dplyr

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

Chaque fois que vous avez deux data.frames distincts et que vous essayez de transférer des informations de l'un à l'autre, la réponse est de merge .

Tout le monde a sa propre méthode de fusion préférée dans R. Mine est data.table.

En outre, puisque vous souhaitez appliquer cela à plusieurs colonnes, il sera plus rapide de melt et dcast - plutôt que de parcourir en boucle les colonnes, appliquez-le une fois à une table remodelée, puis remodelez à nouveau.

library(data.table)

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

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

Si vous trouvez que la variable dcast/melt est un peu intimidante, voici une approche qui consiste simplement à parcourir en boucle les colonnes; dcast/melt évite simplement la boucle pour résoudre ce problème.

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)])
9
MichaelChirico

Créez un vecteur nommé et parcourez chaque colonne et correspondance, voir:

# 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

les données

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)
5
zx8754

La réponse ci-dessus montrant comment faire cela dans dplyr ne répond pas à la question, le tableau est rempli avec des NA. Cela a fonctionné, j'apprécierais tous les commentaires montrant une meilleure façon:

# 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")

Notez qu'il serait probablement utile de conserver la longue table contenant le client, l'animal, l'espèce de l'animal (?) Et leur classe. Cet exemple ajoute simplement une sauvegarde intermédiaire à une variable:

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")
0
dannit