Ich muss die über einen längeren Zeitraum gemachten Vogelbeobachtungen auf doppelte / überlappende Einträge überprüfen.
Beobachter von verschiedenen Punkten (A, B, C) machten Beobachtungen und markierten sie auf Papierkarten. Diese Linien wurden in eine Linie gebracht, die zusätzliche Daten für die Art, den Beobachtungspunkt und die Zeitintervalle enthält, in denen sie gesehen wurden.
Normalerweise kommunizieren die Beobachter während des Beobachtens telefonisch miteinander, aber manchmal vergessen sie es, sodass ich diese doppelten Zeilen bekomme.
Ich habe die Daten bereits auf die Linien reduziert, die den Kreis berühren, sodass ich keine räumliche Analyse durchführen muss, sondern nur die Zeitintervalle für jede Art vergleiche und ziemlich sicher sein kann, dass es sich um dasselbe Individuum handelt, das durch den Vergleich gefunden wird .
Ich suche jetzt nach einer Möglichkeit in R, um die Einträge zu identifizieren, die:
- werden am selben Tag mit einem überlappenden Intervall erstellt
- und wo es die gleiche Art ist
- und die von verschiedenen Beobachtungspunkten (A oder B oder C oder ...) gemacht wurden)
In diesem Beispiel habe ich möglicherweise doppelte Einträge derselben Person manuell gefunden. Der Beobachtungspunkt ist unterschiedlich (A B), die Art ist dieselbe (Sst) und das Intervall der Start- und Endzeiten überschneidet sich.
Ich würde jetzt ein neues Feld "duplizieren" in meinem data.frame erstellen und beiden Zeilen eine gemeinsame ID geben, um sie exportieren und später entscheiden zu können, was zu tun ist.
Ich habe viel nach bereits verfügbaren Lösungen gesucht, aber keine gefunden, was die Tatsache betrifft, dass ich den Prozess für die Art (vorzugsweise ohne Schleife) unterteilen und die Zeilen für 2 + x Beobachtungspunkte vergleichen muss.
Einige Daten zum Herumspielen:
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")
Ich habe eine Teillösung mit der Funktion data.table gefunden, die zB hier /programming//q/25815032 erwähnt wird
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)
Natürlich "funktioniert" das irgendwie, aber es ist wirklich nicht das, was ich am Ende gerne erreichen möchte.
Zuerst muss ich die Beobachtungspunkte hart codieren. Ich würde es vorziehen, eine Lösung mit einer beliebigen Anzahl von Punkten zu finden.
Zweitens liegt das Ergebnis nicht in einem Format vor, mit dem ich wirklich problemlos wieder arbeiten kann. Die übereinstimmenden Zeilen werden tatsächlich in dieselbe Zeile eingefügt, während mein Ziel darin besteht, die Zeilen darunter einzufügen, und in einer neuen Spalte würden sie eine gemeinsame Kennung haben.
Drittens muss ich erneut manuell prüfen, ob sich ein Intervall von allen drei Punkten überschneidet (was bei meinen Daten nicht der Fall ist, aber im Allgemeinen möglich ist).
Am Ende möchte ich nur einen neuen data.frame mit allen Kandidaten erhalten, die durch eine Gruppen-ID identifiziert werden können, die ich wieder mit den Zeilen verbinden und das Ergebnis als Ebene zur weiteren Prüfung exportieren kann.
Also noch jemand Ideen, wie das geht?
quelle
for
Schleifen verwendet!Antworten:
Wie einige Kommentatoren angedeutet haben, ist SQL eine gute Option, um ziemlich komplizierte Sätze von Einschränkungen auszudrücken. Das sqldf- Paket macht es einfach, die Leistung von SQL in R zu nutzen, ohne selbst eine relationale Datenbank einrichten zu müssen.
Hier ist eine Lösung mit SQL. Vor dem Ausführen musste ich die Intervallspalten Ihrer Daten in
startTime
und umbenennen,endTime
da der Namefrom
in SQL reserviert ist.Zum besseren Verständnis
dupes_wide
sieht die SQL-Antwort folgendermaßen aus :Self-Join
FROM testdata x JOIN testdata y
: Das Suchen von Zeilenpaaren aus einem einzelnen Dataset ist ein Self-Join. Wir müssen jede Zeile mit jeder anderen vergleichen. DerON
Ausdruck listet die Einschränkungen für das Halten von Paaren auf.Überlappendes Intervall : Ich bin mir ziemlich sicher, dass sich die Definition der Überlappung, die ich in dieser SQL ( Quelle ) verwendet habe, von der
foverlaps
für Sie durchgeführten unterscheidet. Sie haben den Typ "innerhalb" verwendet, bei dem die Beobachtung am früherenobsPoint
vollständig innerhalb der Beobachtung am späteren sein mussobsPoint
(aber das Gegenteil fehlt, z. B. wenn die Beobachtung von C vollständig innerhalb der von B liegt ). Glücklicherweise ist es in SQL einfach, wenn Sie eine andere Definition der Überlappung codieren müssen.Unterschiedliche Punkte : Ihre Einschränkung, dass Duplikate von unterschiedlichen Beobachtungspunkten erstellt wurden, würde wirklich zum Ausdruck gebracht
(x.obsPoint <> y.obsPoint)
. Wenn ich das eingegeben hätte, würde SQL jedes duplizierte Paar zweimal zurückgeben, nur mit der Reihenfolge der Vögel in jeder Zeile. Stattdessen habe ich a verwendet<
, um nur die eindeutige Hälfte der Zeilen beizubehalten. (Dies ist nicht der einzige Weg, dies zu tun)Eindeutige Duplikat-ID : Wie bei Ihrer vorherigen Lösung listet SQL selbst die Duplikate in derselben Zeile auf.
hex(randomblob(16))
ist eine hackige ( aber empfohlene ) Methode in SQLite, um eindeutige IDs für jedes Paar zu generieren.Ausgabeformat : Sie mochten die Duplikate in derselben Zeile nicht,
melt
teilen sie also auf undmerge
weisen die doppelten IDs wieder Ihrem ursprünglichen Datenrahmen zu.Einschränkungen : Meine Lösung behandelt nicht den Fall, dass derselbe Vogel in mehr als zwei Spuren gefangen wird . Es ist schwieriger und etwas schlecht definiert. Zum Beispiel, wenn ihre Zeitbereiche so aussehen
dann ist Bird1 ein Duplikat von Bird2 , was ein Duplikat von Bird3 ist , aber sind Bird1 und Bird3 Duplikate?
quelle