376 Stimmen

Spalten nach Namen in einem Datenrahmen löschen

Ich habe einen großen Datensatz und möchte bestimmte Spalten lesen oder alle anderen löschen.

data <- read.dta("file.dta")

Ich wähle die Spalten aus, an denen ich nicht interessiert bin:

var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]

und dann würde ich gerne etwas tun wie:

for(i in 1:length(var.out)) {
   paste("data$", var.out[i], sep="") <- NULL
}

um alle unerwünschten Spalten zu entfernen. Ist dies die optimale Lösung?

478voto

juba Punkte 45350

Sie sollten entweder die Indizierung oder den subset Funktion. Zum Beispiel:

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8

Dann können Sie die which Funktion und die - Operator in der Spaltenindexierung :

R> df[ , -which(names(df) %in% c("z","u"))]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

Oder, viel einfacher, verwenden Sie die select Argument des subset Funktion : Sie können dann die - Operator direkt auf einen Vektor von Spaltennamen anwenden, und Sie können sogar die Anführungszeichen um die Namen weglassen !

R> subset(df, select=-c(z,u))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

Beachten Sie, dass Sie auch die gewünschten Spalten auswählen können, anstatt die anderen wegzulassen:

R> df[ , c("x","y")]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

R> subset(df, select=c(x,y))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

150voto

Ista Punkte 9671

Nicht verwenden -which() Das ist extrem gefährlich. Bedenken Sie:

dat <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
dat[ , -which(names(dat) %in% c("z","u"))] ## works as expected
dat[ , -which(names(dat) %in% c("foo","bar"))] ## deletes all columns! Probably not what you wanted...

Verwenden Sie stattdessen die Untermenge oder den ! Funktion:

dat[ , !names(dat) %in% c("z","u")] ## works as expected
dat[ , !names(dat) %in% c("foo","bar")] ## returns the un-altered data.frame. Probably what you want

Das habe ich aus schmerzlicher Erfahrung gelernt. Nicht überstrapazieren which() !

57voto

Antoine Lizée Punkte 3593

Erste Wenn Sie mit demselben Datenrahmen arbeiten, können Sie die direkte Indizierung (mit Booleschen Vektoren) anstelle des erneuten Zugriffs auf Spaltennamen verwenden; dies ist sicherer, wie von Ista erwähnt, und schneller zu schreiben und auszuführen. Was Sie also brauchen, ist nur:

var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")

und dann einfach die Daten neu zuordnen:

data <- data[,var.out.bool] # or...
data <- data[,var.out.bool, drop = FALSE] # You will need this option to avoid the conversion to an atomic vector if there is only one column left

Zweite Sie können den Spalten, die Sie entfernen möchten, direkt NULL zuweisen, was schneller geht:

data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.

Endlich können Sie subset() verwenden, aber es kann nicht wirklich im Code verwendet werden (sogar die Hilfedatei warnt davor). Besonders problematisch ist für mich, dass man, wenn man die Drop-Funktion von susbset() direkt nutzen will, den Ausdruck, der den Spaltennamen entspricht, ohne Anführungszeichen schreiben muss:

subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL

Als Bonus Hier ist ein kleiner Vergleich der verschiedenen Optionen, der deutlich zeigt, dass die Teilmenge am langsamsten ist und dass die erste, neu zugewiesene Methode am schnellsten ist:

                                        re_assign(dtest, drop_vec)  46.719  52.5655  54.6460  59.0400  1347.331
                                      null_assign(dtest, drop_vec)  74.593  83.0585  86.2025  94.0035  1476.150
               subset(dtest, select = !names(dtest) %in% drop_vec) 106.280 115.4810 120.3435 131.4665 65133.780
 subset(dtest, select = names(dtest)[!names(dtest) %in% drop_vec]) 108.611 119.4830 124.0865 135.4270  1599.577
                                  subset(dtest, select = -c(x, y)) 102.026 111.2680 115.7035 126.2320  1484.174

Microbench graph

Code ist unten:

dtest <- data.frame(x=1:5, y=2:6, z = 3:7)
drop_vec <- c("x", "y")

null_assign <- function(df, names) {
  df[names] <- list(NULL)
  df
}

re_assign <- function(df, drop) {
  df <- df [, ! names(df) %in% drop, drop = FALSE]
  df
}

res <- microbenchmark(
  re_assign(dtest,drop_vec),
  null_assign(dtest,drop_vec),
  subset(dtest, select = ! names(dtest) %in% drop_vec),
  subset(dtest, select = names(dtest)[! names(dtest) %in% drop_vec]),
  subset(dtest, select = -c(x, y) ),
times=5000)

plt <- ggplot2::qplot(y=time, data=res[res$time < 1000000,], colour=expr)
plt <- plt + ggplot2::scale_y_log10() + 
  ggplot2::labs(colour = "expression") + 
  ggplot2::scale_color_discrete(labels = c("re_assign", "null_assign", "subset_bool", "subset_names", "subset_drop")) +
  ggplot2::theme_bw(base_size=16)
print(plt)

34voto

Megatron Punkte 14031

Sie können auch die dplyr Paket:

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8
R> library(dplyr)
R> dplyr::select(df2, -c(x, y))  # remove columns x and y
  z u
1 3 4
2 4 5
3 5 6
4 6 7
5 7 8

19voto

Joben R. Ilagan Punkte 1850

Hier ist eine schnelle Lösung für dieses Problem. Angenommen, Sie haben einen Datenrahmen X mit drei Spalten A, B und C:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6))
> X
  A B C
1 1 3 5
2 2 4 6

Wenn ich eine Spalte, z. B. B, entfernen möchte, verwende ich einfach grep auf colnames, um den Spaltenindex zu erhalten, den Sie dann verwenden können, um die Spalte auszulassen.

> X<-X[,-grep("B",colnames(X))]

Ihr neuer X-Datenrahmen würde wie folgt aussehen (diesmal ohne die B-Spalte):

> X
  A C
1 1 5
2 2 6

Das Schöne an grep ist, dass Sie mehrere Spalten angeben können, die dem regulären Ausdruck entsprechen. Wenn ich X mit fünf Spalten (A,B,C,D,E) hätte:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10

Streichen Sie die Spalten B und D:

> X<-X[,-grep("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

EDIT: Unter Berücksichtigung des Vorschlags von Matthew Lundberg in den Kommentaren unten:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10
> X<-X[,!grepl("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

Wenn ich versuche, eine Spalte zu löschen, die nicht vorhanden ist, sollte nichts passieren:

> X<-X[,!grepl("G",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X