Wie entferne ich alle bis auf einen bestimmten doppelten Datensatz in einem R-Datenrahmen? [geschlossen]

16

Ich habe einen Datenrahmen, der einige doppelte IDs enthält. Ich möchte Datensätze mit doppelten IDs entfernen und nur die Zeile mit dem Maximalwert beibehalten.

Also für so strukturiert (andere Variablen nicht gezeigt):

id var_1
1 2
1 4
2 1
2 3
3 5
4 2

Ich möchte folgendes generieren:

id var_1
1 4
2 3
3 5
4 2

Ich kenne unique () und duplicated (), aber ich kann nicht herausfinden, wie ich die Maximierungsregel einbauen soll ...

Abe
quelle
Es sollte eigentlich im Stackoverflow sein, da es sich um eine reine Programmieraufgabe handelt und wenig mit Statistik zu tun hat
Enthusiast

Antworten:

24

Eine Möglichkeit besteht darin, die Daten in umgekehrter Reihenfolge zu sortieren und duplicatedalle Duplikate zu löschen. Für mich ist diese Methode konzeptionell einfacher als die, die zur Anwendung kommen. Ich denke, es sollte auch sehr schnell gehen.

# Some data to start with:
z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
# id var
#  1   2
#  1   4
#  2   1
#  2   3
#  3   5
#  4   2

# Reverse sort
z <- z[order(z$id, z$var, decreasing=TRUE),]
# id var
#  4   2
#  3   5
#  2   3
#  2   1
#  1   4
#  1   2

# Keep only the first row for each duplicate of z$id; this row will have the
# largest value for z$var
z <- z[!duplicated(z$id),]

# Sort so it looks nice
z <- z[order(z$id, z$var),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2

Bearbeiten: Ich habe gerade festgestellt, dass die umgekehrte Sortierung oben überhaupt nicht sortieren muss id. Sie könnten z[order(z$var, decreasing=TRUE),]stattdessen einfach verwenden und es wird genauso gut funktionieren.

Noch ein Gedanke ... Wenn die varSpalte numerisch ist, gibt es eine einfache Möglichkeit, idaufsteigend zu sortieren , aber varabsteigend. Dies macht die Sortierung am Ende überflüssig (vorausgesetzt, Sie wollten sogar, dass sie sortiert wird).

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))

# Sort: id ascending, var descending
z <- z[order(z$id, -z$var),]

# Remove duplicates
z <- z[!duplicated(z$id),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2
wch
quelle
1
Dieser Ansatz ist wesentlich schneller als "split-compute-rbind". Darüber hinaus können mehrere Faktoren gruppiert werden. Für eine c. 650.000 Zeilen (8, schmal, Spalten) dauerte der "order-duplicated" -Ansatz 55 Sekunden, der split-compute-rbind ... 1h15minutes. Wenn die Aggregatberechnung keine Auswahl oder Filterung von Duplikaten ist, werden natürlich der letztere Ansatz oder ähnliche plyr-basierte Ansätze benötigt.
mjv
7

Sie möchten tatsächlich das maximale Element aus den Elementen mit derselben ID auswählen. Dafür können Sie ddplyaus dem Paket plyr folgendes verwenden :

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
> ddply(dt,.(id),summarise,var_1=max(var))
   id var_1
1  1   4
2  2   3
3  3   4
4  4   2

uniqueund duplicateddient zum Entfernen doppelter Datensätze. In Ihrem Fall haben Sie nur doppelte IDs, keine Datensätze.

Update: Hier ist der Code, wenn es zusätzliche Variablen gibt:

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2),bu=rnorm(6))
> ddply(dt,~id,function(d)d[which.max(d$var),])
mpiktas
quelle
Was wäre, wenn es andere Variablen gäbe: Wie tragen Sie sie mit?
Aniko
Wir bewegen solche Fragen nicht - zu viel Eile für zu wenig Gewinn.
6

Die Base-R-Lösung würde folgendermaßen splitaussehen:

z<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
do.call(rbind,lapply(split(z,z$id),function(chunk) chunk[which.max(chunk$var),]))

splitTeilt den Datenrahmen in eine Liste von Blöcken auf, in denen auf die einzelne Zeile mit dem Maximalwert geschnitten wird, und do.call(rbind,...)reduziert dann die Liste der einzelnen Zeilen wieder in einen Datenrahmen.


quelle
1
Und das ist wie immer etwa 2x schneller als die Plyr-Version.
1
@mbq, ja, natürlich, aber wenn Sie die Debugging-Kosten mit einbeziehen, ist die resultierende Geschwindigkeit für gewöhnliche Datensätze dieselbe :) plyr ist nicht der Geschwindigkeit, sondern der Übersichtlichkeit und Bequemlichkeit halber gewidmet.
mpiktas
und mit ave ist sowieso doppelt so schnell :)
Eduardo Leoni
2
@Eduardo aveist ein Wrapper von lapply+ split, überprüfen Sie den Code (-;
1
@Eduardo Ja, aber es funktioniert alles nur aufgrund einer skurrilen Möglichkeit der vektorisierten Sortierung innerhalb von Faktoren unter Verwendung von order; für allgemeinere Probleme splitist unvermeidlich.
5

Ich benutze lieber ave

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,3,3,4,2))
## use unique if you want to exclude duplicate maxima
unique(subset(dt, var==ave(var, id, FUN=max)))
Eduardo Leoni
quelle
+1, wusste nicht über Ave. Wann ist es in R erschienen?
mpiktas
1

Noch eine andere Möglichkeit, dies mit base zu tun:

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))

data.frame(id=sort(unique(dt$var)),max=tapply(dt$var,dt$id,max))
  id max
1  1   4
2  2   3
3  3   4
4  4   2

Ich bevorzuge jedoch die Plyr-Lösung von mpiktas.

Sacha Epskamp
quelle
1

Wenn sich die Spalte var wie im Beispiel bereits in aufsteigender Reihenfolge befindet, müssen wir den Datenrahmen nicht sortieren. Wir verwenden nur die Funktion, duplicateddie das Argument übergibt fromLast = TRUE, sodass die Duplizierung von der Rückseite aus betrachtet wird und die letzten Elemente beibehalten werden:

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
z[!duplicated(z$id, fromLast = TRUE), ]

  id var
2  1   4
4  2   3
5  3   5
6  4   2

Andernfalls sortieren wir den Datenrahmen zuerst in aufsteigender Reihenfolge:

z <- z[order(z$id, z$var), ]
z[!duplicated(z$id, fromLast = TRUE), ]

Verwendung des dplyrPakets:

library(dplyr)
z %>%
  group_by(id) %>%
  summarise(var = max(var))

Source: local data frame [4 x 2]    
  id var
1  1   4
2  2   3
3  3   5
4  4   2
mpalanco
quelle