J'ai un grand ensemble de données et j'aimerais lire des colonnes spécifiques ou supprimer toutes les autres.
data <- read.dta("file.dta")
Je sélectionne les colonnes qui ne m'intéressent pas:
var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]
et que j'aimerais faire quelque chose comme:
for(i in 1:length(var.out)) {
paste("data$", var.out[i], sep="") <- NULL
}
pour supprimer toutes les colonnes indésirables. Est-ce la solution optimale?
Vous devez utiliser l'indexation ou la fonction subset
. Par exemple :
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
Ensuite, vous pouvez utiliser la fonction which
et l'opérateur -
dans l'indexation des colonnes:
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
Ou bien, beaucoup plus simple, utilisez l’argument select
de la fonction subset
: vous pouvez ensuite utiliser l’opérateur -
directement sur un vecteur de noms de colonnes, et vous pouvez même omettre les guillemets des noms !
R> subset(df, select=-c(z,u))
x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Notez que vous pouvez également sélectionner les colonnes souhaitées au lieu de supprimer les autres:
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
N'utilisez pas -which()
pour cela, c'est extrêmement dangereux. Considérer:
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...
À la place, utilisez le sous-ensemble ou la fonction !
:
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
J'ai appris cela d'une expérience douloureuse. Ne pas abuser de which()
!
First , vous pouvez utiliser l'indexation directe (avec des vecteurs booléens) au lieu de ré-accéder aux noms de colonnes si vous utilisez le même trame de données; comme le souligne Ista, il sera plus sûr d’écrire et d’exécuter. Donc, ce dont vous aurez seulement besoin est:
var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")
et ensuite, réaffectez simplement les données:
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
Second , plus rapide à écrire, vous pouvez affecter directement NULL aux colonnes que vous souhaitez supprimer:
data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.
Enfin , vous pouvez utiliser subset (), mais il ne peut pas vraiment être utilisé dans le code (même le fichier d'aide met en garde sur il). Plus précisément, un problème pour moi est que si vous souhaitez utiliser directement la fonction de suppression de susbset (), vous devez écrire sans guillemets l'expression correspondant aux noms de colonne:
subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL
En tant que bonus , voici un petit repère des différentes options, qui montre clairement que le sous-ensemble est le plus lent, et que le Premièrement, la méthode de réaffectation est la plus rapide:
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
Code est ci-dessous:
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)
Vous pouvez également essayer le package dplyr
:
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
Voici une solution rapide pour cela. Disons que vous avez un bloc de données X avec trois colonnes A, B et 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
Si je veux supprimer une colonne, dites B, utilisez simplement grep sur les noms de colonnes pour obtenir l'index de colonne, que vous pouvez ensuite utiliser pour omettre la colonne.
> X<-X[,-grep("B",colnames(X))]
Votre nouveau bloc de données X ressemblerait à ce qui suit (cette fois sans la colonne B):
> X
A C
1 1 5
2 2 6
La beauté de grep réside dans le fait que vous pouvez spécifier plusieurs colonnes correspondant à l'expression régulière. Si j'avais X avec cinq colonnes (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
Sortez les colonnes B et D:
> X<-X[,-grep("B|D",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
EDIT: Considérant la suggestion de grepl de Matthew Lundberg dans les commentaires ci-dessous:
> 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
Si j'essaie de supprimer une colonne inexistante, rien ne devrait arriver:
> X<-X[,!grepl("G",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
J'ai essayé de supprimer une colonne en utilisant le package data.table
et j'ai obtenu un résultat inattendu. Je pense que ce qui suit pourrait valoir la peine d’être publié. Juste un petit avertissement.
[Edité par 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
Fondamentalement, la syntaxe pour data.table
n'est PAS exactement la même que data.frame
. Il y a en fait beaucoup de différences, voir FAQ 1.1 et FAQ 2.17. Tu étais prévenu!
J'ai changé le code en:
# 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]
Quoi qu'il en soit, la réponse de Juba est la meilleure solution à mon problème!
df2 <- df[!names(df) %in% c("c1", "c2")]
Voici une autre solution qui peut être utile aux autres. Le code ci-dessous sélectionne un petit nombre de lignes et de colonnes dans un grand ensemble de données. Les colonnes sont sélectionnées comme dans l'une des réponses de juba, sauf que j'utilise une fonction coller pour sélectionner un ensemble de colonnes avec des noms numérotés de manière séquentielle:
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