web-dev-qa-db-ja.com

因子レベルのクリーンアップ(複数のレベル/ラベルの崩壊)

折りたたむ必要がある複数のレベルを含む要因をクリーンアップする最も効果的な(つまり効率的/適切な)方法は何ですか?つまり、2つ以上の因子レベルを1つに結合する方法です。

次に、2つのレベル「はい」と「Y」を「はい」に、「いいえ」と「N」を「いいえ」に折りたたむ例を示します。

## Given: 
x <- c("Y", "Y", "Yes", "N", "No", "H")   # The 'H' should be treated as NA

## expectedOutput
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No  # <~~ NOTICE ONLY **TWO** LEVELS

もちろん、1つのオプションは、subとフレンドを使用して、文字列を事前にクリーンアップすることです。

別の方法は、ラベルの重複を許可してからドロップすることです

## Duplicate levels ==> "Warning: deprecated"
x.f <- factor(x, levels=c("Y", "Yes", "No", "N"), labels=c("Yes", "Yes", "No", "No"))

## the above line can be wrapped in either of the next two lines
factor(x.f)      
droplevels(x.f) 

ただし、より効果的な方法はありますか


levelslabelsの引数はベクトルでなければならないことは知っていますが、リストと名前付きリストと名前付きベクトルを試して、何が起こるかを確認しました。私の目標。

  factor(x, levels=list(c("Yes", "Y"), c("No", "N")), labels=c("Yes", "No"))
  factor(x, levels=c("Yes", "No"), labels=list(c("Yes", "Y"), c("No", "N")))

  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Yes="Y", Yes="Yes", No="No", No="N"))
  factor(x, levels=c("Yes", "No"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
53
Ricardo Saporta

levels関数を使用して、名前のリストを渡します。名前はレベルの目的の名前で、要素は名前を変更する必要がある現在の名前です。

x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
levels(x) <- list(Yes=c("Y", "Yes"), No=c("N", "No"))
x
## [1] Yes  Yes  Yes  No   No   <NA>
## Levels: Yes No

levelsのドキュメントに記載されているとおり。例を参照してください。

値: 'factor'メソッドの場合、少なくとも 'x'のレベル数の長さを持つ文字列のベクトル、またはレベルの名前を変更する方法を指定する名前付きリスト。

これは、Marekがここで行うように、1行で行うこともできます。 https://stackoverflow.com/a/10432263/21067 ; levels<-ソーサリーはここで説明されています https://stackoverflow.com/a/10491881/21067

> `levels<-`(factor(x), list(Yes=c("Y", "Yes"), No=c("N", "No")))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No
76
Aaron

質問のタイトルはファクタレベルのクリーンアップ(複数のレベル/ラベルの折りたたみ)であるため、完全を期すためにforcatsパッケージもここに記載する必要があります。 forcatsは2016年8月にCRANに登場しました。

因子レベルのクリーンアップに使用できる便利な機能がいくつかあります。

_x <- c("Y", "Y", "Yes", "N", "No", "H") 

library(forcats)
_

因子レベルを手動で定義されたグループにまとめる

_fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes
_

手で因子レベルを変更する

_fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes
_

因子レベルに自動的にラベルを付け直し、必要に応じて折りたたみます

_fun <- function(z) {
  z[z == "Y"] <- "Yes"
  z[z == "N"] <- "No"
  z[!(z %in% c("Yes", "No"))] <- NA
  z
}
fct_relabel(factor(x), fun)
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes
_

fct_relabel()は因子レベルで機能するため、最初の引数としてfactorを想定していることに注意してください。他の2つの関数fct_collapse()fct_recode()は、文書化されていない機能である文字ベクトルも受け入れます。

最初の出現による因子レベルの並べ替え

OPによって提供される期待される出力は

_[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No
_

ここで、レベルは、デフォルトとは異なるxに表示される順序になっています(_?factor_:因子のレベルはデフォルトでソートされています)。

期待される出力に合わせて、これはfct_inorder()beforeを使用してレベルを縮小することで実現できます。

_fct_collapse(fct_inorder(x), Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
fct_recode(fct_inorder(x), Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
_

両方とも、同じ順序のレベルで期待される出力を返すようになりました。

17
Uwe

キーとしての名前付きベクトルが役に立つかもしれません:

> factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x]))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: No Yes

これはあなたの最後の試みに非常に似ています...しかし、これは動作します:-)

別の方法は、マッピングを含むテーブルを作成することです:

# stacking the list from Aaron's answer
fmap = stack(list(Yes = c("Y", "Yes"), No = c("N", "No")))

fmap$ind[ match(x, fmap$values) ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

# or...

library(data.table)
setDT(fmap)[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

私はこの方法を好みます。なぜなら、それは地図を要約する簡単に検査されたオブジェクトを残すからです。 data.tableコードは、その構文の他の結合と同じように見えます。


もちろん、変更を要約するfmapなどのオブジェクトが必要ない場合は、「ワンライナー」にすることができます。

library(data.table)
setDT(stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))))[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes
5
Frank

この回答を追加して、データフレーム内の特定の要素で機能する受け入れられた回答を示します。これは、最初は明らかではなかったためです(おそらくそうなっているはずですが)。

levels(df$var1)
# "0" "1" "Z"
summary(df$var1)
#    0    1    Z 
# 7012 2507    8 
levels(df$var1) <- list("0"=c("Z", "0"), "1"=c("1"))
levels(df$var1)
# "0" "1"
summary(df$var1)
#    0    1 
# 7020 2507
3
Karl Baker

私はあなたの本当のユースケースを知りませんが、strtrimはここで役に立つでしょう...

factor( strtrim( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) )
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: Yes No
2
Simon O'Hanlon

@Aaronのアプローチに似ていますが、もう少し簡単です:

x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
# levels(x)  
# [1] "H"   "N"   "No"  "Y"   "Yes"
# NB: the offending levels are 1, 2, & 4
levels(x)[c(1,2,4)] <- c(NA, "No", "Yes")
x
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes
2
gung

以下の機能を使用して、複数の要素を結合/折りたたみできます。

combofactor <- function(pattern_vector,
         replacement_vector,
         data) {
 levels <- levels(data)
 for (i in 1:length(pattern_vector))
      levels[which(pattern_vector[i] == levels)] <-
        replacement_vector[i]
 levels(data) <- levels
  data
}

例:

Xを初期化する

x <- factor(c(rep("Y",20),rep("N",20),rep("y",20),
rep("yes",20),rep("Yes",20),rep("No",20)))

構造を確認する

str(x)
# Factor w/ 6 levels "N","No","y","Y",..: 4 4 4 4 4 4 4 4 4 4 ...

次の機能を使用します。

x_new <- combofactor(c("Y","N","y","yes"),c("Yes","No","Yes","Yes"),x)

構造を再確認します。

str(x_new)
# Factor w/ 2 levels "No","Yes": 2 2 2 2 2 2 2 2 2 2 ...
1
Nikhil

最初に、この特定のケースでは部分一致を使用できることに注意しましょう。

x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c("Yes","No")
x <- factor(y[pmatch(x,y,duplicates.ok = TRUE)])
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

より一般的なケースでは、dplyr::recode

library(dplyr)
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c(Y="Yes",N="No")
x <- recode(x,!!!y)
x <- factor(x,y)
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No

開始点が要因の場合、わずかに変更されます。

x <- factor(c("Y", "Y", "Yes", "N", "No", "H"))
y <- c(Y="Yes",N="No")
x <- recode_factor(x,!!!y)
x <- factor(x,y)
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No
1

R 3.5.0(2018-04-23)以降、これを明確でシンプルな1行で実行できます。

x = c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA

tmp = factor(x, levels= c("Y", "Yes", "N", "No"), labels= c("Yes", "Yes", "No", "No"))
tmp
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: Yes No

1行、複数の値を同じレベルにマッピングし、欠落レベルのNAを設定します」– h/t @Aaron

1
tim