web-dev-qa-db-ja.com

サブセットデータフレーム内のドロップファクタレベル

因子を含むデータフレームがあります。 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    

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

サブセット化した後に、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)
372
hatmatrix

Rバージョン2.12以降、droplevels()関数があります。

levels(droplevels(subdf$letters))
468
Roman Luštrik

あなたがこの振る舞いを望まないならば、要因を使わないで、代わりに文字ベクトルを使ってください。後でパッチをあてるよりも、これが理にかなっていると思います。 read.tableまたはread.csvを使用してデータをロードする前に、次のことを試してください。

options(stringsAsFactors = FALSE)

不利な点は、あなたがアルファベット順に制限されていることです。 (並べ替えはプロットのためのあなたの友人です)

39
hadley

これは既知の問題であり、考えられる解決策の1つは gdata パッケージのdrop.levels()です。

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

dplyrを使って同じことをする別の方法

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

編集:

また動作します!おかげで agenis

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

これは私が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"
12
ars

完全を期すために、forcatsパッケージにはfct_dropもあります 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
12
Aurèle

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

そのやり方はここにあります

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

これは不快です。他のパッケージをロードしないようにするために、これが私のやり方です。

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

動作しません。

あなたがたくさんのレベルを持っているとき、これは明らかに理想的ではありませんが、少数のために、それは速くて簡単です。

6
Matt Parker

私はこれを行うための効用関数を書きました。今私はgdataのdrop.levelsについて知っていますが、それはかなり似ています。ここにあります(from here ):

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
}
5
Brendan OConnor

非常に興味深いスレッド、私はサブセレクションをもう一度考慮に入れるというアイデアが特に好きでした。私は以前に同様の問題を抱えていました、そして、私はただ文字に変換して、そして次に因数に戻りました。

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

残念ながら、factor()はRevoScaleRのrxDataStepを使用したときには動作しないようです。 2つのステップで行います。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)
0
Jerome Smith