サブセット化されたデータフレームのドロップファクターレベル


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
これは1回限りの場合は問題ありませんが、列数が多いdata.frameでは、すべての列でそれを行うことができます。これは、... drop.levels()などの関数が必要になる要因となります。 gdataから。
Dirk Eddelbuettel、2009

6
わかりました...しかし、ユーザーの観点からは、subdf [] <-lapply(subdf、function(x)if(is.factor(x))factor(x)else x)のようなものを書くのは速いです... drop.levels()は計算的にはるかに効率的ですか、それとも大きなデータセットを使用した方が良いですか?(巨大なデータフレームの場合は、forループで上記の行を書き換える必要があると思います。)
Hatmatrix

1
Stephen&Dirk氏に感謝します。これはある要因の原因を高く評価するためのものですが、因子のデータフレーム全体をクリーンアップするための提案として、皆さんのコメントを読んでいただければ幸いです。
medriscoll 2009

9
副作用として、関数はデータフレームをリストに変換するため、mydf <- droplevels(mydf)以下のRomanLuštrikとTommy O'Dellが提案するソリューションが推奨されます。
ヨハン

1
また、このメソッド変数の順序を保持します。
webelo 2016

492

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

levels(droplevels(subdf$letters))

7
この方法の利点はfactor()、元のデータフレームを変更したり、新しい永続データフレームを作成したりする必要がないことです。droplevelsサブセット化されたデータフレームをラップして、ラティス関数のデータ引数として使用すると、グループが正しく処理されます。
2015年

私のファクターにNAレベル(本物のNAレベル)がある場合、たとえNAが存在していても、ドロップされたレベルによってドロップされることに気づきました。
Meep

46

この動作を望まない場合は、係数を使用せず、代わりに文字ベクトルを使用してください。これは、後でパッチを適用するよりも理にかなっていると思います。read.tableまたはでデータをロードする前に、次のことを試してくださいread.csv

options(stringsAsFactors = FALSE)

欠点は、アルファベット順に制限されていることです。(再注文はプロットの友です)


38

これは既知の問題であり、1つの考えられる解決策は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

drop.levelsだけにgdataを使用すると、「gdata:read.xls support for 'XLS'(Excel 97-2004)files ENABLED」が生成されます。"gdata:read.xls()で必要なperlライブラリをロードできません" "gdata: 'XLSX'(Excel 2007+)ファイルをサポートします。" "gdata:関数 'installXLSXsupport()'を実行します" "gdata:perlを自動的にダウンロードしてインストールする" 使用が卑(から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年に投稿した...
デビッド・アレンブルク

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

見てみると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"

1
私はそのdata.table方法は次のようなものになると思いますfor (j in names(DT)[sapply(DT, is.factor)]) set(DT, j = j, value = factor(DT[[j]]))
デビッド・アレンブルク

1
@DavidArenburg呼び出し[.data.tableは1回だけなので、ここではそれほど変わりません
jangorecki


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()が機能しないようです。私は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)

1

私の場合、すべてではないにしても、ほとんどの例をここで試しましたが、どれもうまくいっていないようです。しばらく苦労した後、factor列でas.character()を使用して、うまく機能しているように見える文字列を含むcolに変更してみました。

パフォーマンスの問題については不明です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.