Как удалить столбцы по имени в кадре данных
У меня большой набор данных, и я хотел бы прочитать определенные столбцы или удалить все остальные.
data <- read.dta("file.dta")
Я выбираю столбцы, которые меня не интересуют:
var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]
и я хотел бы сделать что-то вроде:
for(i in 1:length(var.out)) {
paste("data$", var.out[i], sep="") <- NULL
}
чтобы удалить все нежелательные столбцы. Является ли это оптимальным решением?
Ответы
Ответ 1
Вы должны использовать индексирование или функцию subset
. Например:
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
Затем вы можете использовать функцию which
и оператор -
при индексации столбцов:
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
Или, что гораздо проще, используйте аргумент select
функции subset
: вы можете использовать оператор -
непосредственно в векторе имен столбцов и даже опускать кавычки вокруг имен!
R> subset(df, select=-c(z,u))
x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Обратите внимание, что вы также можете выбрать нужные столбцы вместо того, чтобы отбрасывать остальные:
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
Ответ 2
Не используйте -which()
для этого, это очень опасно. Рассмотрим:
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...
Вместо этого используйте подмножество или функцию !
:
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
Я узнал это от болезненного опыта. Не злоупотребляйте which()
!
Ответ 3
Первый, вы можете использовать прямую индексацию (с булевыми векторами) вместо повторного доступа к именам столбцов, если вы работаете с одним и тем же фреймом данных; это будет безопаснее, как указано Истой, и быстрее писать и исполнять. Так что вам нужно будет только:
var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")
а затем просто переназначьте данные:
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
Второй, быстрее писать, вы можете напрямую назначить NULL столбцам, которые вы хотите удалить:
data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.
Наконец, вы можете использовать subset(), но его нельзя действительно использовать в коде (даже файл справки предупреждает об этом). В частности, проблема в том, что если вы хотите напрямую использовать функцию drop susbset(), вам нужно написать без кавычек выражение, соответствующее именам столбцов:
subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL
В качестве бонуса, здесь приводится небольшая контрольная точка для разных опций, что ясно показывает, что подмножество является более медленным и что первый, переназначающий метод быстрее:
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]()
Код находится ниже:
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)
Ответ 4
Вы также можете попробовать пакет 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
Ответ 5
Я попытался удалить столбец при использовании пакета data.table
и получил неожиданный результат. Я думаю, что следующее может стоить публикации. Просто небольшое предостережение.
[Отредактировано Мэтью...]
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 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
В принципе, синтаксис для data.table
НЕ ТОЛЬКО как data.frame
. На самом деле существует множество различий, см. FAQ 1.1 и FAQ 2.17. Вы были предупреждены!
Ответ 6
Вот быстрое решение для этого. Скажем, у вас есть кадр данных X с тремя столбцами A, B и 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
Если я хочу удалить столбец, скажем B, просто используйте grep для colnames, чтобы получить индекс столбца, который затем можно использовать для пропуска столбца.
> X<-X[,-grep("B",colnames(X))]
Ваш новый фрейм данных X будет выглядеть следующим образом (на этот раз без столбца B):
> X
A C
1 1 5
2 2 6
Красота grep заключается в том, что вы можете указать несколько столбцов, которые соответствуют регулярному выражению. Если бы я имел X с пятью столбцами (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
Вынуть столбцы B и D:
> X<-X[,-grep("B|D",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
РЕДАКТИРОВАТЬ: Учитывая предложение Грефта Мэтью Лундберга в комментариях ниже:
> 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
Если я попытаюсь удалить столбец, который не существует, ничего не должно произойти:
> X<-X[,!grepl("G",colnames(X))]
> X
A C E
1 1 5 9
2 2 6 10
Ответ 7
Вот еще одно решение, которое может быть полезным для других. Приведенный ниже код выбирает небольшое количество строк и столбцов из большого набора данных. Столбцы выбираются как в одном из ответов на juba, за исключением того, что я использую функцию вставки для выбора набора столбцов с именами, последовательно пронумерованными:
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
Ответ 8
df2 <- df[!names(df) %in% c("c1", "c2")]
Ответ 9
Я изменил код на:
# 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]
В любом случае, ответ juba - лучшее решение моей проблемы!
Ответ 10
Я не могу ответить на ваш вопрос в комментариях из-за низкого рейтинга репутации.
Следующий код даст вам ошибку, потому что функция вставки возвращает строку символов
for(i in 1:length(var.out)) {
paste("data$", var.out[i], sep="") <- NULL
}
Вот возможное решение:
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
}
или просто выполните:
for(i in 1:length(var.out)) {
data[var.out[i]] <- NULL
}