Étant donné deux trames de données:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Comment puis-je faire un style de base de données, c'est-à-dire un style SQL, des jointures ? Autrement dit, comment puis-je obtenir:
- Une jointure interne de
df1
etdf2
:
Renvoie uniquement les lignes dans lesquelles le tableau de gauche a des clés correspondantes dans le tableau de droite. - Une jointure externe de
df1
etdf2
:
Renvoie toutes les lignes des deux tables, joint les enregistrements de gauche qui ont des clés correspondantes dans la table de droite. - Une jointure externe gauche (ou simplement une jointure gauche) de
df1
etdf2
retourne toutes les lignes de la table de gauche et toutes les lignes avec des clés correspondantes de la table de droite. - Une jointure externe droite de
df1
etdf2
retourne toutes les lignes de la table de droite et toutes les lignes avec des clés correspondantes de la table de gauche.
Crédit supplémentaire:
Comment puis-je faire une instruction de sélection de style SQL?
Réponses:
En utilisant la
merge
fonction et ses paramètres facultatifs:Jointure interne:
merge(df1, df2)
fonctionnera pour ces exemples parce que R joint automatiquement les trames par des noms de variables communs, mais vous voudrez probablement spécifiermerge(df1, df2, by = "CustomerId")
pour vous assurer que vous correspondiez uniquement sur les champs souhaités. Vous pouvez également utiliser lesparamètresby.x
etby.y
si les variables correspondantes ont des noms différents dans les différentes trames de données.Jointure externe:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Extérieur gauche:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Extérieur droit:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Jointure croisée:
merge(x = df1, y = df2, by = NULL)
Tout comme avec la jointure interne, vous voudrez probablement passer explicitement "CustomerId" à R comme variable correspondante.Je pense qu'il est presque toujours préférable d'indiquer explicitement les identifiants sur lesquels vous souhaitez fusionner; il est plus sûr si les data.frames d'entrée changent de façon inattendue et plus faciles à lire plus tard.Vous pouvez fusionner sur plusieurs colonnes en donnant
by
un vecteur, par exempleby = c("CustomerId", "OrderId")
.Si les noms de colonne sur
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
lesquels fusionner ne sont pas identiques, vous pouvez spécifier, par exemple, oùCustomerId_in_df1
est le nom de la colonne dans le premier bloc de données etCustomerId_in_df2
le nom de la colonne dans le deuxième bloc de données. (Ceux-ci peuvent également être des vecteurs si vous devez fusionner sur plusieurs colonnes.)la source
data.table
package - c'est un tout nouvel ensemble de syntaxe de jointure, mais c'est radicalement plus rapide que tout ce dont nous parlons ici.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
maintenant, même fonction juste plus rapidement.Je recommanderais de vérifier le package sqldf de Gabor Grothendieck , qui vous permet d'exprimer ces opérations en SQL.
Je trouve que la syntaxe SQL est plus simple et plus naturelle que son équivalent R (mais cela peut simplement refléter mon biais RDBMS).
Voir sqldf GitHub de Gabor pour plus d'informations sur les jointures.
la source
Il y a l' approche data.table pour une jointure interne, qui est très efficace en temps et en mémoire (et nécessaire pour certains data.frames plus grands):
merge
fonctionne également sur data.tables (car il est générique et appellemerge.data.table
)data.table documenté sur stackoverflow:
comment effectuer une opération de fusion data.table
Traduire des jointures SQL sur des clés étrangères en R syntaxe data.table Alternatives
efficaces à fusionner pour des data.frames plus grands R
Comment faire une jointure externe gauche de base avec data.table dans R?
Encore une autre option est la
join
fonction trouvée dans le paquet plyrOptions pour
type
:inner
,left
,right
,full
.From
?join
: Contrairement àmerge
, [join
] conserve l'ordre de x quel que soit le type de jointure utilisé.la source
plyr::join
. Le micro-benchmarking indique qu'il fonctionne environ 3 fois plus vite quemerge
.data.table
est beaucoup plus rapide que les deux. Il y a aussi un grand support dans SO, je ne vois pas beaucoup de rédacteurs de packages répondre aux questions ici aussi souvent que ledata.table
rédacteur ou les contributeurs.data.table
syntaxe pour fusionner une liste de trames de données ?nomatch = 0L
dans ce cas.Vous pouvez également faire des jointures en utilisant le formidable package dplyr de Hadley Wickham .
Jointures mutantes: ajoutez des colonnes à df1 en utilisant des correspondances dans df2
Filtrage des jointures: filtrez les lignes dans df1, ne modifiez pas les colonnes
la source
CustomerId
en numérique? Je ne vois aucune mention dans la documentation (pourplyr
et pourdplyr
) de ce type de restriction. Votre code fonctionnerait-il incorrectement, si la colonne de fusion était decharacter
type (particulièrement intéressé parplyr
)? Suis-je en train de manquer quelque chose?Il y a quelques bons exemples de cela sur le R Wiki . Je vais voler un couple ici:
Méthode de fusion
Étant donné que vos clés sont nommées de la même manière, la méthode la plus courte pour effectuer une jointure interne est merge ():
une jointure interne complète (tous les enregistrements des deux tables) peut être créée avec le mot clé "all":
une jointure externe gauche de df1 et df2:
une jointure externe droite de df1 et df2:
vous pouvez les retourner, les gifler et les frotter pour obtenir les deux autres jointures externes dont vous avez parlé :)
Méthode d'indice
Une jointure externe gauche avec df1 à gauche en utilisant une méthode en indice serait:
L'autre combinaison de jointures externes peut être créée en neutralisant l'exemple d'indice de jointure externe gauche. (ouais, je sais que c'est l'équivalent de dire "je vais le laisser comme un exercice pour le lecteur ...")
la source
Nouveau en 2014:
Surtout si vous êtes également intéressé par la manipulation de données en général (y compris le tri, le filtrage, le sous-ensemble, le résumé, etc.), vous devriez certainement y jeter un œil
dplyr
, qui comprend une variété de fonctions conçues pour faciliter votre travail en particulier avec les blocs de données et certains autres types de bases de données. Il offre même une interface SQL assez élaborée, et même une fonction pour convertir (la plupart) du code SQL directement en R.Les quatre fonctions liées à la jonction dans le package dplyr sont (pour citer):
inner_join(x, y, by = NULL, copy = FALSE, ...)
: retourne toutes les lignes de x où il y a des valeurs correspondantes dans y, et toutes les colonnes de x et yleft_join(x, y, by = NULL, copy = FALSE, ...)
: retourne toutes les lignes de x et toutes les colonnes de x et ysemi_join(x, y, by = NULL, copy = FALSE, ...)
: retourne toutes les lignes de x où il y a des valeurs correspondantes dans y, en gardant seulement les colonnes de x.anti_join(x, y, by = NULL, copy = FALSE, ...)
: retourne toutes les lignes de x où il n'y a pas de valeurs correspondantes dans y, en gardant seulement les colonnes de xTout est ici en détail.
La sélection des colonnes peut être effectuée par
select(df,"column")
. Si cela ne vous suffit pas, il y a lasql()
fonction, dans laquelle vous pouvez entrer le code SQL tel quel, et il fera l'opération que vous avez spécifiée comme si vous écriviez en R depuis le début (pour plus d'informations, veuillez vous référer à la vignette dplyr / bases de données ). Par exemple, si appliqué correctement,sql("SELECT * FROM hflights")
sélectionnera toutes les colonnes de la table dplyr "hflights" (un "tbl").la source
Mise à jour des méthodes data.table pour joindre des ensembles de données. Voir ci-dessous des exemples pour chaque type de jointure. Il existe deux méthodes, l'une à partir du
[.data.table
moment où le deuxième data.table est passé comme premier argument du sous-ensemble, une autre façon consiste à utiliser unemerge
fonction qui distribue la méthode data.table rapide.Ci-dessous les tests de référence de base R, sqldf, dplyr et data.table.
Benchmark teste des ensembles de données non clés / non indexés. Le benchmark est effectué sur des ensembles de données de 50 millions de lignes, il y a 50 millions de valeurs communes sur la colonne de jointure, de sorte que chaque scénario (interne, gauche, droit, complet) peut être testé et la jointure n'est toujours pas triviale à effectuer. C'est le type de jointure qui met bien l'accent sur les algorithmes de jointure. Timings sont en date du
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Sachez qu'il existe d'autres types de jointures que vous pouvez effectuer à l'aide de
data.table
:- mise à jour lors de la jointure - si vous souhaitez rechercher des valeurs d'une autre table dans votre table principale
- agréger lors de la jointure - si vous souhaitez agréger sur la clé que vous rejoignez, vous n'avez pas à se matérialiser tous les résultats de jointure
- chevauchement rejoindre - si vous souhaitez fusionner par des plages
- roulement rejoindre - si vous voulez fusionner pour pouvoir correspondre aux valeurs de précédant / suivant les lignes en les faisant rouler vers l' avant ou vers l' arrière
- non équi rejoindre - si votre la condition de jointure n'est pas égale
Code à reproduire:
la source
on =
?on
argmerge.data.table
l'sort = TRUE
argument par défaut , qui ajoute une clé pendant la fusion et la laisse là dans le résultat. C'est quelque chose à surveiller, surtout si vous essayez d'éviter de définir des clés.data.table
, que voulez-vous dire? Pouvez-vous être plus précis s'il vous plaît.dplyr depuis la version 0.4 a implémenté toutes ces jointures, y compris
outer_join
, mais il était intéressant de noter que pour les premières versions antérieures à la version 0.4, il ne proposait pasouter_join
, et en conséquence, il y avait beaucoup de code utilisateur de contournement hacky qui circulait pendant un bon moment après (vous pouvez toujours trouver un tel code dans SO, Kaggle répond, github de cette période. Par conséquent, cette réponse sert toujours un but utile.)Points saillants des versions liées aux jointures :
v0.5 (6/2016)
v0.4.0 ( 1/2015 )
v0.3 (10/2014)
v0.2 (5/2014)
v0.1.3 (4/2014)
Solutions de contournement par les commentaires de hadley dans ce numéro:
la source
dplyr
syntaxe, le passage delazyeval
àrlang
backends a cassé un tas de code pour moi, ce qui m'a poussé à en savoir plusdata.table
, et maintenant j'utilise principalementdata.table
.)plyr
/dplyr
/data.table
/ tidyverse dépend énormément de l'année de début et de l'état (embryonnaire) dans lequel les colis étaient à l'époque, par opposition à maintenant ...En joignant deux trames de données avec ~ 1 million de lignes chacune, une avec 2 colonnes et l'autre avec ~ 20, j'ai étonnamment trouvé que
merge(..., all.x = TRUE, all.y = TRUE)
c'était plus rapide alorsdplyr::full_join()
. C'est avec dplyr v0.4La fusion prend ~ 17 secondes, full_join prend ~ 65 secondes.
Un peu de nourriture pour moi, car je passe généralement par défaut à dplyr pour les tâches de manipulation.
la source
Dans le cas d'une jointure gauche avec une
0..*:0..1
cardinalité ou d'une jointure droite avec une0..1:0..*
cardinalité, il est possible d'affecter en place les colonnes unilatérales du jointeur (la0..1
table) directement sur la joinee (la0..*
table), et ainsi d'éviter la création de une toute nouvelle table de données. Cela nécessite de faire correspondre les colonnes clés du joinee dans le menuisier et d'indexer + de classer les lignes du menuisier en conséquence pour l'affectation.Si la clé est une seule colonne, nous pouvons utiliser un seul appel
match()
pour effectuer la correspondance. C'est le cas que je couvrirai dans cette réponse.Voici un exemple basé sur l'OP, sauf que j'ai ajouté une ligne supplémentaire à
df2
avec un identifiant de 7 pour tester la casse d'une clé non correspondante dans la jointure. Ceci est effectivementdf1
laissé jointdf2
:Dans ce qui précède, j'ai codé en dur une hypothèse selon laquelle la colonne clé est la première colonne des deux tables d'entrée. Je dirais que, en général, ce n'est pas une hypothèse déraisonnable, car, si vous avez un data.frame avec une colonne clé, il serait étrange qu'il n'ait pas été configuré comme la première colonne du data.frame de Le début. Et vous pouvez toujours réorganiser les colonnes pour qu'il en soit ainsi. Une conséquence avantageuse de cette hypothèse est que le nom de la colonne clé ne doit pas être codé en dur, bien que je suppose que cela remplace simplement une hypothèse par une autre. La concision est un autre avantage de l'indexation entière, ainsi que la vitesse. Dans les benchmarks ci-dessous, je changerai l'implémentation pour utiliser l'indexation des noms de chaîne pour correspondre aux implémentations concurrentes.
Je pense que c'est une solution particulièrement appropriée si vous avez plusieurs tables que vous souhaitez laisser joint contre une seule grande table. La reconstruction répétée de la table entière pour chaque fusion serait inutile et inefficace.
D'un autre côté, si vous avez besoin que le joinee reste inchangé pendant cette opération pour une raison quelconque, alors cette solution ne peut pas être utilisée, car elle modifie directement le joinee. Bien que dans ce cas, vous pouvez simplement faire une copie et effectuer les affectations sur place sur la copie.
En remarque, j'ai brièvement examiné les solutions de correspondance possibles pour les clés multicolonnes. Malheureusement, les seules solutions correspondantes que j'ai trouvées étaient:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
, ou la même idée avecpaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
et les fonctions de fusion basées sur des packages équivalentes, qui allouent toujours une nouvelle table pour renvoyer le résultat fusionné, et ne conviennent donc pas à une solution basée sur des affectations sur place.Par exemple, voir Correspondance de plusieurs colonnes sur différents blocs de données et obtention d'une autre colonne comme résultat , correspondance de deux colonnes avec deux autres colonnes , Correspondance sur plusieurs colonnes et dupe de cette question à l'origine de la solution sur place, Combiner deux trames de données avec un nombre différent de lignes de R .
Analyse comparative
J'ai décidé de faire mon propre benchmarking pour voir comment l'approche d'affectation sur place se compare aux autres solutions qui ont été proposées dans cette question.
Code de test:
Voici une référence de l'exemple basé sur l'OP que j'ai démontré plus tôt:
Ici, je compare les données d'entrée aléatoires, en essayant différentes échelles et différents modèles de chevauchement des touches entre les deux tables d'entrée. Cette référence est toujours limitée au cas d'une clé entière à une seule colonne. De plus, pour garantir que la solution en place fonctionnerait pour les jointures gauche et droite des mêmes tables, toutes les données de test aléatoires utilisent la
0..1:0..1
cardinalité. Ceci est mis en œuvre en échantillonnant sans remplacement la colonne clé du premier data.frame lors de la génération de la colonne clé du second data.frame.J'ai écrit du code pour créer des graphiques de journal des journaux des résultats ci-dessus. J'ai généré un tracé séparé pour chaque pourcentage de chevauchement. C'est un peu encombré, mais j'aime avoir tous les types de solution et les types de jointure représentés dans le même tracé.
J'ai utilisé l'interpolation spline pour montrer une courbe lisse pour chaque combinaison de type solution / jointure, dessinée avec des symboles pch individuels. Le type de jointure est capturé par le symbole pch, en utilisant un point pour les parenthèses intérieures, gauche et droite pour la gauche et la droite, et un diamant pour le plein. Le type de solution est capturé par la couleur comme indiqué dans la légende.
Voici un deuxième benchmark à grande échelle qui est plus robuste, en ce qui concerne le nombre et les types de colonnes clés, ainsi que la cardinalité. Pour cette référence, j'utilise trois colonnes clés: un caractère, un entier et une logique, sans aucune restriction sur la cardinalité (c'est-à-dire
0..*:0..*
). (En général, il n'est pas conseillé de définir des colonnes clés avec des valeurs doubles ou complexes en raison de complications de comparaison à virgule flottante, et fondamentalement, personne n'utilise jamais le type brut, encore moins pour les colonnes clés, donc je n'ai pas inclus ces types dans la clé Aussi, à titre d'information, j'ai d'abord essayé d'utiliser quatre colonnes clés en incluant une colonne clé POSIXct, mais le type POSIXct ne fonctionnait pas bien avec lasqldf.indexed
solution pour une raison quelconque, probablement en raison d'anomalies de comparaison à virgule flottante, donc j'ai retiré.)Les tracés résultants, en utilisant le même code de tracé donné ci-dessus:
la source
merge
fonction, nous pouvons sélectionner la variable du tableau de gauche ou du tableau de droite, de la même manière que nous connaissons tous l'instruction SELECT en SQL (EX: Sélectionnez a. * ... ou Sélectionnez b. * Dans .....)Nous devons ajouter du code supplémentaire qui sera un sous-ensemble de la table nouvellement jointe.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
De la même façon
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
la source
Pour une jointure interne sur toutes les colonnes, vous pouvez également utiliser
fintersect
le paquet data.table ouintersect
le paquet dplyr comme alternative àmerge
sans spécifier lesby
colonnes. cela donnera les lignes qui sont égales entre deux trames de données:Exemples de données:
la source
Mettre à jour la jointure. Une autre jointure de style SQL importante est une " jointure de mise à jour " où les colonnes d'une table sont mises à jour (ou créées) à l'aide d'une autre table.
Modification des exemples de tables OP ...
Supposons que nous voulons ajouter l'état du client de
cust
à la table des achatssales
, en ignorant la colonne année. Avec la base R, nous pouvons identifier les lignes correspondantes, puis copier les valeurs sur:Comme vous pouvez le voir ici,
match
sélectionne la première ligne correspondante dans la table des clients.Mettre à jour la jointure avec plusieurs colonnes. L'approche ci-dessus fonctionne bien lorsque nous nous joignons à une seule colonne et sommes satisfaits de la première correspondance. Supposons que nous voulons que l'année de mesure dans la table client corresponde à l'année de vente.
Comme le mentionne la réponse de @ bgoldst,
match
avecinteraction
pourrait être une option pour ce cas. Plus simplement, on pourrait utiliser data.table:Rolling update join. Alternativement, nous pouvons vouloir prendre le dernier état dans lequel le client a été trouvé:
Les trois exemples ci-dessus se concentrent tous sur la création / l'ajout d'une nouvelle colonne. Voir la FAQ R associée pour un exemple de mise à jour / modification d'une colonne existante.
la source