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?
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)
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)
Chaque fois que vous avez deux data.frame
s 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)])
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
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)
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")