Вопрос:

Уровни коэффициента отбрасывания в заданном кадре данных

r dataframe r-factor r-faq

349807 просмотра

14 ответа

14702 Репутация автора

У меня есть фрейм данных, содержащий фактор. Когда я создаю подмножество этого фрейма данных, используя subset()или другую функцию индексации, создается новый фрейм данных. Однако переменная фактора сохраняет все свои исходные уровни - даже если они не существуют в новом фрейме данных.

Это создает головную боль при выполнении граненых графиков или при использовании функций, основанных на факторных уровнях.

Какой самый краткий способ удалить уровни из фактора в моем новом фрейме данных?

Вот мой пример:

df <- data.frame(letters=letters[1:5],
                    numbers=seq(1:5))

levels(df$letters)
## [1] "a" "b" "c" "d" "e"

subdf <- subset(df, numbers <= 3)
##   letters numbers
## 1       a       1
## 2       b       2
## 3       c       3    

## but the levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"
Автор: medriscoll Источник Размещён: 28.07.2009 06:21

Ответы (14)


35 плюса

291303 Репутация автора

Это известная проблема, и один из возможного средства предусмотрено drop.levels()в GData пакете , где ваш пример становится

> drop.levels(subdf)
  letters numbers
1       a       1
2       b       2
3       c       3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"

Также есть dropUnusedLevelsфункция в пакете Hmisc . Однако он работает только путем изменения оператора подмножества [и здесь неприменим.

Как следствие, прямой подход для каждого столбца прост as.factor(as.character(data)):

> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"
Автор: Dirk Eddelbuettel Размещён: 28.07.2009 06:37

6 плюса

20556 Репутация автора

Это противно. Вот как я обычно это делаю, чтобы не загружать другие пакеты:

levels(subdf$letters)<-c("a","b","c",NA,NA)

который получает вас:

> subdf$letters
[1] a b c
Levels: a b c

Обратите внимание, что новые уровни заменят все, что занимает их индекс на старых уровнях (subdf $ letters), поэтому что-то вроде:

levels(subdf$letters)<-c(NA,"a","c",NA,"b")

не сработает

Это явно не идеально, когда у вас много уровней, но для некоторых это быстро и просто.

Автор: Matt Parker Размещён: 28.07.2009 06:44

380 плюса

19338 Репутация автора

Решение

Все, что вам нужно сделать, это снова применить factor () к вашей переменной после поднабора:

> subdf$letters
[1] a b c
Levels: a b c d e
subdf$letters <- factor(subdf$letters)
> subdf$letters
[1] a b c
Levels: a b c

РЕДАКТИРОВАТЬ

Из примера страницы фактора:

factor(ff)      # drops the levels that do not occur

Для удаления уровней из всех столбцов фактора в кадре данных вы можете использовать:

subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
Автор: hatmatrix Размещён: 28.07.2009 10:41

40 плюса

83243 Репутация автора

Если вы не хотите такого поведения, не используйте факторы, используйте вместо них векторы символов. Я думаю, что это имеет больше смысла, чем исправление вещей впоследствии. Попробуйте выполнить следующее перед загрузкой данных с помощью read.tableили read.csv:

options(stringsAsFactors = FALSE)

Недостатком является то, что вы ограничены в алфавитном порядке. (изменить порядок - ваш друг по участкам)

Автор: hadley Размещён: 28.07.2009 11:53

13 плюса

88677 Репутация автора

Вот еще один способ, который, я считаю, эквивалентен factor(..)подходу:

> df <- data.frame(let=letters[1:5], num=1:5)
> subdf <- df[df$num <= 3, ]

> subdf$let <- subdf$let[ , drop=TRUE]

> levels(subdf$let)
[1] "a" "b" "c"
Автор: ars Размещён: 29.07.2009 03:40

5 плюса

6492 Репутация автора

Я написал вспомогательные функции для этого. Теперь, когда я знаю о drop.levels gdata, он выглядит примерно так же. Вот они ( отсюда ):

present_levels <- function(x) intersect(levels(x), x)

trim_levels <- function(...) UseMethod("trim_levels")

trim_levels.factor <- function(x)  factor(x, levels=present_levels(x))

trim_levels.data.frame <- function(x) {
  for (n in names(x))
    if (is.factor(x[,n]))
      x[,n] = trim_levels(x[,n])
  x
}
Автор: Brendan OConnor Размещён: 01.09.2009 08:37

475 плюса

52914 Репутация автора

Начиная с версии R 2.12, есть droplevels()функция.

levels(droplevels(subdf$letters))
Автор: Roman Luštrik Размещён: 26.11.2010 11:37

5 плюса

483 Репутация автора

вот способ сделать это

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
Автор: Diogo Размещён: 31.01.2014 03:25

3 плюса

307 Репутация автора

Очень интересная ветка, мне особенно понравилась идея просто снова проанализировать выборку. Раньше у меня была похожая проблема, и я просто перешел на характер, а затем снова на фактор.

   df <- data.frame(letters=letters[1:5],numbers=seq(1:5))
   levels(df$letters)
   ## [1] "a" "b" "c" "d" "e"
   subdf <- df[df$numbers <= 3]
   subdf$letters<-factor(as.character(subdf$letters))
Автор: DfAC Размещён: 25.05.2015 12:08

21 плюса

3190 Репутация автора

Еще один способ сделать то же самое, но с dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

Редактировать:

Также работает! Спасибо Agenis

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
Автор: Prradep Размещён: 15.07.2015 11:14

7 плюса

8712 Репутация автора

Глядя на кодdroplevels методов в исходном коде R, вы можете видеть, что он factorработает. Это означает, что вы можете воссоздать столбец с помощью factorфункции.
Ниже data.table способ отбрасывать уровни из всех столбцов факторов.

library(data.table)
dt = data.table(letters=factor(letters[1:5]), numbers=seq(1:5))
levels(dt$letters)
#[1] "a" "b" "c" "d" "e"
subdt = dt[numbers <= 3]
levels(subdt$letters)
#[1] "a" "b" "c" "d" "e"

upd.cols = sapply(subdt, is.factor)
subdt[, names(subdt)[upd.cols] := lapply(.SD, factor), .SDcols = upd.cols]
levels(subdt$letters)
#[1] "a" "b" "c"
Автор: jangorecki Размещён: 09.12.2015 02:56

14 плюса

7752 Репутация автора

Для полноты картины , теперь есть и fct_dropв forcatsпакете http://forcats.tidyverse.org/reference/fct_drop.html .

Он отличается от droplevelsтого, как он работает с NA:

f <- factor(c("a", "b", NA), exclude = NULL)

droplevels(f)
# [1] a    b    <NA>
# Levels: a b <NA>

forcats::fct_drop(f)
# [1] a    b    <NA>
# Levels: a b
Автор: Aurèle Размещён: 12.06.2017 09:44

0 плюса

12 Репутация автора

К сожалению, factor () не работает при использовании rxDataStep из RevoScaleR. Я делаю это в два этапа: 1) Преобразование в символ и сохранение во временном внешнем фрейме данных (.xdf). 2) Преобразовать обратно в фактор и сохранить в определенном внешнем фрейме данных. Это исключает любые неиспользуемые уровни факторов без загрузки всех данных в память.

# Step 1) Converts to character, in temporary xdf file:
rxDataStep(inData = "input.xdf", outFile = "temp.xdf", transforms = list(VAR_X = as.character(VAR_X)), overwrite = T)
# Step 2) Converts back to factor:
rxDataStep(inData = "temp.xdf", outFile = "output.xdf", transforms = list(VAR_X = as.factor(VAR_X)), overwrite = T)
Автор: Jerome Smith Размещён: 28.01.2019 09:10

0 плюса

23 Репутация автора

Попробовал большинство примеров здесь, если не все, но ни один, кажется, не работает в моем случае. После долгого времени я пытался использовать as.character () для столбца factor, чтобы изменить его на col со строками, который, кажется, работает нормально.

Не уверен в проблемах с производительностью.

Автор: Naga Pakalapati Размещён: 02.09.2019 03:20
Вопросы из категории :
32x32