5
votes

Comment regrouper des lignes dans une plage et envisager une 3ème colonne?

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           
  • Gene3 et Gene4 ne sont pas dans le groupe 1 car ils sont sur un 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.
  • Gene5 n'est pas dans le même groupe que Gene1 car, bien qu'ils correspondent au 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.


5 commentaires

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.


4 Réponses :


2
votes

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"))


0 commentaires

2
votes

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)
]


0 commentaires

3
votes

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


0 commentaires

3
votes

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


Éditer

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 =....] .


0 commentaires