R
dplyr

{dplyr::filter}のfactor型処理後に、droplevels()でLevelsの要素を整える

More than 1 year has passed since last update.

概要

R言語でのデータ前処理を楽にしてくれる{dplyr}パッケージには、マトリクスデータから指定列の条件に合う行のみを抽出してくれるfilter()という便利な関数があります。

ただfilter()関数は、各列から因子(factor)型の種類を示すlevelsが抽出元のデータのままになる特徴があり、そのままにしておくと、例えば{graphics::plot}で余分な部分を含んでプロットしてしまう。そのためdroplevels()関数を用いて、Levelsの要素を整えてあげる必要があります。

以降にて、データの準備と抽出処理と調整までの、一連の説明とコードを記載します。
(なお{ggplot2}パッケージであれば、調整せずとも簡単にプロットできます)

説明とコード

パッケージとデータの準備

パッケージを読込({tidyverse}のみでも可)

library(dplyr) #0.7.1
library(data.table) #1.10.4

「商品3種(apple,banana,orange)を担当者4人(A,B,C,D)が何個売ったか」のサンプルデータを生成

set.seed(11)
product <- sample(factor(c("apple","banana","orange")),size=30,replace=TRUE)
number <- sample(c(1:10),size = 30,replace = TRUE)
staff <- sample(factor(c("A","B","C","D")),size= 30,replace= TRUE)

sales_dt <- data.table(PRODUCT = product,NUMBER = number,STAFF = staff)
head(sales_dt)
PRODUCT NUMBER STAFF
1 apple 6 A
2 apple 4 D
3 banana 5 A
4 apple 3 C
5 apple 9 D
6 orange 7 A

filter()関数で、「担当者AもしくはDの、appleの販売個数」と条件づけてデータを抽出。

sales_dt %>% 
  filter(STAFF == "A" | STAFF =="D") %>%
  filter(PRODUCT == "apple") -> filter_sales_dt

抽出前後データの因子を確認

抽出元のsales_dtの因子要素を確認する。

sapply(sales_dt, FUN = "levels")
$PRODUCT
[1] "apple"  "banana" "orange"

$NUMBER
NULL

$STAFF
[1] "A" "B" "C" "D"

抽出後のfilter_sales_dtの因子要素を確認する。

sapply(filter_sales_dt, FUN = "levels")
$PRODUCT
[1] "apple"  "banana" "orange"

$NUMBER
NULL

$STAFF
[1] "A" "B" "C" "D"

どちらも同じ結果が表示されていることがわかる。
(変数$NUMBERは整数(integer)型のためNULL表記)

droplevels()を用いてlevels要素を整える

droplevels()を用いて、不要な因子型を取り除く。

check_dt <- droplevels(filter_sales_dt)
sapply(check_dt, FUN = "levels")
$PRODUCT
[1] "apple"

$NUMBER
NULL

$STAFF
[1] "A" "D"

なお、もしこの調整をしないでプロットすると、不要な担当者"B"と"C"まで含めてしまう。

plot(filter_sales_dt$STAFF, filter_sales_dt$NUMBER)

Rplot.png

最後に

今後もしこのようにプロットされる事に遭遇したら、まずLevelsを疑うことがオススメです。
{ggplot2}以外でプロットしなければいけない場合(決定木で{rpart.plot}を使う場合など)は、データ型の取り扱いミスしていないか確認すると近道かも。

参照元

Rで解析:不要な水準を取り除く「droplevels」コマンド
https://www.karada-good.net/analyticsr/r-319

How to drop unused levels after filtering by factor? [duplicate]
https://stackoverflow.com/questions/26826865/how-to-drop-unused-levels-after-filtering-by-factor