J'ai un ensemble de données génétiques dans lequel je souhaite regrouper des variantes / lignes génétiques physiquement proches les unes des autres dans le génome. Je souhaite regrouper les gènes qui se situent dans des limites allant de certains endroits du génome par chromosome ( chrom
).
Mon ensemble de données `` spots '' est composé de positions dont les variantes / lignes doivent se trouver dans une plage et ressemble à:
df1 <- structure(list(chrom = c(1L, 1L, 5L), low = c(500L, 19500L, 400L), high = c(1700L, 20600L, 1500L )), row.names = c(NA, -3L), class = c("data.table", "data.frame")) df2 <- structure(list(Gene = c("Gene1", "Gene2", "Gene3", "Gene4", "Gene5" ), chrom = c(1L, 1L, 5L, 5L, 1L), position = c(1200L, 10000L, 500L, 560L, 20100L)), row.names = c(NA, -5L), class = c("data.table", "data.frame"))
Mes colonnes low
et high
sont les plages dans lesquelles je veux voir si des lignes de mon prochain ensemble de données tombent, en tenant également compte du fait que le chromosome ( chrom
) doit également correspondre. Chaque ligne avec une combinaison unique de plage et de chrominance est son propre groupe dans lequel je cherche à voir si quelque chose dans mon autre ensemble de données tombe.
Mon autre ensemble de données a une valeur de position que je cherche à voir si elle correspond à l'une des plages ci-dessus avec le chrom
correspondant, afin de l'étiqueter comme correspondant à cette plage, puis je peux regrouper les positions dans la même plage et chromer ensemble :
Gene chrom position Group Gene1 1 1200 1 #position is in one of the ranges and matches the chrom so is in a group Gene2 1 10000 NA #does not fit into any range on chrom 2 (no matches) Gene3 5 500 2 #position is in one of the ranges and matches the chrom so is in a group Gene4 5 560 2 #position is in the same range and chrom as above so joins that group Gene5 1 20100 3 #position matches a chrom and range and so gets a group corresponding to that particular chrom and range
J'ai essayé d'utiliser group_by()
et between()
pour configurer la plage, depuis que j'ai vu d'autres questions similaires pour les plages de dates / heures, mais j'ai du mal à expliquer la nécessité de faire correspondre le chromosome ( chrom
) entre les ensembles de données avant de rechercher la plage.
La sortie ressemblerait à:
Gene chrom position Gene1 1 1200 Gene2 1 10000 Gene3 5 500 Gene4 5 560 Gene5 1 20100
chrom
différent, mais ils correspondent au chrom et sont à portée de la 3ème ligne de mon premier ensemble de données - ils peuvent donc être dans le groupe qui correspond à cette plage et chrom.chrom
ils sont dans différentes gammes de low
et de high
, alors obtenez leurs propres groupes pour les gammes uniques. Je crée donc une colonne Group
avec un numéro partagé pour toutes les lignes de la même plage entre low
et high
sur le même chrom
, ou NA si leur position ne correspond à aucune plage et chrom dans le premier ensemble de données.
Des données d'entrée:
chrom low high 1 500 1700 1 19500 20600 5 400 1500
Je cherche également à donner à mon premier ensemble de données des identifiants uniques pour chaque plage unique et combinaison de chrom, puis à attribuer cet identifiant à n'importe quelle ligne de l'ensemble de données 2 qui correspond également à la combinaison, de sorte que l'identifiant crée ma colonne de numéros de groupe. Bien que mes données réelles soient 2,3k lignes de plages et 82k lignes à faire correspondre dans des groupes partagés, j'ai également des problèmes pour exécuter les options de dplyr que j'essaierais normalement.
4 Réponses :
Comme indiqué dans les commentaires, il vous suffit d'utiliser findOverlaps
de GenomicRanges
pour trouver les lignes de votre dataframe de référence qui englobent vos lignes dans le deuxième data.frame
Votre df2 est un peu différent de celui montré dans l'exemple, donc je le change pour qu'il corresponde:
gr2 = makeGRangesFromDataFrame(df2,start.field="position",end.field="position") ovlp = as.data.frame(findOverlaps(gr2,gr1)) df2$Group = ovlp$subjectHits[match(1:length(gr2),ovlp$queryHits)] Gene chrom position Group 1 Gene1 1 1200 1 2 Gene2 1 10000 NA 3 Gene3 5 500 3 4 Gene4 1 560 1 5 Gene5 1 20100 2
Et votre df1 a un ordre différent:
library(GenomicRanges) gr1 = makeGRangesFromDataFrame(df1,start.field="low",end.field="high") gr1$Group = 1:length(gr1) GRanges object with 3 ranges and 1 metadata column: seqnames ranges strand | Group <Rle> <IRanges> <Rle> | <integer> [1] 1 500-1700 * | 1 [2] 1 19500-20600 * | 2 [3] 5 400-1500 * | 3
Nous pouvons créer un objet GenomicRanges comme ci-dessous:
chrom min max low high 1 1 1000 1200 500 1700 2 1 20000 20100 19500 20600 3 5 900 1000 400 1500
Ensuite, faites de même pour la deuxième trame de données et trouvez le chevauchement:
df2 = structure(list(Gene = c("Gene1", "Gene2", "Gene3", "Gene4", "Gene5" ), chrom = c(1L, 1L, 5L, 5L, 1L), position = c(1200L, 10000L, 500L, 560L, 20100L)), row.names = c(NA, -5L), class = c("data.table", "data.frame"))
Voici une solution data.table
. Nous pouvons utiliser la fonction foverlaps
introduite dans ce post cité par Ronak.
> res Gene chrom position Group 1: Gene1 1 1200 1 2: Gene2 1 10000 NA 3: Gene3 5 500 3 4: Gene4 1 560 1 5: Gene5 1 20100 2
Production
library(data.table) setDT(df1, key = c("chrom", "low", "high"))[ , c("min", "max", "Group") := .(NULL, NULL, .I) ] setDT(df2)[, position2 := position] res <- foverlaps( df2, df1, by.x = c("chrom", "position", "position2"), type = "within" )[ , .(Gene, chrom, position, Group) ]
Si vous connaissez sql
cela peut être rapidement résolu en sql + R:
df1$group_id <- seq(nrow(df1)) #This creates the unique groups for each interval sqldf::sqldf(' SELECT df2.*, df1.group_id FROM df2 LEFT JOIN df1 ON df2.chrom = df1.chrom AND position between low AND high') Gene chrom position group_id 1 Gene1 1 1200 1 2 Gene2 1 10000 NA 3 Gene3 5 500 3 4 Gene4 5 560 3 5 Gene5 1 20100 2
Vous pouvez utiliser une jointure non équi dans data.table
:
df2[, grp := df1[.SD, which = TRUE, on = .(chrom, low <= position, high >= position)]]
Ici, j'ai d'abord défini un groupe pour chaque ligne de df1
. Après la fusion, la ligne est associée à un groupe si la condition est remplie.
Les fusions non équi ne sont pas super intuitives, mais super puissantes et explicites: la condition de fusion .(chrom, low < position, high > position)
est littéralement ce que vous avez expliqué (vous voulez le même chromosome et la position entre bas et haut).
Dans data.table
, quand vous faites
df2[df1,on = .(chrom,position >low , position<high)] Gene chrom position position.1 group 1: Gene1 1 500 1700 1 2: Gene5 1 19500 20600 2 3: Gene3 5 400 1500 3 4: Gene4 5 400 1500 3
vous sous-ensemble df1
avec les lignes de df2
remplissant la condition exprimée par on
. Si something
est juste une variable commune de df1
et df2
, alors c'est équivalent à
merge(df1,df2,all.y = T,by = "someting")
Mais something
peut être une liste de variables et de conditions entre les variables de vos deux data.tables. Ici,. .()
Indique une liste, et .(chrom,low < position, high > position)
indique que vous fusionnez sur la variable chrom
(identique entre les deux data.tables) et low < position
et high > position
. Lorsque vous exprimez l'inégalité, vous devez commencer par la variable de la table data.table principale ( df1
ici), puis les variables du sous-ensemble data.table ( df2
).
La sortie de cette fusion non équi en utilisant des inégalités remplace la variable exprimée en inégalités de la table de données principale (c'est-à-dire df1
) par les variables du sous-ensemble data.table (c'est-à-dire df2
ici), et donc low
et high
deviennent position
. Si vous souhaitez conserver les valeurs low
et high
, vous devez les copier dans une autre variable ou les fusionner sur une copie de ces variables.
Vous pouvez en fait faire la fusion inverse, nous allons sous-ensemble df2
par les entrées df1
, avec la même condition:
df1[df2,on = something]
Ici, vous sous-ensemble df1
avec les entrées de df2
remplissant les conditions exprimées dans on = .()
, Et obtenez la liste des Gene
qui appartiennent réellement à un groupe ( Gene2
n'est pas ici car il ne correspond pas au sous-ensemble).
De manière similaire à ce qui a été expliqué ci-dessus, ici la position
devient low
et high
Je viens de voir le commentaire de @DavidArenburg, et c'est une version plus condensée et meilleure de ce que j'ai proposé et expliqué:
library(data.table) df1 <- setDT(df1) df2 <- setDT(df2) df1[,group := 1:.N] df1[df2,on = .(chrom, low < position, high > position)] chrom low high group Gene 1: 1 1200 1200 1 Gene1 2: 1 10000 10000 NA Gene2 3: 5 500 500 3 Gene3 4: 5 560 560 3 Gene4 5: 1 20100 20100 2 Gene5
associer directement le résultat de la fusion non équi df1[df2,on = .(chrom, low < position, high > position)]
à la variable de groupe, en utilisant which = TRUE
, qui vous donne la ligne de df2
qui remplit la condition de fusion de df1[df2 , on =....]
.
Cet article pourrait aider - stackoverflow.com/questions/24480031/...
Merci, je vais examiner l'utilisation des chevauchements
J'ai mis à jour votre question pour faire correspondre l'ensemble de données fourni à ce que vous affichez réellement. en ce qui concerne la question elle-même, je ferais simplement
df2[, grp := df1[.SD, which = TRUE, on = .(chrom, low <= position, high >= position)]]
(Si vous ne vous souciez pas vraiment à propos de l'ordre des groupes)Comment n'est-ce pas un doublon d'un article lié par Ronak: stackoverflow.com/questions/24480031/...
J'essaie également de regrouper les correspondances et d'essayer de considérer le code qui fonctionnera sur mes données réelles plus volumineuses. L'option de chevauchements ne fonctionne pas pour mes données réelles, soit je reçois des erreurs, soit elle s'exécute indéfiniment pour moi.