一所懸命に手抜きする

デスクワークばかりのスポーツ嫌いで50歳も過ぎ、いよいよ足腰に衰えを感じつつある昨今。

【R】factor型について(1)_Levels:・・・って?

2020/2/26改訂

Levels:・・・って?

 Rで data.frame を処理して得られた戻り値の下に
 Levels: ○,○・・・ のように記載されていることがあります。

これはfactor型と言うらしい

 わかりにくいので調べてみました。
 例として、文字列ベクトルを用意します。

# Sample1
>( Vc<-c("Jan","Feb","Mar","Apr") )
[1] "Jan" "Feb" "Mar" "Apr"

 続いて、文字列をfactor型に変換してみます。

factor型では文字型要素に順序がつく(デフォルトは辞書順)
# Sample2
> Vc<-factor(c("Feb","Jan","Mar","Apr"))
>     names(Vc)<-c("2月","1月","3月","4月") 
> Vc
2134月 
Feb Jan Mar Apr 
Levels: Apr Feb Jan Mar

Levels: Apr Feb Jan Mar は月順ではなく辞書順ですね。

factor型は順序番号を持っている

factor型では見えないけれど順序番号が振られている

 factorとは日本語では因子と言いますが、この例では Jan とか Feb が因子です。これらに(デフォルトでは)辞書順で順番をつけたのがfactor型です。
 つけた順序は as.integer() により確認できます。

# Sample2 続き
> as.integer(Vc)
[1] 2 3 4 1

 Levels: Apr Feb Jan Mar の順にFeb=2 Jan=3 Mar=4 Apr=1 と因子に順序が割り振られています。

順序をつけるとソートができる

 順序をつけるとソートができるようになります。

# Sample2 から続く
> sort(Vc)
4213月 
Apr Feb Jan Mar 
Levels: Apr Feb Jan Mar

 因子が辞書順にsortされています。

辞書順以外も指定できる

 順序(並べ方)は、辞書順が都合が良いとは限りません。希望の並べ方を引数 levels=c(,,,) にて指定することができます。辞書順に並んでいた月名を、月順に設定します。

# Sample3
> Vc<-factor(c("Feb","Jan","Mar","Apr"),levels=c("Jan","Feb","Mar","Apr"))
>      names(Vc)<-c("2月","1月","3月","4月")
> Vc
2134月 
Feb Jan Mar Apr 
Levels: Jan Feb Mar Apr
> sort(Vc)
1234月 
Jan Feb Mar Apr 
Levels: Jan Feb Mar Apr

 Levels: Jan Feb Mar Apr が月順になりました。これにより、因子が希望通り月順にsortできています。

factor型では因子の大小比較も可能にできる

ordered=TRUE

 因子の順序を利用して行える操作はソートくらいですが、順序に大小関係を付与することもできます。その場合にはfactor()関数の引数に ordered=TRUE(デフォルトではFALSE) を指定します。
 大小関係が付与されると、~以下、~より大きいといった比較ができるようになります。

# Sample4
> ( Vc<-factor( c("Jan","Feb","Mar","Apr"),levels=c("Jan","Feb","Mar","Apr"),ordered=TRUE ) )
[1] Jan Feb Mar Apr
Levels: Jan < Feb < Mar < Apr

## Mar より前(小さい)
> Vc<-[Vc<"Mar"]
[1] Jan Feb
Levels: Jan < Feb < Mar < Apr 

 単純な文字列比較ですと "Apr" < "Jan" ですが、Sample4の場合、levelsで指定した順序により Jan=1 Feb=2 Mar=3 Apr=4 となったので Mar=3 より小さい Jan,Feb が抽出されます。

ordered=TRUE なしでは大小比較できない
# Sample5
>Vc<-factor(c("Jan","Feb","Mar","Apr"),levels=c("Jan","Feb","Mar","Apr"))
>Vc[Vc<"Mar"]
[1] <NA> <NA> <NA> <NA>
Levels: Jan Feb Mar Apr
 警告メッセージ: 
 Ops.factor(Vc,"Mar"):<’ not meaningful for factors

 警告メッセージの意味は「因子どうしで大小比較はできない(意味がない)」というものです。ordered=TRUE をつけないと大小比較ができないということです。

後からordered()関数を通す手もある

factor() 関数の引数に ordered=TRUE をつけるのを忘れた場合には、ordered() 関数を通せば大小比較ができるようになります。

# Sample6
>Vc<-factor(c("Jan","Feb","Mar","Apr"),levels=c("Jan","Feb","Mar","Apr"))
Vc<-ordered(Vc)
Vc[Vc<"Mar"]
[1] Jan Feb
Levels: Jan < Feb < Mar < Apr

invalid factor level, NA generated

最初のlevelsに設定した因子以外は追加できない

 factor型ベクトルには levels= に設定した因子は追加できますが、設定外の因子は NA となります( invalid factor level, NA generated )。

# Sample7
>Vc<-factor(c("Jan","Feb","Mar","Apr"),levels=c("Jan","Feb","Mar","Apr"))
>
Vc[5]<-"Jan"
Vc[6]<-"May"
警告メッセージ: 
 `[<-.factor`(`*tmp*`, 6, value = "May"): 
  invalid factor level, NA generated
>Vc
[1] Jan  Feb  Mar  Apr  Jan  <NA>
Levels: Jan Feb Mar Apr

 実務的な例で言えば、データフレームAにデータフレームBを rbind() する時に、factor型のカラムにてAにはない値がBに出てきた場合にこのようなエラーが出ます。

文字型のデータをデータフレームに引き渡すとfactor型として処理される

文字列をdata.frameにするとfactor型になる

# Sample8
> mn<-c("5","2","3","4")
> en<-c("May","Feb","Mar","Apr")
> df<-data.frame(mn,en)
> df$mn
[1] 5 2 3 4
Levels: 2 3 4 5
> df$en
[1] May Feb Mar Apr
Levels: Apr Feb Mar May

 mn のように数値であっても文字型としてデータフレームに引き渡すとfactor型として処理されています。

factor型にしたくない時はstringsAsFactors=FALSEをつける

data.frame(.....,stringsAsFactors=FALSE)```のようにしてやると

# Sample9
> mn<-c("5","2","3","4")
> en<-c("May","Feb","Mar","Apr")
> df<-data.frame(mn,en,stringsAsFactors=FALSE)
> df$en
[1] "May" "Feb" "Mar" "Apr"
> df$mn
[1] "5" "2" "3" "4"
> class(df$mn)
[1] "character"

 stringsAsFactors=FALSEとすることで、文字列はfactor型ではなくcharacter型になりました。

factor型ではなくcharacter型にすればデータ追加可能
# Sample10  Sample9の df を使用
df[5]<-c("12","Dec")
> df[5,]<-c("10","Oct")
> df
  mn  en
1  5 May
2  2 Feb
3  3 Mar
4  4 Apr
5 10 Oct

 最初の要素( 2,3,4,5 や Feb Mar Apr May )にはなかった 10 Oct が追加されています。エラー( invalid factor level, NA generated )が回避できました。

数値からなる factor型をnumeric型にするとトラブルが発生するかも

 数値をfactor型として、さらにnumeric型にすると…

# Sample11
> ( fc<-factor(c("5","2","3","4")) ) 
[1] 5 2 3 4
Levels: 2 3 4 5

> fc[1]
[1] 5
Levels: 2 3 4 5
> ( nm<-as.numeric(fc) )
[1] 4 1 2 3
> nm[1]
[1] 4

5だった要素が4になるなど数値が変わっています。困りますね。

a-habakiri.hateblo.jp