Je dois vérifier les observations d'oiseaux faites sur une période plus longue pour les entrées en double / qui se chevauchent.
Des observateurs de différents points (A, B, C) ont fait des observations et les ont marquées sur des cartes papier. Ces lignes ont été introduites dans une ligne avec des données supplémentaires pour l'espèce, le point d'observation et les intervalles de temps où elles ont été vues.
Normalement, les observateurs communiquent entre eux par téléphone pendant l'observation, mais parfois ils oublient, donc je reçois ces lignes en double.
J'ai déjà réduit les données aux lignes qui touchent le cercle, donc je n'ai pas à faire d'analyse spatiale, mais je compare seulement les intervalles de temps pour chaque espèce et je peux être sûr que c'est le même individu que l'on trouve par la comparaison .
Je cherche maintenant un moyen dans R d'identifier les entrées qui:
- sont faites le même jour avec un intervalle qui se chevauchent
- et où c'est la même espèce
- et qui ont été faites à partir de différents points d'observation (A ou B ou C ou ...))
Dans cet exemple, j'ai trouvé manuellement des entrées éventuellement dupliquées de la même personne. Le point d'observation est différent (A <-> B), l'espèce est la même (Sst) et l'intervalle des heures de début et de fin se chevauche.
Je créerais maintenant un nouveau champ "dupliquer" dans mon data.frame, donnant aux deux lignes un identifiant commun pour pouvoir les exporter et décider plus tard quoi faire.
J'ai cherché beaucoup de solutions déjà disponibles, mais je n'ai trouvé aucune préoccupation concernant le fait que je dois sous-définir le processus pour l'espèce (de préférence sans boucle) et comparer les lignes pour 2 + x points d'observation.
Quelques données avec lesquelles jouer:
testdata <- structure(list(bird_id = c("20150712_0810_1410_A_1", "20150712_0810_1410_A_2",
"20150712_0810_1410_A_4", "20150712_0810_1410_A_7", "20150727_1115_1430_C_1",
"20150727_1120_1430_B_1", "20150727_1120_1430_B_2", "20150727_1120_1430_B_3",
"20150727_1120_1430_B_4", "20150727_1120_1430_B_5", "20150727_1130_1430_A_2",
"20150727_1130_1430_A_4", "20150727_1130_1430_A_5", "20150812_0900_1225_B_3",
"20150812_0900_1225_B_6", "20150812_0900_1225_B_7", "20150812_0907_1208_A_2",
"20150812_0907_1208_A_3", "20150812_0907_1208_A_5", "20150812_0907_1208_A_6"
), obsPoint = c("A", "A", "A", "A", "C", "B", "B", "B", "B",
"B", "A", "A", "A", "B", "B", "B", "A", "A", "A", "A"), species = structure(c(11L,
11L, 11L, 11L, 10L, 11L, 10L, 11L, 11L, 11L, 11L, 10L, 11L, 11L,
11L, 11L, 11L, 11L, 11L, 11L), .Label = c("Bf", "Fia", "Grr",
"Kch", "Ko", "Lm", "Rm", "Row", "Sea", "Sst", "Wsb"), class = "factor"),
from = structure(c(1436687150, 1436689710, 1436691420, 1436694850,
1437992160, 1437991500, 1437995580, 1437992360, 1437995960,
1437998360, 1437992100, 1437994000, 1437995340, 1439366410,
1439369600, 1439374980, 1439367240, 1439367540, 1439369760,
1439370720), class = c("POSIXct", "POSIXt"), tzone = ""),
to = structure(c(1436687690, 1436690230, 1436691690, 1436694970,
1437992320, 1437992200, 1437995600, 1437992400, 1437996070,
1437998750, 1437992230, 1437994220, 1437996780, 1439366570,
1439370070, 1439375070, 1439367410, 1439367820, 1439369930,
1439370830), class = c("POSIXct", "POSIXt"), tzone = "")), .Names = c("bird_id",
"obsPoint", "species", "from", "to"), row.names = c("20150712_0810_1410_A_1",
"20150712_0810_1410_A_2", "20150712_0810_1410_A_4", "20150712_0810_1410_A_7",
"20150727_1115_1430_C_1", "20150727_1120_1430_B_1", "20150727_1120_1430_B_2",
"20150727_1120_1430_B_3", "20150727_1120_1430_B_4", "20150727_1120_1430_B_5",
"20150727_1130_1430_A_2", "20150727_1130_1430_A_4", "20150727_1130_1430_A_5",
"20150812_0900_1225_B_3", "20150812_0900_1225_B_6", "20150812_0900_1225_B_7",
"20150812_0907_1208_A_2", "20150812_0907_1208_A_3", "20150812_0907_1208_A_5",
"20150812_0907_1208_A_6"), class = "data.frame")
J'ai trouvé une solution partielle avec les foverlaps de la fonction data.table mentionnés par exemple ici https://stackoverflow.com/q/25815032
library(data.table)
#Subsetting the data for each observation point and converting them into data.tables
A <- setDT(testdata[testdata$obsPoint=="A",])
B <- setDT(testdata[testdata$obsPoint=="B",])
C <- setDT(testdata[testdata$obsPoint=="C",])
#Set a key for these subsets (whatever key exactly means. Don't care as long as it works ;) )
setkey(A,species,from,to)
setkey(B,species,from,to)
setkey(C,species,from,to)
#Generate the match results for each obsPoint/species combination with an overlapping interval
matchesAB <- foverlaps(A,B,type="within",nomatch=0L) #nomatch=0L -> remove NA
matchesAC <- foverlaps(A,C,type="within",nomatch=0L)
matchesBC <- foverlaps(B,C,type="within",nomatch=0L)
Bien sûr, cela "fonctionne" en quelque sorte, mais ce n'est vraiment pas ce que j'aime à la fin.
Tout d'abord, je dois coder en dur les points d'observation. Je préférerais trouver une solution prenant un nombre arbitraire de points.
Deuxièmement, le résultat n'est pas dans un format avec lequel je peux vraiment recommencer à travailler facilement. Les lignes correspondantes sont en fait placées dans la même ligne, alors que mon objectif est de placer les lignes en dessous, et dans une nouvelle colonne, elles auraient un identifiant commun.
Troisièmement, je dois vérifier à nouveau manuellement, si un intervalle chevauche les trois points (ce qui n'est pas le cas avec mes données, mais le pourrait généralement)
En fin de compte, je voudrais simplement recevoir un nouveau data.frame avec tous les candidats identifiables par un identifiant de groupe que je peux joindre aux lignes et exporter le résultat sous forme de couche pour un examen plus approfondi.
Alors, quelqu'un a-t-il d'autres idées sur la façon de procéder?
la source
for
boucles!Réponses:
Comme certains commentateurs l'ont fait allusion, SQL est une bonne option pour exprimer des ensembles de contraintes plutôt compliqués. Le package sqldf facilite l'utilisation de la puissance de SQL dans R sans avoir à configurer vous-même une base de données relationnelle.
Voici une solution utilisant SQL. Avant de courir, j'ai dû renommer les colonnes d'intervalle de vos données en
startTime
etendTime
parce que le nomfrom
est réservé en SQL.Pour faciliter la compréhension, la réponse SQL
dupes_wide
finit par ressembler à ceci:Auto-jointure
FROM testdata x JOIN testdata y
: la recherche de paires de lignes à partir d'un seul ensemble de données est une auto-jointure. Nous devons comparer chaque ligne avec toutes les autres. L'ON
expression répertorie les contraintes de conservation des paires.Intervalle de chevauchement : je suis presque sûr que la définition du chevauchement que j'ai utilisée dans ce SQL ( source ) diffère de ce
foverlaps
que vous faisiez. Vous avez utilisé le type "dedans", qui exige que l'observation au plus tôtobsPoint
soit entièrement dans l'observation au plus tardobsPoint
(mais il manque l'inverse, par exemple si l'observation de C est entièrement dans les B ). Heureusement, c'est facile en SQL si vous devez encoder une définition différente du chevauchement.Différents points : Votre contrainte que des doublons ont été faits à partir de différents points d'observation serait vraiment exprimée
(x.obsPoint <> y.obsPoint)
. Si j'avais tapé cela, SQL retournerait chaque paire dupliquée deux fois, juste avec l'ordre de basculement des oiseaux dans chaque ligne. Au lieu de cela, j'ai utilisé un<
pour ne conserver que la moitié unique des lignes. (Ce n'est pas la seule façon de le faire)ID de doublon unique : comme pour votre solution précédente, le SQL lui-même répertorie les doublons sur la même ligne.
hex(randomblob(16))
est un moyen hacky ( mais recommandé ) dans SQLite pour générer des ID uniques pour chaque paire.Format de sortie : vous n'aimiez pas les doublons dans la même ligne, alors
melt
divisez-les etmerge
attribuez les ID en double à votre bloc de données initial.Limitations : ma solution ne gère pas le cas où le même oiseau est capturé sur plus de deux pistes . C'est plus délicat et quelque peu mal défini. Par exemple, si leurs plages de temps ressemblent à
alors Bird1 est un doublon de Bird2 , qui est un doublon de Bird3 , mais les doublons Bird1 et Bird3 sont - ils ?
la source