趣旨
普段ベクトルを作成したりするときに使うc()
。
c("a", "b", "c")
[1] "a" "b" "c"
しかし「ベクトル作る関数だな」程度の認識だった私、apply()
やmapply()
で躓きました。
というわけで 『Rユーザーが最も使う関数c()
を改めて見てみましょう。』 という企画。
c()
とは?
まずは名前から行きましょう。公式ヘルプを呼び出します。
help(c)
こちらでも同じものが読めます。
R: Combine Values into a Vector or List
タイトルは 『Combine Values into a Vector or List』 です。つまりc()
の「c」はCombineに由来するものなんですね。
さらに説明を読むと以下の記述があります。
This is a generic function which combines its arguments.
The default method combines its arguments to form a vector. All arguments are coerced to a common type which is the type of the returned value, and all attributes except names are removed.
まあ要するに、**『引数に渡したオブジェクトをベクトルかリストの形式で結合してくれる関数』**ということですね。
ここで注目しておきたいのは 引き渡すオブジェクトの種類(データ型・構造)に制約を設けていない 点です。つまり、あらゆるオブジェクトを最大公約数的なカタチで結合してくれるようですね。
# install.packages("Hmisc")
char <- "moji"
df <- data.frame(d = 888, 999)
# c() による結合
cmb1 <- c(char, df)
# 結合データの構造を確認
Hmisc::list.tree(cmb1)
cmb1 = list 2 (528 bytes)
. [[1]] = character 1= moji
. d = double 2= 888 999
このように異なるデータ型(character
を格納したvector
+ numeric
を格納したdata.frame
)であっても問題なくリスト型データに結合してくれます。
そう、意外と『万能』なんです。
二種類の返り値
c()
の返り値は、vector型
またはlist型
の2種類があります。それによって挙動が微妙に違うようです。
リストによる返り値
こっちのほうが簡単なので先に見ていきましょう。
env <- new.env()
char <- "moji"
num <- 1
int <- as.integer(0)
vec <- c("p", "q")
fct <- factor(c("Z", "Y", "X"), levels = c("X", "Y", "Z"))
mtr <- matrix(3330:3335, 2,3)
lst <- list(l1 = 111, l2 = "LIST")
df <- data.frame(d = c(888, 999))
fun <- function(x, y) x + y
# c() による結合
cmb2 <- c(env, char, num, int, vec, fct, mtr, lst, df, fun)
# 結合データの構造を確認
Hmisc::list.tree(cmb2, maxcomp = length(cmb2))
cmb2 = list 19 (3584 bytes)
. [[1]] = environment 0
. [[2]] = character 1= moji
. [[3]] = double 1= 1
. [[4]] = integer 1= 0
. [[5]] = character 1= p
. [[6]] = character 1= q
. [[7]] = integer 1= 3
. [[8]] = integer 1= 2
. [[9]] = integer 1= 1
. [[10]] = integer 1= 1
. [[11]] = integer 1= 2
. [[12]] = integer 1= 3
. [[13]] = integer 1= 4
. [[14]] = integer 1= 5
. [[15]] = integer 1= 6
. l1 = double 1= 111
. l2 = character 1= LIST
. d = double 2= 888 999
. [[19]] = function 1
. A srcref = integer 8( srcref )= 1 8 1 27 8 27 1 ...
. A A srcfile = environment 2( srcfilecopy srcfile )
**「単一のベクトルで表現されないデータ構造」**を格納したオブジェクトを1つ以上含めて結合しようとするとlist型
データが返却されます。その際、character
、numeric
といった、結合するオブジェクトのデータ型(データ「構造」ではない。)は返却後も保持されています。
上の例では、list型
・data.frame型
・environment型
・funcion型
が「単一のベクトルで表現されないデータ構造」に該当します。
ベクトルによる返り値
**「単一のベクトルで表現できるデータ構造」**のみを結合するとvector型データが返却されます。
上の例では、list型
・data.frame型
・environment型
・funcion型
が「単一のベクトルで表現されないデータ構造」に該当します。
属性について
注意したいのが、c()
による結合は、$names
属性以外のデータを保持しない ということです。
よく分からないかもしれませんが、要はオブジェクトのメインコンテンツ以外は切り捨てられるということです。これは冒頭で引用したヘルプにも書いてあることです。
(中略) all attributes except names are removed.
これがどのように影響するのか、具体的には例えば以下のようなことが挙げられます。
結合するデータ型 | 症状 | 回避策 |
---|---|---|
data.frame型 |
data.frame 属性が失われるため、list型 に変換されてから結合されてしまう。 |
list() による結合 |
matrix型 |
dim 属性が失われるため、一行のベクトルに変換されてしまう。 |
一旦data.frame型 へ変換させる。その上でさらに list型 を回避したい場合は上記を参考。 |
factor型 |
levels ・ordered 等の属性情報が失われるため、そのまま結合させるとinteger型 のベクトルに変換されてしまう。 |
ラベルを保持したい場合は、as.vector() 等で変換してから結合を行う。属性全体を保持したい場合は、 list() による結合が有効。 |
list()による結合
以上に関連して、c()
を使った結合とlist()
による結合には多少の差があります。
> c(df)
$d
[1] 888 999
> list(df)
[[1]]
d
1 888
2 999
# c() による結合
# data.frameがlist型に強制変換されている
> c(df)[[1]]
[1] 888 999
> is.data.frame(c(df)[[1]])
[1] FALSE
# list() による結合
# data.frameが保持されている
> list(df)[[1]]
d
1 888
2 999
> is.data.frame(list(df)[[1]])
[1] TRUE
このように、c()
の場合はデータ構造は一律でvector型
またはlist型
に変換されますが、list()
の場合は元のデータ構造が保持されることになります。
また、matrix型
の場合も示していきます。
> c(mtr)
[1] 3330 3331 3332 3333 3334 3335
> list(mtr)
[[1]]
[,1] [,2] [,3]
[1,] 3330 3332 3334
[2,] 3331 3333 3335
factor型データの結合
もう一つ、factor型を結合したときの挙動を記しておきます。
> c(fct)
[1] 3 2 1
このように順序データとして結合されます。これが望ましいケースもあるので、回避策をするか否かはケースバイケースでご判断ください。
# ラベルのほうが残るが、Levels等の属性情報は失われるため、
# 結合後は単なる文字列ベクトルとして扱いたい場合に有効。
> c(as.vector(fct))
[1] "Z" "Y" "X"
# すべての属性情報を保持できるため、
# 結合後もfactorとして扱いたい場合には有効。
> list(fct)
[[1]]
[1] Z Y X
Levels: X Y Z
names属性について
ここまでは属性情報は切り捨てられるというお話で進めてきましたが、例外的にnames
属性については維持されるようです。
> vec2 <- vec
> names(vec2) <- c("P", "Q")
> c(vec, vec2)
P Q
"p" "q" "p" "q"
ベクトル結合の注意点
ベクトルを結合する際に、返り値がリストの場合、ベクトルの各要素がリストの各要素に変換されてしまいます。
> c(vec, df)
[[1]]
[1] "p"
[[2]]
[1] "q"
$d
[1] 888 999
これを回避したい場合は、list()
による結合か、一度ベクトルをlist型
データに格納することをおすすめします。
> list(vec, df)
[[1]]
[1] "p" "q"
[[2]]
d
1 888
2 999
> c(list(vec), df)
[[1]]
[1] "p" "q"
$d
[1] 888 999
# data.frameも保持したい場合は以下のようにする
> c(list(vec), list(df))
[[1]]
[1] "p" "q"
[[2]]
d
1 888
2 999
オプションについて
c()
にはいくつかのオプションが引数で設定できます。
引数 | 説明 | 規定値 |
---|---|---|
recursive |
vector型 に強制変換するか |
FALSE |
use.names |
names 属性を保持するか |
TRUE |
recursive
data.frame型
との結合のように返り値が通常list型
になる場合に、強制的にvector型
に変換するかどうかを設定できます。
#
c(df, char)
> c(df, char)
$d
[1] 888 999
[[2]]
[1] "moji"
> c(df, char, recursive = T)
d1 d2
"888" "999" "moji"
use.names
名前の通り、names属性を保持するか否かを選択できます。
> c(vec, vec2)
P Q
"p" "q" "p" "q"
> c(vec, vec2, use.names = F)
[1] "p" "q" "p" "q"
おわりに
基本的な関数ながら意外と万能で奥深い?c()
でした。
とはいえ普段はそんなに気にせず使ってもいいと思いますが、たまにトラブルが起きるかもしれませんので、その時はまた見返してもらえたら嬉しいですね。
Enjoy!
おしまい。