Почему [- подмножество (например, удаление) столбцов невозможно с именами?

Я очень опасаюсь, что это было задано и будет отменено, но я не нашел ответа в документах (? "[" ) и обнаружил, что его трудно найти.

data(wines)
# This is allowed:
alcoholic <- wines[, 1]
alcoholic <- wines[, "alcohol"]
nonalcoholic <- wines[, -1]
# But this is not:
fail <- wines[, -"alcohol"]

Я знаю о двух решениях, но разочарован им в необходимости.

win <- wines[, !colnames(wines) %in% "alcohol"]  # snappy
win <- wines[, -which(colnames(wines) %in% "alcohol")]  # snappier!

Ответы

Ответ 1

Когда вы делаете

wines[, -1]

-1 оценивается до того, как используется [. Как вы знаете, унарный оператор - не будет работать с объектом класса character, поэтому сделать то же самое с "алкоголем" приведет вас к:

Error in -"alcohol" : invalid argument to unary operator

Вы можете добавить следующие альтернативы:

wines[, -match("alcohol", colnames(wines))]
wines[, setdiff(colnames(wines), "alcohol")]

но вы должны знать о рисках отрицательного индексирования, например, посмотреть, что произойдет, если вы ошиблись "alcool" (sic.). Поэтому ваше первое предложение и последнее здесь (@Ананда) должны быть предпочтительнее. Вы также можете написать функцию, которая будет выходить из системы, если вы укажете имя, которое не является частью ваших данных.

Ответ 2

Другая возможность:

subset(wines,select=-alcohol)

Вы даже можете сделать

subset(wines,select=-c(alcohol,other_drop))

Фактически, если у вас есть смежный набор столбцов, которые вы хотите отбросить, вы можете даже

subset(wines,select=-(first_drop:last_drop))

что может быть удобно (хотя IMO это зависит от порядка столбцов, что может быть хрупким: я мог бы предпочесть решение на основе grep, если бы был способ идентифицировать столбцы или более явный отдельное определение групп столбцов).

В этом случае subset использует нестандартную оценку, которая, как обсуждалось в других местах, может быть опасна в некоторых контекстах. Но мне все еще нравится простое управление данными на верхнем уровне из-за его удобочитаемости.

Ответ 3

Другой метод, который использует числовую индексацию и обобщает на ситуации, когда вы хотите удалить кучу столбцов с одинаковым именем:

dfrm[ , -grep("^val", names(dfrm) )] #remove columns starting with "val"

(Я дал свой голос флоле, так как его ответ описал "почему" знак "минус" не работал. По сути, поскольку авторы R не перегружали оператора "-" для этой цели, t перегрузить "+", чтобы сделать конкатенацию так, как это делали некоторые языки.

Ответ 4

Как писать простую небольшую функцию и вставлять ее в .Rprofile. Что-то вроде...

dropcols <- function( df , cols ){
  out <- df[ , !names(df) %in% cols]
  return( out )
}

#  To use it....
data( mtcars )
head( dropcols( mtcars , "mpg" ) )
#                  cyl disp  hp drat    wt  qsec vs am gear carb
#Mazda RX4           6  160 110 3.90 2.620 16.46  0  1    4    4
#Mazda RX4 Wag       6  160 110 3.90 2.875 17.02  0  1    4    4
#Datsun 710          4  108  93 3.85 2.320 18.61  1  1    4    1
#Hornet 4 Drive      6  258 110 3.08 3.215 19.44  1  0    3    1
#Hornet Sportabout   8  360 175 3.15 3.440 17.02  0  0    3    2
#Valiant             6  225 105 2.76 3.460 20.22  1  0    3    1

Ответ 5

Я не могу найти это в документации, но следующий синтаксис работает с data.table:

dt = data.table(wines)

dt[, !"alcohol", with = F]

И вы также можете иметь список столбцов, если хотите:

dt[, !c("Country", "alcohol"), with = F]

Это было просто задокументировано в NEWS для v1.8.4, похоже:

Когда с = FALSE, "!" также может быть префиксом на j, # 1384ii. Это выбирает все, кроме названных столбцов.

DF[,-match("somecol",names(DF))]
# works when somecol exists. If not, NA causes an error.

DF[,-match("somecol",names(DF),nomatch=0)]
# works when somecol exists. Empty data.frame when it doesn't, silently.

DT[,-match("somecol",names(DT)),with=FALSE]
# same issues.

DT[,setdiff(names(DT),"somecol"),with=FALSE]
# works but you have to know order of arguments, and no warning if missing

против

DT[,!"somecol",with=FALSE]
# works and easy to read. With (helpful) warning if somecol isn't there.

Но все вышеперечисленное копирует каждый столбец, отличный от удалённого. Чаще всего:

DT[,somecol:=NULL]

чтобы удалить столбец по имени по ссылке.

Ответ 6

Вы можете получить желаемое поведение следующим образом:

data(iris)
str(iris)
delete <- which(colnames(iris) == "Species")
iris2 <- iris[, -delete]
str(iris2)