Question

Par exemple (pas sûr exemple le plus représentatif si):

N <- 1e6
d1 <- data.frame(x=sample(N,N), y1=rnorm(N))
d2 <- data.frame(x=sample(N,N), y2=rnorm(N))

est ce que j'ai à ce jour:

d <- merge(d1,d2)
# 7.6 sec

library(plyr)
d <- join(d1,d2)
# 2.9 sec

library(data.table)
dt1 <- data.table(d1, key="x")
dt2 <- data.table(d2, key="x")
d <- data.frame( dt1[dt2,list(x,y1,y2=dt2$y2)] )
# 4.9 sec

library(sqldf)
sqldf()
sqldf("create index ix1 on d1(x)")
sqldf("create index ix2 on d2(x)")
d <- sqldf("select * from d1 inner join d2 on d1.x=d2.x")
sqldf()
# 17.4 sec
Était-ce utile?

La solution

L'approche match fonctionne quand il y a une clé unique dans la seconde trame de données pour chaque valeur de clé dans la première. S'il y a des doublons dans la seconde trame de données, puis les approches de match et de fusion ne sont pas les mêmes. Match est, bien sûr, plus rapide, car il ne fait pas autant. En particulier, il ne regarde jamais les clés en double. (Suite après le code)

DF1 = data.frame(a = c(1, 1, 2, 2), b = 1:4)
DF2 = data.frame(b = c(1, 2, 3, 3, 4), c = letters[1:5])
merge(DF1, DF2)
    b a c
  1 1 1 a
  2 2 1 b
  3 3 2 c
  4 3 2 d
  5 4 2 e
DF1$c = DF2$c[match(DF1$b, DF2$b)]
DF1$c
[1] a b c e
Levels: a b c d e

> DF1
  a b c
1 1 1 a
2 1 2 b
3 2 3 c
4 2 4 e

Dans le code sqldf qui a été écrit dans la question, il pourrait sembler que les indices ont été utilisés sur les deux tableaux, mais, en fait, ils sont placés sur des tables qui ont été oblitérées avant la Sql choisir EVER fonctionne et que, en partie, explique pourquoi il est si lent. L'idée de sqldf est que les trames de données dans votre session R constituent la base de données, pas les tables SQLite. Ainsi, chaque fois que le code fait référence à un nom de table non qualifié, il regardera dans votre espace de travail de R pour - non pas dans la base principale de SQLite. Ainsi l'instruction select qui a été montré lit d1 et d2 de l'espace de travail dans la base de données principale de sqlite ceux qui démolir étaient là avec les indices. Par conséquent, il ne une jointure sans index. Si vous voulez utiliser des versions de d1 et d2 qui se trouvaient dans la base de données principale de sqlite vous devez vous référer à eux comme main.d1 et main.d2 et non comme d1 et d2. En outre, si vous essayez de le faire fonctionner aussi vite que possible, alors noter que simple jointure ne peut pas utiliser d'index sur les tables afin que vous puissiez enregistrer le temps de créer un des indices. Dans le code ci-dessous nous illustrons ces points.

Sa peine de noter que le calcul précis peut faire une énorme différence sur quel paquet est le plus rapide. Par exemple, nous faisons une fusion et un agrégat ci-dessous. Nous voyons que les résultats sont presque inversés pour les deux. Dans le premier exemple du plus rapide au plus lent que nous obtenons: data.table, plyr, fusion et sqldf alors que dans le second exemple sqldf, ensemble, data.table et plyr - presque l'inverse de la première. Dans le premier exemple sqldf est 3x plus lent que data.table et dans le second son 200x plus rapide que plyr et 100 fois plus rapide que data.table. Ci-dessous, nous montrons le code d'entrée, les temps de sortie pour la fusion et les temps de sortie pour l'ensemble. Son notant également que la peine sqldf est basé sur une base de données et donc peut gérer plus grand que R peut manipuler des objets (si vous utilisez l'argument nombase de sqldf) tandis que les autres approches sont limitées au traitement dans la mémoire principale. En outre, nous avons illustré sqldf avec SQLite, mais il prend également en charge les bases de données PostgreSQL et H2 ainsi.

library(plyr)
library(data.table)
library(sqldf)

set.seed(123)
N <- 1e5
d1 <- data.frame(x=sample(N,N), y1=rnorm(N))
d2 <- data.frame(x=sample(N,N), y2=rnorm(N))

g1 <- sample(1:1000, N, replace = TRUE)
g2<- sample(1:1000, N, replace = TRUE)
d <- data.frame(d1, g1, g2)

library(rbenchmark)

benchmark(replications = 1, order = "elapsed",
   merge = merge(d1, d2),
   plyr = join(d1, d2),
   data.table = { 
      dt1 <- data.table(d1, key = "x")
      dt2 <- data.table(d2, key = "x")
      data.frame( dt1[dt2,list(x,y1,y2=dt2$y2)] )
      },
   sqldf = sqldf(c("create index ix1 on d1(x)",
      "select * from main.d1 join d2 using(x)"))
)

set.seed(123)
N <- 1e5
g1 <- sample(1:1000, N, replace = TRUE)
g2<- sample(1:1000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

benchmark(replications = 1, order = "elapsed",
   aggregate = aggregate(d[c("x", "y")], d[c("g1", "g2")], mean), 
   data.table = {
      dt <- data.table(d, key = "g1,g2")
      dt[, colMeans(cbind(x, y)), by = "g1,g2"]
   },
   plyr = ddply(d, .(g1, g2), summarise, avx = mean(x), avy=mean(y)),
   sqldf = sqldf(c("create index ix on d(g1, g2)",
      "select g1, g2, avg(x), avg(y) from main.d group by g1, g2"))
)

Les sorties des deux appels de référence comparant les calculs de fusion sont:

Joining by: x
        test replications elapsed relative user.self sys.self user.child sys.child
3 data.table            1    0.34 1.000000      0.31     0.01         NA        NA
2       plyr            1    0.44 1.294118      0.39     0.02         NA        NA
1      merge            1    1.17 3.441176      1.10     0.04         NA        NA
4      sqldf            1    3.34 9.823529      3.24     0.04         NA        NA

La sortie de l'appel de référence en comparant les calculs globaux sont les suivants:

        test replications elapsed  relative user.self sys.self user.child sys.child
4      sqldf            1    2.81  1.000000      2.73     0.02         NA        NA
1  aggregate            1   14.89  5.298932     14.89     0.00         NA        NA
2 data.table            1  132.46 47.138790    131.70     0.08         NA        NA
3       plyr            1  212.69 75.690391    211.57     0.56         NA        NA

Autres conseils

Les 132 secondes rapportés dans les résultats de Gabor pour data.table est le timing en fait les fonctions de base et colMeans cbind (l'allocation de mémoire et la copie induite par l'utilisation de ces fonctions). Il y a de bonnes et de mauvaises façons d'utiliser data.table aussi.

benchmark(replications = 1, order = "elapsed", 
  aggregate = aggregate(d[c("x", "y")], d[c("g1", "g2")], mean),
  data.tableBad = {
     dt <- data.table(d, key = "g1,g2") 
     dt[, colMeans(cbind(x, y)), by = "g1,g2"]
  }, 
  data.tableGood = {
     dt <- data.table(d, key = "g1,g2") 
     dt[, list(mean(x),mean(y)), by = "g1,g2"]
  }, 
  plyr = ddply(d, .(g1, g2), summarise, avx = mean(x), avy=mean(y)),
  sqldf = sqldf(c("create index ix on d(g1, g2)",
      "select g1, g2, avg(x), avg(y) from main.d group by g1, g2"))
  ) 

            test replications elapsed relative user.self sys.self
3 data.tableGood            1    0.15    1.000      0.16     0.00
5          sqldf            1    1.01    6.733      1.01     0.00
2  data.tableBad            1    1.63   10.867      1.61     0.01
1      aggregate            1    6.40   42.667      6.38     0.00
4           plyr            1  317.97 2119.800    265.12    51.05

packageVersion("data.table")
# [1] ‘1.8.2’
packageVersion("plyr")
# [1] ‘1.7.1’
packageVersion("sqldf")
# [1] ‘0.4.6.4’
R.version.string
# R version 2.15.1 (2012-06-22)

S'il vous plaît noter que je ne sais pas si plyr bien s'il vous plaît faire vérifier avec Hadley avant de se fonder sur les horaires de plyr ici. Notez également que le data.table ne comprennent le temps de se convertir à data.table et définir la clé, pour fareness.


Cette réponse a été mis à jour depuis répondu à l'origine en décembre 2010. Les résultats de référence précédents sont ci-dessous. S'il vous plaît voir l'historique des révisions de cette réponse pour voir ce qui a changé.

              test replications elapsed   relative user.self sys.self
4   data.tableBest            1   0.532   1.000000     0.488    0.020
7            sqldf            1   2.059   3.870301     2.041    0.008
3 data.tableBetter            1   9.580  18.007519     9.213    0.220
1        aggregate            1  14.864  27.939850    13.937    0.316
2  data.tableWorst            1 152.046 285.800752   150.173    0.556
6 plyrwithInternal            1 198.283 372.712406   189.391    7.665
5             plyr            1 225.726 424.296992   208.013    8.004

Pour une tâche simple (valeurs uniques des deux côtés de rejoindre) J'utilise match:

system.time({
    d <- d1
    d$y2 <- d2$y2[match(d1$x,d2$x)]
})

Il est beaucoup plus rapide que la fusion (sur ma machine à 0.13s 3.37s).

Mes timings:

  • merge: 3.32s
  • plyr: 0.84s
  • match: 0.12s

Je pensais que ce serait intéressant de poster une référence avec dplyr dans le mélange: (a eu beaucoup de choses en cours d'exécution)

            test replications elapsed relative user.self sys.self
5          dplyr            1    0.25     1.00      0.25     0.00
3 data.tableGood            1    0.28     1.12      0.27     0.00
6          sqldf            1    0.58     2.32      0.57     0.00
2  data.tableBad            1    1.10     4.40      1.09     0.01
1      aggregate            1    4.79    19.16      4.73     0.02
4           plyr            1  186.70   746.80    152.11    30.27

packageVersion("data.table")
[1] ‘1.8.10’
packageVersion("plyr")
[1] ‘1.8’
packageVersion("sqldf")
[1] ‘0.4.7’
packageVersion("dplyr")
[1] ‘0.1.2’
R.version.string
[1] "R version 3.0.2 (2013-09-25)"

Il suffit d'ajouter:

dplyr = summarise(dt_dt, avx = mean(x), avy = mean(y))

et configurer les données pour dplyr avec une table de données:

dt <- tbl_dt(d)
dt_dt <- group_by(dt, g1, g2)

Mise à jour:. J'ai enlevé data.tableBad et plyr et rien que rstudio ouvert (Core i7, 16 Go de RAM)

Avec data.table 1.9 et dplyr avec trame de données:

            test replications elapsed relative user.self sys.self
2 data.tableGood            1    0.02      1.0      0.02     0.00
3          dplyr            1    0.04      2.0      0.04     0.00
4          sqldf            1    0.46     23.0      0.46     0.00
1      aggregate            1    6.11    305.5      6.10     0.02

Avec data.table 1.9 et dplyr avec table de données:

            test replications elapsed relative user.self sys.self
2 data.tableGood            1    0.02        1      0.02     0.00
3          dplyr            1    0.02        1      0.02     0.00
4          sqldf            1    0.44       22      0.43     0.02
1      aggregate            1    6.14      307      6.10     0.01

packageVersion("data.table")
[1] '1.9.0'
packageVersion("dplyr")
[1] '0.1.2'

Par souci de cohérence est ici l'original avec tous et data.table 1.9 et dplyr à l'aide d'une table de données:

            test replications elapsed relative user.self sys.self
5          dplyr            1    0.01        1      0.02     0.00
3 data.tableGood            1    0.02        2      0.01     0.00
6          sqldf            1    0.47       47      0.46     0.00
1      aggregate            1    6.16      616      6.16     0.00
2  data.tableBad            1   15.45     1545     15.38     0.01
4           plyr            1  110.23    11023     90.46    19.52

Je pense que ces données est trop petit pour le nouveau data.table et dplyr:)

ensemble de données plus volumineux:

N <- 1e8
g1 <- sample(1:50000, N, replace = TRUE)
g2<- sample(1:50000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

A pris autour 10-13GB de RAM juste pour contenir les données avant d'exécuter l'indice de référence.

Résultats:

            test replications elapsed relative user.self sys.self
1          dplyr            1   14.88        1      6.24     7.52
2 data.tableGood            1   28.41        1     18.55      9.4

Essayé 1 milliard, mais a fait exploser la RAM. 32GB va la traiter sans problème.


[Modifier Arun] (dotcomken, pourriez-vous s'il vous plaît exécuter ce code et collez vos résultats de cette évaluation? Merci).

require(data.table)
require(dplyr)
require(rbenchmark)

N <- 1e8
g1 <- sample(1:50000, N, replace = TRUE)
g2 <- sample(1:50000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

benchmark(replications = 5, order = "elapsed", 
  data.table = {
     dt <- as.data.table(d) 
     dt[, lapply(.SD, mean), by = "g1,g2"]
  }, 
  dplyr_DF = d %.% group_by(g1, g2) %.% summarise(avx = mean(x), avy=mean(y))
) 

Selon la demande de Arun ici la sortie de ce que vous me avez fourni à exécuter:

        test replications elapsed relative user.self sys.self
1 data.table            5   15.35     1.00     13.77     1.57
2   dplyr_DF            5  137.84     8.98    136.31     1.44

Désolé pour la confusion, tard dans la nuit est arrivé à moi.

Utilisation dplyr avec trame de données semble être la façon moins efficace aux résumés des processus. Est-ce les méthodes pour comparer la fonctionnalité exacte de data.table et dplyr avec leurs méthodes de structure de données inclus? Je préfère presque séparer que la plupart des données devront être nettoyés avant group_by ou créer le data.table. Il pourrait être une question de goût, mais je pense que le plus important est la façon efficace les données peuvent être modélisés.

En utilisant la fonction de fusion et de ses paramètres optionnels:

jointure interne: fusion (DF1, DF2) travaillera pour ces exemples, car R se connecte automatiquement les images par des noms de variables communes, mais vous voulez le plus susceptible de spécifier fusion (DF1, DF2, par = « CustomerId ») pour faire vous que vous étiez correspondant sur que les champs souhaités. Vous pouvez également utiliser la by.x et by.y paramètres si les variables d'appariement ont des noms différents dans les différentes trames de données.

Outer join: merge(x = df1, y = df2, by = "CustomerId", all = TRUE)

Left outer: merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)

Right outer: merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)

Cross join: merge(x = df1, y = df2, by = NULL)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top