1
votes

Vectorisation du remplacement de nom sur une plage de colonnes

J'ai un grand ensemble de données donc ceci est un exemple de jouet.

Voici le dataframe df

df[["Partner1"]] <- key[ match(df[['Partner1']], key[['name']] ) , 'id']

Je souhaite désidentifier chaque membre des colonnes Target et Partner à l'aide de la clé fournie ici.

structure(list(name = structure(c(2L, 5L, 1L, 6L, 4L, 3L), .Label = c("Andrew", 
"Jim", "Kurt", "Lester", "Mickey", "Taylor"), class = "factor"), 
    id = structure(c(2L, 5L, 1L, 6L, 4L, 3L), .Label = c("A3", 
    "J9", "K5", "L4", "M4", "T7"), class = "factor")), class = "data.frame", row.names = c(NA, 
-6L))

Je sais que vous pouvez remplacer les noms de chaque colonne individuellement de cette manière

structure(list(Target = structure(c(1L, 4L, 5L, 2L, 3L), .Label = c("Jim", 
"Kurt", "Lester", "Tara", "Taylor"), class = "factor"), Gender = structure(c(2L, 
1L, 1L, 2L, 2L), .Label = c("F", "M"), class = "factor"), Partner1 = structure(c(1L, 
4L, 4L, 2L, 3L), .Label = c("Andrew", "Jim", "Mickey", "Taylor"
), class = "factor"), Partner2 = structure(c(2L, 3L, 1L, 4L, 
3L), .Label = c("Andrew", "Jim", "Kurt", "Mickey"), class = "factor"), 
    Partner4 = structure(c(4L, 3L, 2L, 3L, 1L), .Label = c("Andrew", 
    "Jim", "Lester", "Tara"), class = "factor")), class = "data.frame", row.names = c(NA, 
-5L))

mais j'aimerais pour le vectoriser afin que je puisse recoder chaque nom à l'intérieur de la clé avec son id correspondant sur toutes les colonnes en parallèle

Les données réelles seront des centaines de colonnes et environ 30 de ces colonnes seront celles que voulez vous désidentifier

Des suggestions?


1 commentaires

Donc quelque chose comme cbind ({colonnes que je veux}) puis exécuter lapply sur tout?


3 Réponses :


2
votes

Une possibilité en utilisant tidyverse :

data.frame(apply(df[, -2], 2, function(x) as.character(df2$id)[match(x, as.character(df2$name))]),
           Gender = df[, 2])

  Target Partner1 Partner2 Partner4 Gender
1     J9       A3       J9     <NA>      M
2   <NA>       T7       K5       L4      F
3     T7       T7       A3       J9      F
4     K5       J9       M4       L4      M
5     L4       M4       K5       A3      M

Premièrement, il effectue une transformation de données très longue. Deuxièmement, il joint le df transformé avec df2. S'il y a un ID dans df2 pour le nom dans df, il remplace le nom dans df par cet ID, sinon avec NA. Enfin, il transforme les données au format d'origine.

Ou une solution de base R:

df %>%
 rowid_to_column() %>%
 gather(var, val, -rowid) %>%
 left_join(df2, by = c("val" = "name")) %>%
 mutate(val = ifelse(var == "Gender", val, 
                     ifelse(!is.na(id), paste0(id), NA_character_))) %>%
 select(-id) %>%
 spread(var, val) %>%
 select(-rowid)

  Gender Partner1 Partner2 Partner4 Target
1      M       A3       J9     <NA>     J9
2      F       T7       K5       L4   <NA>
3      F       T7       A3       J9     T7
4      M       J9       M4       L4     K5
5      M       M4       K5       A3     L4


1 commentaires

Pour la base R, existe-t-il un moyen de rendre les autres colonnes immuables afin de ne pas avoir à rattacher les colonnes de genre qui ne sont pas désidentifiées? Mon exemple réel comporte plusieurs centaines de colonnes que je souhaite conserver et que je préfère ne pas sélectionner chacune d'elles dans la fonction data.frame



3
votes

Une solution possible en base R:

> df2
  Target Gender Partner1 Partner2 Partner4
1     J9      M       A3       J9     Tara
2   Tara      F       T7       K5       L4
3     T7      F       T7       A3       J9
4     K5      M       J9       M4       L4
5     L4      M       M4       K5       A3

Résultat:

# column names to replace
cols <- c('Target','Partner1','Partner2','Partner4')
# convert df subset to a matrix of characters
mx <- as.matrix(df[,cols])
# get the replacements values using match
repl <- as.character(key$id)[match(mx,as.character(key$name))]
# substitute NA's in replacements with the original values
repl[is.na(repl)] <- mx[is.na(repl)]
# create a copy of df
df2 <- df
# replace the values of df2 with the replacements
df2[,cols] <- repl


0 commentaires

3
votes

Une autre solution de base R :

# Create lookup vector
lu_vect <- setNames(as.character(df2[["id"]]), df2[["name"]])
lu_vect
#   Jim Mickey Andrew Taylor Lester   Kurt 
#  "J9"   "M4"   "A3"   "T7"   "L4"   "K5"

# Make a list of columns we want to *update*
cols_to_anonymise <- c("Target", "Partner1", "Partner2", "Partner4")

# Anonymise column by column, if name is not in key, replace by NA
df[cols_to_anonymise] <- lapply(
  df[cols_to_anonymise],
  function(x) lu_vect[as.character(x)]
)

# Print out results
df
#   Target Gender Partner1 Partner2 Partner4
# 1     J9      M       A3       J9     <NA>
# 2   <NA>      F       T7       K5       L4
# 3     T7      F       T7       A3       J9
# 4     K5      M       J9       M4       L4
# 5     L4      M       M4       K5       A3


3 commentaires

Regarde et fonctionne très bien! dans la partie cols_to_anonymise, puis-je sélectionner uniquement une plage de noms de colonnes comme c ("Target", "Partner1: Partner4")? Je suppose que cela ne ressemblerait pas à ça, mais je suis curieux car l'exemple complet a beaucoup de colonnes à sélectionner et je préfère indexer à partir de la première et de la dernière colonne.


Aussi, y a-t-il une raison pour laquelle vous utilisez la fonction as.character ici? Juste curieux


Sélection des colonnes. Peut-être que startsWith () ou grep () peut vous aider? les variables de facteur se ressemblent mais sont en fait très différentes des variables de caractère.