Ik heb een grote dataset en ik wil graag specifieke kolommen lezen of alle andere schrappen.
data <- read.dta("file.dta")
Ik selecteer de kolommen waarin ik niet geïnteresseerd ben:
var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]
en dan zou ik iets willen doen als:
for(i in 1:length(var.out)) {
paste("data$", var.out[i], sep="") <- NULL
}
om alle ongewenste kolommen te verwijderen. Is dit de optimale oplossing?
Antwoord 1, autoriteit 100%
U moet ofwel indexeren of de functie subset
gebruiken. Bijvoorbeeld:
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
Vervolgens kunt u de functie which
en de operator -
gebruiken in kolomindexering :
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
Of, veel eenvoudiger, gebruik het argument select
van de functie subset
: u kunt dan de operator -
rechtstreeks op een vector van kolomnamen, en u kunt zelfs de aanhalingstekens rond de namen weglaten!
R> subset(df, select=-c(z,u))
x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Houd er rekening mee dat u ook de gewenste kolommen kunt selecteren in plaats van de andere te laten vallen:
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
Antwoord 2, autoriteit 32%
Gebruik -which()
hiervoor niet, het is extreem gevaarlijk. Overweeg:
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...
Gebruik in plaats daarvan subset of de functie !
:
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
Ik heb dit uit pijnlijke ervaring geleerd. Gebruik which()
niet te veel!
Antwoord 3, autoriteit 12%
Ten eerstekunt u directe indexering (met booleans vectoren) gebruiken in plaats van opnieuw toegang te krijgen tot kolomnamen als u met hetzelfde dataframe werkt; het zal veiliger zijn, zoals aangegeven door Ista, en sneller te schrijven en uit te voeren. Dus wat je alleen nodig hebt is:
var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")
en dan, eenvoudig gegevens opnieuw toewijzen:
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
Ten tweede, sneller te schrijven, kunt u direct NULL toewijzen aan de kolommen die u wilt verwijderen:
data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.
Eindelijkkun je subset() gebruiken, maar het kan niet echt in de code worden gebruikt (zelfs het helpbestand waarschuwt ervoor). Een specifiek probleem voor mij is dat als je de drop-functie van susbset() direct wilt gebruiken, je de uitdrukking die overeenkomt met de kolomnamen moet schrijven zonder aanhalingstekens:
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 bonusis hier een kleine benchmark van de verschillende opties, waaruit duidelijk blijkt dat de subset langzamer is en dat de eerste, opnieuw toewijzende methode de snellere is:
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
Codestaat hieronder:
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)
Antwoord 4, autoriteit 7%
Je kunt ook het pakket dplyr
proberen:
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
Antwoord 5, autoriteit 4%
Hier is een snelle oplossing hiervoor. Stel, je hebt een dataframe X met drie kolommen A, B en 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
Als ik een kolom wil verwijderen, zeg B, gebruik dan grep op colnames om de kolomindex te krijgen, die u vervolgens kunt gebruiken om de kolom weg te laten.
> X<-X[,-grep("B",colnames(X))]
Uw nieuwe X-dataframe ziet er als volgt uit (dit keer zonder de B-kolom):
> X
A C
1 1 5
2 2 6
Het mooie van grep is dat je meerdere kolommen kunt specificeren die overeenkomen met de reguliere expressie. Als ik X had met vijf kolommen (A,B,C,D,E):
> 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
Kolom B en D eruit halen:
> X<-X[,-grep("B|D",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
EDIT: Gezien de grepl-suggestie van Matthew Lundberg in de opmerkingen hieronder:
> 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
Als ik een kolom probeer te verwijderen die niet bestaat, zou er niets moeten gebeuren:
> X<-X[,!grepl("G",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
Antwoord 6, autoriteit 2%
Ik heb geprobeerd een kolom te verwijderen terwijl ik het pakket data.table
gebruikte en kreeg een onverwacht resultaat. Ik denk dat het volgende misschien de moeite waard is om te posten. Gewoon een kleine waarschuwing.
[ Bewerkt door Matthew … ]
DF = read.table(text = "
fruit state grade y1980 y1990 y2000
apples Ohio aa 500 100 55
apples Ohio bb 0 0 44
apples Ohio cc 700 0 33
apples Ohio dd 300 50 66
", sep = "", header = TRUE, stringsAsFactors = FALSE)
DF[ , !names(DF) %in% c("grade")] # all columns other than 'grade'
fruit state y1980 y1990 y2000
1 apples Ohio 500 100 55
2 apples Ohio 0 0 44
3 apples Ohio 700 0 33
4 apples Ohio 300 50 66
library('data.table')
DT = as.data.table(DF)
DT[ , !names(dat4) %in% c("grade")] # not expected !! not the same as DF !!
[1] TRUE TRUE FALSE TRUE TRUE TRUE
DT[ , !names(DT) %in% c("grade"), with=FALSE] # that's better
fruit state y1980 y1990 y2000
1: apples Ohio 500 100 55
2: apples Ohio 0 0 44
3: apples Ohio 700 0 33
4: apples Ohio 300 50 66
In principe is de syntaxis voor data.table
NIET exact hetzelfde als data.frame
. Er zijn namelijk veel verschillen, zie FAQ 1.1 en FAQ 2.17. Je bent gewaarschuwd!
Antwoord 7
df2 <- df[!names(df) %in% c("c1", "c2")]
Antwoord 8
Ik heb de code gewijzigd in:
# read data
dat<-read.dta("file.dta")
# vars to delete
var.in<-c("iden", "name", "x_serv", "m_serv")
# what I'm keeping
var.out<-setdiff(names(dat),var.in)
# keep only the ones I want
dat <- dat[var.out]
Hoe dan ook, het antwoord van juba is de beste oplossing voor mijn probleem!
Antwoord 9
Hier is nog een oplossing die voor anderen nuttig kan zijn. De onderstaande code selecteert een klein aantal rijen en kolommen uit een grote dataset. De kolommen zijn geselecteerd zoals in een van de antwoorden van juba, behalve dat ik een plakfunctie gebruik om een reeks kolommen te selecteren met namen die opeenvolgend zijn genummerd:
df = read.table(text = "
state county city region mmatrix X1 X2 X3 A1 A2 A3 B1 B2 B3 C1 C2 C3
1 1 1 1 111010 1 0 0 2 20 200 4 8 12 NA NA NA
1 2 1 1 111010 1 0 0 4 NA 400 5 9 NA NA NA NA
1 1 2 1 111010 1 0 0 6 60 NA NA 10 14 NA NA NA
1 2 2 1 111010 1 0 0 NA 80 800 7 11 15 NA NA NA
1 1 3 2 111010 0 1 0 1 2 1 2 2 2 10 20 30
1 2 3 2 111010 0 1 0 2 NA 1 2 2 NA 40 50 NA
1 1 4 2 111010 0 1 0 1 1 NA NA 2 2 70 80 90
1 2 4 2 111010 0 1 0 NA 2 1 2 2 10 100 110 120
1 1 1 3 010010 0 0 1 10 20 10 200 200 200 1 2 3
1 2 1 3 001000 0 0 1 20 NA 10 200 200 200 4 5 9
1 1 2 3 101000 0 0 1 10 10 NA 200 200 200 7 8 NA
1 2 2 3 011010 0 0 1 NA 20 10 200 200 200 10 11 12
", sep = "", header = TRUE, stringsAsFactors = FALSE)
df
df2 <- df[df$region == 2, names(df) %in% c(paste("C", seq_along(1:3), sep=''))]
df2
# C1 C2 C3
# 5 10 20 30
# 6 40 50 NA
# 7 70 80 90
# 8 100 110 120
Antwoord 10
Ik kan je vraag niet beantwoorden in de reacties vanwege de lage reputatiescore.
De volgende code geeft een foutmelding omdat de plakfunctie een tekenreeks retourneert
for(i in 1:length(var.out)) {
paste("data$", var.out[i], sep="") <- NULL
}
Hier is een mogelijke oplossing:
for(i in 1:length(var.out)) {
text_to_source <- paste0 ("data$", var.out[i], "<- NULL") # Write a line of your
# code like a character string
eval (parse (text=text_to_source)) # Source a text that contains a code
}
of doe gewoon:
for(i in 1:length(var.out)) {
data[var.out[i]] <- NULL
}
Antwoord 11
df = mtcars
verwijder vs en ben omdat ze categorisch zijn. In de dataset
vs staat in kolom 8, am staat in kolom 9
dfnum = df[,-c(8,9)]