137 votes

Pourquoi X [Y] join de data.tables ne permettent pas une jointure externe complète, ou une jointure gauche ?

C'est un peu une question philosophique sur les données.le tableau de la syntaxe de jointure. Je viens de trouver de plus en plus de données.tables, mais encore à apprendre...

La jointure format X[Y] pour les données.les tables est très concis, pratique et efficace, mais aussi loin que je peux dire, il prend uniquement en charge les jointures internes et en droit des jointures externes. Pour obtenir de gauche ou de jointure externe complète, j'ai besoin d'utiliser de fusion:

  • X[Y,nomatch=NA] -- toutes les lignes Y -- jointure externe droite (par défaut)
  • X[Y,nomatch=0] -- seules les lignes avec des allumettes dans les directions X et Y -- jointure interne
  • merge(X,Y,all=TRUE) -- toutes les lignes de X et de Y -- jointure externe complète
  • merge(X,Y,all.x=TRUE) -- toutes les lignes de X -- jointure externe gauche

Il me semble qu'il serait pratique si X[Y] joindre format pris en charge tous les 4 types de jointures. Est-il une raison que deux types de jointures sont pris en charge?

Pour moi, le "nomatch=0" et "nomatch=NA" les valeurs de paramètre ne sont pas très intuitif pour les actions effectuées. Il est plus facile pour moi de comprendre et de mémoriser la fusion de la syntaxe: "tous les=TRUE", "tous les.x=TRUE" et "tous les.y=TRUE". Depuis le X[Y] opération ressemble de fusion beaucoup plus de match, pourquoi ne pas utiliser la fusion de la syntaxe pour les jointures plutôt que le match de la fonction de nomatch paramètre?

Voici des exemples de code de la 4 types de jointure:

# sample X and Y data.tables
library(data.table)
X <- data.table(t=1:4,a=(1:4)^2)
setkey(X,t)
X
#   t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16
Y <- data.table(t=3:6,b=(3:6)^2)
setkey(Y,t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
X[Y,nomatch=NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
merge(X,Y,by="t",all.y=TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
identical( X[Y], merge(X,Y,by="t",all.y=TRUE) )
# [1] TRUE

# only rows in both X and Y - inner join
X[Y,nomatch=0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
merge(X,Y,by="t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
merge(X,Y,by="t",all=FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
identical( X[Y,nomatch=0], merge(X,Y,by="t",all=FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X,Y,by="t",all.x=TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X,Y,by="t",all=TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

6 votes

Avez-vous lu FAQ 1.12 ? Vous pouvez toujours appeler Y[X] si vous voulez que le jointure externe gauche de X[Y] y rbind(Y[X],X[Y]) si vous voulez une jointure externe complète

0 votes

Voir ma réponse pour une approche plus data.table de la jointure externe complète.

0 votes

@mnel, je suppose que votre unique() l'approche ci-dessous pour la jointure complète est préférable à rbind(Y[X],X[Y]) puisque le rbind impliquerait de copier la table. C'est bien cela ?

78voto

mnel Points 48160

Pour citer les données.tableau 1.12 FAQ

1.12

Quelle est la diérence entre X[Y] et de fusion(X,Y)?

  • X[Y] est une jointure, la recherche de X lignes à l'aide de Y (ou Y touche, si elle en a un) comme un indice.
  • Y[X] est une jointure, la recherche Y est lignes à l'aide de X (ou X est la clé s'il en a un)
  • merge(X,Y) ne les deux sens en même temps.

Le nombre de lignes de X[Y] et Y[X] diffèrent généralement; considérant que le nombre de les lignes retournées par merge(X,Y) et merge(Y,X) est le même. MAIS que manque le point principal. La plupart des tâches besoin de quelque chose à faire sur le données après une jointure ou de fusion. Pourquoi fusionner toutes les colonnes de données, seulement à utilisez un petit sous-ensemble de la suite? Vous pouvez suggérer merge(X[,ColsNeeded1],Y[,ColsNeeded2]), mais qui prend des copies de la les sous - ensembles de données, et elle oblige le programmeur à travailler sur la les colonnes sont nécessaires. X[Y,j] dans les données.le tableau fait tout cela en une seule étape pour vous. Lorsque vous écrivez X[Y,sum(foo*bar)], données.tableau automatiquement inspecte le j expression pour voir les colonnes qu'il utilise. Il ne sous-ensemble de ces colonnes seulement; les autres sont ignorés. La mémoire est créé pour les colonnes j utilise, et Y colonnes de profiter de la norme de R règles en matière de recyclage dans le contexte de chaque groupe. Disons que toto est dans X, et le bar est Y (avec 20 autres colonnes en Y). N'est-ce pas X[Y,sum(foo*bar)] plus rapide à programmer et plus rapide à exécuter que d'une fusion suivi par un sous-ensemble?

Je vous souhaitez une jointure externe gauche de l' X[Y]

le <- Y[X]
mallx <- merge(X,Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le,mallx)
# [1] TRUE

Si vous voulez une jointure externe complète

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]

6 votes

Merci @mnel. La FAQ 1.12 ne mentionne pas la jointure externe complète ou gauche. Votre suggestion de jointure externe complète avec unique() est d'une grande aide. Elle devrait figurer dans la FAQ. Je sais que Matthew Dowle "l'a conçu pour son propre usage, et il le voulait ainsi". (FAQ 1.9), mais je pensais que X[Y,all=T] pourrait être un moyen élégant de spécifier une jointure externe complète dans la syntaxe data.table X[Y]. Ou bien X[Y,all.x=T] pour la jointure gauche. Je me demande pourquoi ça n'a pas été conçu de cette façon. C'est juste une idée.

1 votes

@DouglasClark J'ai ajouté la réponse et je l'ai classée. 2302:Ajout de la syntaxe de fusion-association de mnel à la FAQ (avec les délais) . Excellentes suggestions !

1 votes

@mnel Merci pour la solution... ça m'a fait du bien... :)

25voto

Matt Dowle Points 20936

@mnel la réponse est sur place, afin de faire accepter cette réponse. C'est juste de suivi, trop long pour les commentaires.

Comme mnel dit, gauche/droite de la jointure externe est obtenu par la permutation Y et X: Y[X] -vs- X[Y]. Si 3 des 4 types de jointure sont pris en charge dans cette syntaxe, pas 2, iiuc.

L'ajout de la 4e semble une bonne idée. Disons que nous ajoutons full=TRUE ou both=TRUE ou merge=TRUE (pas sûr que le meilleur argument de nom?) ensuite, il n'y a pas pensé à moi avant que l' X[Y,j,merge=TRUE] serait utile pour les raisons après le MAIS dans la FAQ 1.12. Nouvelle demande de fonctionnalité maintenant ajoutée et lié de retour ici, merci :

FR#2301 : Ajouter de fusion=VRAI argument pour les deux X[Y] et Y[X] jointure comme merge ().

Les versions récentes ont accéléré merge.data.table (par la prise d'une copie en interne pour définir les touches de manière plus efficace, par exemple). Si nous essayons d'apporter merge() et X[Y] de plus près, et de fournir toutes les options à l'utilisateur, pour une flexibilité totale. Il y a des avantages et des inconvénients des deux. Une autre caractéristique remarquable de demande est :

FR#2033 : Ajouter par.x et par.y à fusionner.les données.table

Si il y a tout les autres, merci de les garder à venir.

Par cette partie dans la question :

pourquoi ne pas utiliser la fusion de la syntaxe pour les jointures plutôt que le match de la fonction de nomatch paramètre?

Si vous préférez merge() de la syntaxe et de ses 3 arguments all,all.x et all.y puis il suffit de l'utiliser à la place de X[Y]. Pense qu'il devrait couvrir tous les cas. Ou avez-vous dire pourquoi l'argument est-il un seul nomatch en [.data.table? Si oui, c'est juste la manière qui semblait naturel, étant donné FAQ 2.14 : "Pouvez-vous expliquer davantage pourquoi les données.le tableau est inspiré par Un[B] la syntaxe de base?". Mais aussi, nomatch ne prend que deux valeurs actuellement 0 et NA. Qui pourrait être élargie de manière à ce qu'une valeur négative signifie quelque chose, ou 12 signifierait l'utilisation de la 12e ligne, de valeurs pour remplir NAs, par exemple, ou nomatch à l'avenir pourrait être un vecteur ou même de lui-même un data.table.

Hm. Comment par-sans-par interagir avec fusion=TRUE? Nous devrions peut-être prendre cela sur datatable-aider.

0 votes

Merci @Matthew. La réponse de @mnel est excellente, mais ma question n'était pas de savoir comment faire une jointure complète ou gauche, mais "Y a-t-il une raison pour laquelle seuls deux types de jointures sont supportés ?". Donc maintenant c'est un peu plus philosophique ;-) En fait, je ne préfère pas la syntaxe de fusion, mais il semble qu'il y ait une tradition dans R pour construire sur des choses existantes avec lesquelles les gens sont familiers. J'avais griffonné join="all", join="all.x", join="all.y" and join="x.and.y" dans la marge de mes notes. Je ne sais pas si c'est mieux.

0 votes

@DouglasClark Peut-être join comme ça, bonne idée. J'ai posté sur datatable-help alors voyons voir. Peut-être donner data.table un peu de temps pour s'installer, aussi. Est-ce que tu dois by-without-by encore par exemple, et rejoindre la portée héritée ?

0 votes

Comme indiqué dans mon commentaire ci-dessus, je suggère d'ajouter un join en , lorsque i est une table de données : X[Y,j,join=string] . Les valeurs possibles de la chaîne de caractères pour la jointure sont suggérées comme étant : 1) "all.y" et "right" -.

18voto

Douglas Clark Points 418

Cette "réponse" est une proposition de discussion: Comme indiqué dans mon commentaire, je vous suggère d'ajouter une join paramètre pour [.les données.tableau() pour activer d'autres types de jointures, c'est à dire: X[Y,j,join=string]. En plus des 4 types d'ordinaire les jointures, je suggère également à l'appui de 3 types de exclusive des jointures, et la croix de jointure.

L' join chaîne de valeurs (et alias) pour les différents types de jointure sont proposées:

  1. "all.y" et "right" -- jointure droite, les données actuelles.par défaut de la table (nomatch=NA) - tous Y les lignes avec NAs où il n'existe pas de X match;
  2. "both" et "inner" -- jointure interne (nomatch=0) - uniquement les lignes où X et Y correspondent;

  3. "all.x" et "left" -- left join - toutes les lignes de X, NAs où pas Y correspondre:

  4. "outer" et "full" -- jointure externe complète - toutes les lignes de X et de Y, NAs où aucune correspondance

  5. "only.x" et "not.y" -- non-rejoindre ou anti-rejoindre le retour de X lignes où il n'Y match

  6. "only.y" et "not.x" -- non-rejoindre ou anti-joignez-vous de retourner Y les lignes où il n'y a pas de match X
  7. "not.both" -- exclusif de rejoindre le retour de X et Y les lignes où il n'y a pas de match à l'autre table, c'est à dire un ou-exclusif (XOR)
  8. "cross" -- jointure croisée ou produit Cartésien avec chaque ligne de X appariés à chaque ligne de Y

La valeur par défaut est join="all.y" ce qui correspond à la présente par défaut.

Le "tous", "tous les.x" et "tous les.y" chaîne de valeurs correspondent à l' merge() paramètres. La "droite", "gauche", "intérieur" et "extérieur" des chaînes peut être plus propice aux utilisateurs de SQL.

La "deux" et "pas.les deux" chaînes sont ma meilleure suggestion pour le moment -- mais quelqu'un peut avoir une meilleure chaîne de suggestions pour la jointure interne et exclusif de les rejoindre. (Je ne sais pas si "exclusif" est la bonne terminologie, corrigez-moi si il y a un terme correct pour un "XOR" join).

L'utilisation d' join="not.y" est une alternative à l' X[-Y,j] ou X[!Y,j] non-syntaxe de jointure et peut-être plus clair (pour moi), même si je ne suis pas sûr si elles sont les mêmes (nouvelle fonctionnalité dans les données.tableau version 1.8.3).

La jointure croisée peut être pratique parfois, mais il ne peut pas entrer dans les données.table de paradigme.

1 votes

Veuillez l'envoyer à aide sur les tables de données pour la discussion.

3 votes

+1 Mais, s'il vous plaît soit envoyer à Aide sur les tables de données ou déposer une demande de fonctionnalité . Cela ne me dérange pas d'ajouter join mais à moins qu'il ne soit sur le tracker, il sera oublié.

1 votes

Je vois que tu ne t'es pas connecté à S.O. depuis un moment. Donc j'ai classé ça dans FR#2301

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X