서브 세트 데이터 프레임의 드롭 팩터 레벨


543

을 포함하는 데이터 프레임이 있습니다 factor. subset또는 다른 인덱싱 기능을 사용하여이 데이터 프레임의 하위 집합을 만들면 새 데이터 프레임이 만들어집니다. 그러나 factor변수는 새 데이터 프레임에없는 경우에도 원래 수준을 모두 유지합니다.

패싯 플로팅을 수행하거나 요인 수준에 의존하는 함수를 사용할 때 문제가 발생합니다.

새 데이터 프레임에서 요인에서 수준을 제거하는 가장 간결한 방법은 무엇입니까?

예를 들면 다음과 같습니다.

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    

# all levels are still there!
levels(subdf$letters)
## [1] "a" "b" "c" "d" "e"

답변:


420

서브셋 후에 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)

22
일회성에는 좋지만 많은 수의 열이있는 data.frame에서는 요소 인 모든 열에 대해 그렇게해야합니다 ... drop.levels ()와 같은 함수가 필요합니다. gdata에서.
Dirk Eddelbuettel

6
알지만 ... 사용자 관점에서 subdf [] <-lapply (subdf, function (x) if (is.factor (x)) factor (x) else x)와 같은 것을 작성하는 것이 빠릅니다. drop.levels () 큰 데이터 세트에서 훨씬 더 효율적인 계산 또는 더 나은? (거대한 데이터 프레임을 위해 위 루프를 for-loop로 다시 작성해야한다고 생각합니다.)
hatmatrix

1
감사합니다 Stephen & Dirk-이 요소를 하나의 요인으로 해결하려고 합니다만, 사람들은 전체 요인의 데이터 프레임 정리에 대한 귀하의 제안에 대해이 의견을 읽을 수 있기를 바랍니다.
medriscoll

9
부작용 mydf <- droplevels(mydf)으로이 함수는 데이터 프레임을 목록으로 변환하므로 아래의 Roman Luštrik 및 Tommy O'Dell이 제안한 솔루션이 바람직합니다.
Johan

1
또한 :이 방법 변수의 순서를 유지합니다.
webelo

492

R 버전 2.12부터는 droplevels()기능이 있습니다.

levels(droplevels(subdf$letters))

7
이 방법을 사용하는 것보다 장점은 factor()원본 데이터 프레임을 수정하거나 새로운 영구 데이터 프레임을 만들 필요가 없다는 것입니다. 부분 droplevels집합 데이터 프레임을 감싸서 격자 함수의 데이터 인수로 사용할 수 있으며 그룹이 올바르게 처리됩니다.
화성

내 요인 (진정한 NA 수준)에 NA 수준이 있으면 NA가 존재하더라도 수준이 떨어짐을 알 수 있습니다.
Meep

46

이 동작을 원하지 않으면 요인을 사용하지 말고 대신 문자형 벡터를 사용하십시오. 나는 이것이 나중에 물건을 패치하는 것보다 더 의미가 있다고 생각합니다. read.table또는로 데이터를로드하기 전에 다음을 시도하십시오 read.csv.

options(stringsAsFactors = FALSE)

단점은 알파벳 순서로 제한되어 있다는 것입니다. (재 순서는 줄거리의 친구입니다)


38

이는 알려진 문제이며, 하나의 가능한 해결책에 의해 제공됩니다 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"

Hmisc 패키지 에도 dropUnusedLevels기능 이 있습니다 . 그러나 하위 집합 연산자를 변경해야만 작동하며 여기에는 해당되지 않습니다.[

결과적으로 열별로 직접 접근하는 것은 간단합니다 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"

5
함수 의 reorder매개 변수는 drop.levels언급 할 가치가 있습니다. 원인의 원래 순서를 유지 해야하는 경우 FALSE값 과 함께 사용하십시오 .
daroczig 2012 년

drop.levels에 gdata를 사용하면 "gdata : 'XLS'(Excel 97-2004) 파일에 대한 read.xls 지원이 활성화됩니다." "gdata : read.xls ()에 필요한 perl 라이브러리를로드 할 수 없습니다." "gdata : 'XLSX'(Excel 2007+) 파일을 지원합니다." "gdata : 'installXLSXsupport ()'기능을 실행하십시오" ""gdata : perl을 자동으로 다운로드하여 설치하십시오. " baseR의 droplevels 사용 ( stackoverflow.com/a/17218028/9295807 )
Vrokipal

물건은 시간이 지남에 따라 발생합니다. 당신 내가 9 년 전에 쓴 답변에 대해 언급하고 있습니다. 기본 R 솔루션이 지금부터 약 N 년 동안 계속 될 기능을 사용하는 솔루션이므로 일반적으로 선호하는 힌트로 이것을 보자 .
Dirk Eddelbuettel

25

똑같이하지만 다른 방법으로 dplyr

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

편집하다:

또한 작동합니다! agenis 덕분에

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)


15

다음은 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"

하, 내가 알고하지 않았다 이러한 모든 년 후가 `[.factor`이 방법 drop... 인수하고 2009 년이 게시 한
데이비드 Arenburg

8

이것은 독특합니다. 다른 패키지를로드하지 않으려면 다음과 같이하십시오.

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")

작동하지 않습니다.

레벨이 많을 때 이상적이지는 않지만 몇 가지 경우 빠르고 쉽습니다.


8

R 소스droplevels메소드 코드를 살펴보면 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"

1
내가 생각하는 data.table방법은 같은 것for (j in names(DT)[sapply(DT, is.factor)]) set(DT, j = j, value = factor(DT[[j]]))
데이비드 Arenburg

1
@DavidArenburg 우리가 [.data.table한 번만 부름에 따라 여기서 많이 바뀌지 않습니다
jangorecki

7

여기에 그 방법이 있습니다

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]

2
이의 잘 속는 사람입니다 오년 이전에 게시 한 대답.
David Arenburg

6

이를 위해 유틸리티 기능을 작성했습니다. 이제 gdata의 drop.levels에 대해 알았으므로 꽤 비슷해 보입니다. 여기 있습니다 ( 여기에서 ).

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
}

4

매우 흥미로운 스레드, 특히 하위 선택을 다시 고려하는 아이디어가 마음에 들었습니다. 나는 전에 비슷한 문제가 있었고 방금 문자로 변환 한 다음 다시 요인으로 전환했습니다.

   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))

내 말은, factor(as.chracter(...))작동하지만보다 효율적이지 않고 간결하게 factor(...). 다른 답변보다 엄청나게 나쁜 것 같습니다.
Gregor Thomas

1

불행히도 RevoScaleR의 rxDataStep을 사용할 때 factor ()가 작동하지 않는 것 같습니다. 두 단계로 수행합니다. 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)

1

전부는 아니지만 내 경우에는 아무것도 작동하지 않는 것 같습니다. 꽤 오랫동안 고생 한 후 요인 열에서 as.character () 를 사용하여 문자열이있는 열로 변경 하려고 시도했지만 제대로 작동하는 것 같습니다.

성능 문제가 확실하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.