hack のためのネタ帳, etc,,,

型判定

特定の型かどうかの判定は t を型名(クラス名)、x をオブジェクトとした場合 is.t(x) または is(x, "t") 関数で判定出来る。
型名が分からないデータについては以下の関数で調査できる。
  • mode(x)
  • storage.mode(x)
  • typeof(x)
  • class(x)
これらの関数の help を引くと mode と storage mode と type と class の 4 種類の用語があるようなのだが、以下の挙動を見る限り storage mode と type の違いはよくわからない。(typeof の説明でも type or storage mode と言われているので多分同義なのだろう。)
> x=T; c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "logical" "logical" "logical" "logical"
> x=1L; c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "integer" "integer" "integer"
> x=1; c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "double"  "double"  "numeric"
> x=1+0i; c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "complex" "complex" "complex" "complex"
> x="1"; c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "character" "character" "character" "character"
> x=factor(1); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "integer" "integer" "factor" 
> x=matrix(1); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "double"  "double"  "matrix" 
> x=matrix("1"); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "character" "character" "character" "matrix"   
> x=table(1); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "integer" "integer" "table"  
> x=table("1"); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "numeric" "integer" "integer" "table"  
> x=list(1); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "list" "list" "list" "list"
> x=data.frame(1); c(mode(x),storage.mode(x),typeof(x),class(x))
[1] "list"       "list"       "list"       "data.frame"
また unclass(x) 関数で内容の詳細を(Ruby の p とか pp みたいな感じで)確認できる。
他にも str(x) 関数を使うと、冒頭のデータによる簡潔な表示で、データの構造を確認することも出来る。

型変換

t を型名(クラス名)、x をオブジェクトとした場合
as.t(x) または as(x, "t") 関数で変換出来る。
また、先程の mode, storage.mode, class 関数を使って以下のような変換も出来るようだ。
mode(x) <- "t"
storage.mode(x) <- "t"
class(x) <- "t"

numeric

数値型。
numeric() 関数で任意の個数まとめて作成できる。

ストレージモードとしては double か integer が選べる。
一応 single もあるけど、これは疑似モードなんだそうで、オブジェクト x に対して attr(x, "Csingle") が設定されているか否かの違いなんだそうな。

何も考えずに数値を書くと double 扱いされる。
integer にしたい場合は 1L とか 1:1 とか as.ingeter(1) とかする必要がある。

character

文字列型。
character() 関数で任意の個数まとめて作成できる。

vector

一次元配列。
R ではスカラーのつもりで書いても要素数 1 の vector になるので、表面的には vector という型は表には出てこない。
要素数 2 以上の vector を作成するには c() 関数を用いる。
n 番目の要素へは [n] でアクセスする。n は 1 オリジンのインデックス値(one-based index)。

vector は入れ子にならない。
要素の型はすべて同一となる。

c(11,22,33,44)[1] # 11
c(c(1,2),c(3,4))  # c(1,2,3,4)
c(11,22,33,"44")  # c("11","22","33","44")

list

list() 関数で作成する。
様々なデータ型を内包出来る。
n 番目の要素には [[n]] でアクセスする。
[x] とすると x で指定した要素を抜き出した list が得られる。
list(c(11,22,33),c(44,55,66),c(77,88,99))[[1]]      # c(11,22,33)
list(c(11,22,33),c(44,55,66),c(77,88,99))[1]        # list(c(11,22,33))
list(c(11,22,33),c(44,55,66),c(77,88,99))[c(1,3)]   # list(c(11,22,33), c(77,88,99))
list(c(11,22,33),c(44,55,66),c(77,88,99))[[c(1,3)]] # 33
list(c(11,22,33),c(44,55,66),c(77,88,99))[[1]][3]   # 33

factor

因子型。
数値インデックス化された文字列と言うかラベルと言うか、文字列やそのハッシュを見出しにすると検索コストが高いので、見出し語に 1 から連番振って代用する感じのデータ型。
factor() 関数または ordered() 関数に vector を食わせると重複排除した levels が作成され、内部的には levels のインデックス値への参照になる仕組み。
code=c("Alpha", "Bravo", "Charlie", "Alpha")
phonetic=factor(code)
> phonetic
[1] Alpha   Bravo   Charlie Alpha
Levels: Alpha Bravo Charlie
> levels(phonetic)
[1] "Alpha"   "Bravo"   "Charlie"
> labels(phonetic)
[1] "1" "2" "3"
> as.numeric(phonetic)
[1] 1 2 3 1
> as.character(phonetic)
[1] "Alpha"   "Bravo"   "Charlie" "Alpha"
> phonetic[4]
[1] Alpha
Levels: Alpha Bravo Charlie
> as.numeric(phonetic[4])
[1] 1
> as.character(phonetic[4])
[1] "Alpha"

data.frame

いわゆる行列と言うか、表形式のデータと言うか、フィールド名付きの CSV のようなデータ。
data.frame() 関数で作成する。
与えたパラメータの名前(変数名)がそのままフィールド名(カラム名)になるが、「フィールド名=値」とする事で任意のフィールド名を指定出来る。
vector の内容が文字列の場合、自動的に factor 化される。

m 行 n 列の要素へは [m,n] でアクセスできる。
m, n は行または列の名前でも問題ない。
m, n に vector を与えると複数の行または列が対象となり、省略されるとすべての行またはすべての列が対象となる。
ただし、1列のみ抽出すると data.frame が解除されて vector になる。
これを防ぐには、drop パラメータに FALSE を与える。
フィールド名でアクセスする場合は「$フィールド名」を与えてもよいが、この場合の vector 化の防止方法は良く分からない。

code=c("Alpha", "Bravo", "Charlie")
dec=c(10,11,12)
phonetic = data.frame(code,dec)
phonetic$code  # factor(c("Alpha","Bravo","Charlie"))
phonetic$dec   # c(10,11,12)
nrow(phonetic) # 3
ncol(phonetic) # 2
> phonetic
     code dec
1   Alpha  10
2   Bravo  11
3 Charlie  12
> phonetic[1,]
   code dec
1 Alpha  10
> phonetic[,1] # 1列のみ抽出すると data.frame ではなく vector になる。
[1] Alpha   Bravo   Charlie
Levels: Alpha Bravo Charlie
> phonetic[,1,drop=F] # drop に FALSE を与えると1列のみ抽出時でも data.frame で得られる。
     code
1   Alpha
2   Bravo
3 Charlie
> phonetic[,c(2,1)]
  dec    code
1  10   Alpha
2  11   Bravo
3  12 Charlie
> phonetic[,c("code","dec")]
     code dec
1   Alpha  10
2   Bravo  11
3 Charlie  12

データフレームの合成

データフレームの合成は merge() 関数を使う。
これはフィールド名が同じ列を主キーに見立てて共通の主キーが存在する行を合成する、つまり SQL で言う所の INNER JOIN 句のような結合が出来る。

code=c("Alpha", "Bravo", "Charlie")
dec=c(10,11,12)
phonetic = data.frame(code,dec)

code2=c("Alpha","Delta", "Echo", "Foxtrot")
hex=c("a","d","e","f")
phonetic2=data.frame(code=code2,hex)
> phonetic
     code dec
1   Alpha  10
2   Bravo  11
3 Charlie  12
> phonetic2
     code hex
1   Alpha   a
2   Delta   d
3    Echo   e
4 Foxtrot   f
> merge(phonetic, phonetic2)
   code dec hex
1 Alpha  10   a

全ての行を合成する、つまり SQL で言う所の FULL OUTER JOIN 的に合成するには all=T を与える。
> merge(phonetic, phonetic2, all=T)
     code dec  hex
1   Alpha  10    a
2   Bravo  11 <NA>
3 Charlie  12 <NA>
4   Delta  NA    d
5    Echo  NA    e
6 Foxtrot  NA    f
by を用いると任意のフィールドを主キー扱いすることも出来る。
> merge(phonetic, phonetic2, all=T, by="code")
     code dec  hex
1   Alpha  10    a
2   Bravo  11 <NA>
3 Charlie  12 <NA>
4   Delta  NA    d
5    Echo  NA    e
6 Foxtrot  NA    f
> merge(phonetic, phonetic2, all=T, by.x=c("code","dec"), by.y=c("code","hex"))
     code dec
1   Alpha  10
2   Alpha   a
3   Bravo  11
4 Charlie  12
5   Delta   d
6    Echo   e
7 Foxtrot   f
> merge(phonetic, phonetic2, all=T, by.x=c("dec"), by.y=c("hex"))
  dec  code.x  code.y
1  10   Alpha    <NA>
2  11   Bravo    <NA>
3  12 Charlie    <NA>
4   a    <NA>   Alpha
5   d    <NA>   Delta
6   e    <NA>    Echo
7   f    <NA> Foxtrot

要調査:
LEFT OUTER JOIN、RIGHT OUTER JOIN 相当はどうやれば良いのか?
2019-07-31:
all=T じゃなくて all.x=T とか all.y=T とすれば良いらしい。
> merge(phonetic, phonetic2, all.x=T)
     code dec  hex
1   Alpha  10    a
2   Bravo  11 <NA>
3 Charlie  12 <NA>
> merge(phonetic, phonetic2, all.y=T)
     code dec hex
1   Alpha  10   a
2   Delta  NA   d
3    Echo  NA   e
4 Foxtrot  NA   f


フィールド名が同じデータフレームを cat コマンド的に単純に行方向に繋げる場合は rbind() を用いる。
> rbind(phonetic, phonetic)
     code dec
1   Alpha  10
2   Bravo  11
3 Charlie  12
4   Alpha  10
5   Bravo  11
6 Charlie  12
列方向に繋げる場合は cbind() を用いる。
> cbind(phonetic,phonetic)
     code dec    code dec
1   Alpha  10   Alpha  10
2   Bravo  11   Bravo  11
3 Charlie  12 Charlie  12

データフレームの差分

既成の方法が見つからなかったので以下のような関数を作ってみた。
# 差集合 a - b を得る
group_sub <- function(a,b) {
  c = merge(a,b)
  c = data.frame(c, common=1)
  c = merge(a,c,all=T)
  c[is.na(c$common),names(a),drop=F]
}
> group_sub(phonetic, phonetic2)
     code dec
2   Bravo  11
3 Charlie  12
> group_sub(phonetic2, phonetic)
     code hex
2   Delta   d
3    Echo   e
4 Foxtrot   f
クロス集計形式の度数分布表。
例えば、標準で入ってるデータセットである Titanic がこの形式。
table() 関数でクロス集計して、as.data.frame() 関数でデータフレームに戻せる模様。
ただしデータフレームに戻す際、重複行は行として復元されず度数として Freq 列に集計されるため、rpart で決定木分析するような場合は、Freq 列のカウントを元に、重複行として復元する必要が生じる。

data.frame の所で作った phonetic に対して適用してみるとこんな感じ。
> table(phonetic)
         dec
code      10 11 12
  Alpha    1  0  0
  Bravo    0  1  0
  Charlie  0  0  1
> as.data.frame(table(phonetic))
     code dec Freq
1   Alpha  10    1
2   Bravo  10    0
3 Charlie  10    0
4   Alpha  11    0
5   Bravo  11    1
6 Charlie  11    0
7   Alpha  12    0
8   Bravo  12    0
9 Charlie  12    1
不慣れなので dup 使って無理やり戻すとこんな感じだが、もう少し簡単に書けんものか?
> data.frame(
  code = rep(as.data.frame(table(phonetic))$code, as.data.frame(table(phonetic))$Freq),
  dec  = rep(as.data.frame(table(phonetic))$dec , as.data.frame(table(phonetic))$Freq)
)
     code dec
1   Alpha  10
2   Bravo  11
3 Charlie  12

関連

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます