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
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/
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), ]
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.
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
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
.
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.
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:
left_join
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.
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.
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"]