因子を含むデータフレームがあります。 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"
サブセット化した後に、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)
Rバージョン2.12以降、droplevels()
関数があります。
levels(droplevels(subdf$letters))
あなたがこの振る舞いを望まないならば、要因を使わないで、代わりに文字ベクトルを使ってください。後でパッチをあてるよりも、これが理にかなっていると思います。 read.table
またはread.csv
を使用してデータをロードする前に、次のことを試してください。
options(stringsAsFactors = FALSE)
不利な点は、あなたがアルファベット順に制限されていることです。 (並べ替えはプロットのためのあなたの友人です)
これは既知の問題であり、考えられる解決策の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"
dplyr
を使って同じことをする別の方法
library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)
編集:
また動作します!おかげで agenis
subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
これは私が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"
完全を期すために、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
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"
そのやり方はここにあります
varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
これは不快です。他のパッケージをロードしないようにするために、これが私のやり方です。
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")
動作しません。
あなたがたくさんのレベルを持っているとき、これは明らかに理想的ではありませんが、少数のために、それは速くて簡単です。
私はこれを行うための効用関数を書きました。今私は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
}
非常に興味深いスレッド、私はサブセレクションをもう一度考慮に入れるというアイデアが特に好きでした。私は以前に同様の問題を抱えていました、そして、私はただ文字に変換して、そして次に因数に戻りました。
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()は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)