2015-09-08 1 views
0

Je souhaite créer une nouvelle colonne calculée (le résumé du texte d'une autre colonne). Pour vous reproduisez créer un df comme exemple reproductible:La nouvelle colonne de données comme fonction (résumé) d'une autre ne fonctionne pas pour moi

df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE) 

> head(df,3) 
       name 
1 ZKBOZVFKNJBRSDWTUEYR 
2 RQPHUECABPQZLKZPTFLG 
3 FTBVBEQTRLLUGUVHDKAY 

Maintenant, je veux une 2ème colonne avec le condensé du « nom » col pour chaque ligne Cela fonctionne très bien, mais il est lent (chaque md5 est différent et il est le condensé de la colonne de nom correspondant):

> df$md5 <- sapply(df$name, digest) 
> head(df, 3) 
       name        md5 
1 ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2 
2 RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053 
3 FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078 

Mais (en utilisant dplyr) ne voit fonctionne pas et je ne sais pas pourquoi: le md5 est la même pour chaque ligne! En fait, c'est le résumé du nom complet de df $, y compris toutes les lignes. S'il vous plaît, quelqu'un peut-il m'expliquer?

> df <- mutate(df, md5=digest(name)) 
> head(df, 3) 
        name        md5 
1 ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8 
2 RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8 
3 FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8 

Encore une fois si je le chemin de la table de données, il semble que cela ne fonctionne pas en utilisant la méthode standard pour de nouvelles variables:

> dt <- data.table(df) 
> dt[, md5:=digest(name)] 
> head(dt,3) 
        name        md5 
1: ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8 
2: RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8 
3: FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8 

Si je force à un groupe alors il fonctionne à nouveau (mais lent) :

> dt[,md5:=digest(name), by=name] 
> head(dt, 3) 
        name        md5 
1: ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2 
2: RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053 
3: FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078 

J'ai également testé tapply et de travaux (création d'un facteur, mais mes données réelles que des millions de lignes et il est très lent). Puis, d'abord, quelqu'un peut-il m'expliquer pourquoi le mpl de dplyr ne prend pas la valeur de chaque ligne pour calculer le résumé et pourquoi la même chose se produit avec la notation de table de données (à moins que je groupe)? Ensuite, y a-t-il un moyen plus rapide de calculer ce résumé pour toutes les lignes?

+0

dplyr mute peut prendre une variable de la même taille que la trame de données, ou de longueur 1, auquel cas elle va copier cette valeur afin qu'elle corresponde au nombre de lignes dans votre trame de données.Comme vous l'avez déjà vu, 'digest (df $ name)' donne un seul md5 (vous pouvez calculer le hachage d'objets R arbitraires, et c'est ce que vous faites en ne l'appelant pas explicitement sur chaque ligne), copié dans chaque ligne de la nouvelle colonne par mutation. – dd3

Répondre

2

Considérant que vous avez un ensemble de données très grand, il est préférable de tester les différentes approches sur un ensemble de données un peu plus grand (pour cet exemple, j'utilise 100000 lignes, plus gros ensembles de données prennent les âges sur mon système):

df <- data.frame(name = replicate(1e5, paste(sample(LETTERS, 20, replace=TRUE), collapse="")), stringsAsFactors=FALSE) 

Première , nous allons examiner plusieurs approches possibles:

# base R 
df$md5 <- sapply(df$name, digest) 

# data.table (grouping by name, based on the assumption that all names are unique) 
dt[, md5:=digest(name), name] 

# data.table with a unique identifier for each row 
dt[,indx:=.I][, md5:=digest(name), indx] 

# dplyr (grouping by name, based on the assumption that all names are unique) 
df %>% group_by(name) %>% mutate(md5=digest(name)) 

# dplyr with rowwise (from the other answer) 
df %>% rowwise() %>% mutate(md5=digest(name)) 

En second lieu, test qui appraoch est le plus rapide:

library(rbenchmark) 
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"), 
      baseR = df$md5 <- sapply(df$name, digest), 
      dtbl1 = dt[, md5:=digest(name), name], 
      dtbl2 = dt[,indx:=.I][, md5:=digest(name), indx], 
      dplyr = df %>% group_by(name) %>% mutate(md5=digest(name)), 
      rowwi = df %>% rowwise() %>% mutate(md5=digest(name))) 

qui donne:

test elapsed relative 
2 dtbl1 77.878 1.000 
3 dtbl2 78.343 1.006 
1 baseR 81.399 1.045 
5 rowwi 118.799 1.525 
4 dplyr 129.748 1.666 

Alors, coller à une solution de base de R n'est pas un si mauvais choix du tout. Je soupçonne que la raison pour laquelle il est lent sur votre jeu de données réel est probablement la fonction digest et pas un mauvais comportement d'un certain paquet/fonction.

+0

Merci, je vois que le problème clé n'est pas l'approche mais la fonction elle-même. –

+0

Vous méritez mon vote mais je ne peux pas le faire comme étant nouveau le système ne permet pas de le faire. –

+0

@EmilioG. Bien que vous ne puissiez pas donner un upvote, vous pouvez accepter la réponse. Cela donnera aux futurs lecteurs une idée de la valeur de la solution. Voir aussi cette page d'aide: [Que dois-je faire lorsque quelqu'un répond à ma question?] (Http://stackoverflow.com/help/someone-answers) – Jaap

1

La raison pour laquelle vous obtenez la même valeur md5 est que la fonction digest n'est pas une fonction vectorisée. Pour résoudre ce problème, placez rowwise avant de muter comme dans:

df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE) 
ptm <- proc.time() 
df %>% rowwise() %>% mutate(md5=digest(name)) %>% print(n=3) 

1 SSYNAIPPMBNICTXCTZMH cf06eaeab2a4b1b3f0fb964e91867702 
2 XAFNBFYOXSDIFSSCGKKX 28cb7f90ac14f4a2ee5743a1dce91ac7 
3 TMWBHOHWVDSRUPBGKYGS a248a7eb31657555b2bf8b463b7e3ce3 
..     ...        ... 

proc.time() - ptm 
user system elapsed 
0.09 0.00 0.09 

En ce qui concerne la vitesse, vous pouvez voir il n'a fallu que 1/10 de seconde sur mon bureau.