web-dev-qa-db-fra.com

Fusionner deux blocs de données tout en conservant l'ordre des lignes d'origine

Je souhaite fusionner deux blocs de données en conservant l'ordre des lignes d'origine de l'un d'eux (df.2 dans l'exemple ci-dessous).

Voici quelques exemples de données (toutes les valeurs de la colonne class sont définies dans les deux trames de données):

df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))
df.2 <- data.frame(object = c('A', 'B', 'D', 'F', 'C'), class = c(2, 1, 2, 3, 1))

Si je fais:

merge(df.2, df.1)

La sortie est:

  class object prob
1     1      B  0.5
2     1      C  0.5
3     2      A  0.7
4     2      D  0.7
5     3      F  0.3

Si j'ajoute sort = FALSE:

merge(df.2, df.1, sort = F)                                                        

Le résultat est:

  class object prob
1     2      A  0.7
2     2      D  0.7
3     1      B  0.5
4     1      C  0.5
5     3      F  0.3

Mais ce que je voudrais, c'est:

  class object prob
1     2      A  0.7
2     1      B  0.5
3     2      D  0.7
4     3      F  0.3    
5     1      C  0.5
51
DJack

Découvrez la fonction de jointure dans le package plyr. C'est comme la fusion, mais cela vous permet de conserver l'ordre des lignes de l'un des ensembles de données. Dans l'ensemble, c'est plus flexible que la fusion.

En utilisant vos données d'exemple, nous utiliserions join comme ceci:

> join(df.2,df.1)
Joining by: class
  object class prob
1      A     2  0.7
2      B     1  0.5
3      D     2  0.7
4      F     3  0.3
5      C     1  0.5

Voici quelques liens décrivant les correctifs de la fonction de fusion pour conserver l'ordre des lignes:

http://www.r-statistics.com/2012/01/merging-two-data-frame-objects- while-preserving-the-rows-order/

http://r.789695.n4.nabble.com/patching-merge-to-allow-the-user-to-keep-the-order-of-one-of-the-two-data- frame-objects-merged-td4296561.html

26
user2635373

Il vous suffit de créer une variable qui donne le numéro de ligne dans df.2. Ensuite, une fois que vous avez fusionné vos données, vous triez le nouvel ensemble de données en fonction de cette variable. Voici un exemple :

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
df.2$id  <- 1:nrow(df.2)
out  <- merge(df.2,df.1, by = "class")
out[order(out$id), ]
45
PAC

Depuis data.table v1.9.5 + , vous pouvez faire:

require(data.table) # v1.9.5+
setDT(df.1)[df.2, on="class"]

Le effectue une jointure sur la colonne class en recherchant les lignes correspondantes dans df.1 pour chaque ligne de df.2 et extraire les colonnes correspondantes.

11
Arun

Vous pouvez également consulter le inner_join fonction dans le package dplyr de Hadley (prochaine itération de plyr). Il préserve l'ordre des lignes du premier ensemble de données. La différence mineure avec la solution souhaitée est qu'elle préserve également l'ordre des colonnes d'origine du premier ensemble de données. Donc, cela ne met pas nécessairement la colonne que nous avons utilisée pour la fusion à la première position.

En utilisant votre exemple ci-dessus, le inner_join le résultat ressemble à ceci:

inner_join(df.2,df.1)
Joining by: "class"
  object class prob
1      A     2  0.7
2      B     1  0.5
3      D     2  0.7
4      F     3  0.3
5      C     1  0.5
8
alex23lemm

Par souci d'exhaustivité, la mise à jour dans une jointure préserve également l'ordre d'origine des lignes. Cela pourrait être une alternative à Arun's data.table réponse s'il n'y a que quelques colonnes à ajouter:

library(data.table)
setDT(df.2)[df.1, on = "class", prob := i.prob][]
   object class prob
1:      A     2  0.7
2:      B     1  0.5
3:      D     2  0.7
4:      F     3  0.3
5:      C     1  0.5

Ici, df.2 est joint à droite à df.1 et obtient une nouvelle colonne prob qui est copiée à partir des lignes correspondantes de df.1.

5
Uwe

Le réponse acceptée propose un moyen manuel de maintenir l'ordre lors de l'utilisation de merge, qui fonctionne la plupart du temps mais nécessite un travail manuel inutile. Cette solution vient sur le dos de Comment faire ddply () sans trier? , qui traite de la question du maintien de l'ordre mais dans un contexte de split-apply-combine:

Cela est apparu sur la liste de diffusion plyr il y a quelque temps (soulevé par @kohske pas moins) et c'est une solution proposée par Peter Meilstrup pour des cas limités:

#Peter's version used a function gensym to
# create the col name, but I couldn't track down
# what package it was in.
keeping.order <- function(data, fn, ...) { 
  col <- ".sortColumn"
  data[,col] <- 1:nrow(data) 
  out <- fn(data, ...) 
  if (!col %in% colnames(out)) stop("Ordering column not preserved by function") 
  out <- out[order(out[,col]),] 
  out[,col] <- NULL 
  out 
} 

Alors maintenant, vous pouvez utiliser ce générique keeping.order fonction pour conserver l'ordre des lignes d'origine d'un appel merge:

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
keeping.order(df.2, merge, y=df.1, by = "class")

Ce qui donnera, comme demandé:

> keeping.order(df.2, merge, y=df.1, by = "class")
  class object id prob
3     2      A  1  0.7
1     1      B  2  0.5
4     2      D  3  0.7
5     3      F  4  0.3
2     1      C  5  0.5

Alors keeping.order automatise efficacement l'approche dans la réponse acceptée.

3
landroni

Dans ce cas précis, vous pourriez nous factor pour une solution de base compacte:

df.2$prob = factor(df.2$class,labels=df.1$prob)

df.2
#   object class prob
# 1      A     2  0.7
# 2      B     1  0.5
# 3      D     2  0.7
# 4      F     3  0.3
# 5      C     1  0.5

Cependant, ce n'est pas une solution générale, cela fonctionne si:

  1. Vous disposez d'une table de recherche contenant des valeurs uniques
  2. Vous souhaitez mettre à jour une table, pas en créer une nouvelle
  3. la table de recherche est triée par la colonne de fusion
  4. La table de recherche n'a pas de niveaux supplémentaires
  5. Vous voulez une left_join
  6. Si vous êtes d'accord avec les facteurs

1 n'est pas négociable, pour le reste nous pouvons faire:

df.3  <- df.2 # deal with 2.
df.1b <- df.1[order(df.1$class),] # deal with 3
df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4.
df.3$prob = factor(df.3$class,labels=df.1b$prob)
df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join`
df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6.
1
Moody_Mudskipper

Grâce à @PAC, j'ai trouvé quelque chose comme ceci:

merge_sameord = function(x, y, ...) {
    UseMethod('merge_sameord')
}

merge_sameord.data.frame = function(x, y, ...) {
    rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='')
    x[, rstr] = 1:nrow(x)
    res = merge(x, y, all.x=TRUE, sort=FALSE, ...)
    res = res[order(res[, rstr]), ]
    res[, rstr] = NULL
    res
}

Cela suppose que vous souhaitez conserver l'ordre du premier bloc de données et que le bloc de données fusionné aura le même nombre de lignes que le premier bloc de données. Il vous donnera le cadre de données propre sans colonnes supplémentaires.

1
qed

Il existe plusieurs cas d'utilisation dans lesquels un sous-ensemble simple fera l'affaire:

# Use the key variable as row.names
row.names(df.1) = df.1$key

# Sort df.1 so that it's rows match df.2
df.3 = df.1[df.2$key, ]

# Create a data.frame with cariables from df.1 and (the sorted) df.2
df.4 = cbind(df.1, df.3)

Ce code préservera df.2 et son ordre et n'ajoutera que les données correspondantes de df.1

Si une seule variable doit être ajoutée, la cbind() n'est pas requise:

row.names(df.1) = df.1$key
df.2$data = df.1[df.2$key, "data"]
0
BurninLeo