7 Basic: type conversion (optional)
转换包括两种,element type 转换和 structure type 转换。
为什么需要转换?是因为在使用某个 function 处理某个 object 的时候,这个对象并不是 function 所要求提供的 element type 或 structure type。所以需要转换才能让 function 顺利执行。
7.1 Element type conversion
7.1.1 Coder initialized conversion
当需要转换时,用户可以主动触发转换。不同的 element type 之的转换,可以使用as.fun()族,如下:
function | 
用途 | 
|---|---|
as.logical() | 
转换为 logical 类型 | 
as.integer() | 
转换为 integer 类型 | 
as.double() | 
转换为 double 类型 | 
as.character() | 
转换为 character 类型 | 
如果转换的对象是 integer 或 double,那么该对象中的 0 会被转换为FALSE,所有非 0 的元素会被转换为TRUE,除此之外的任何对象都会被转换为NA例如:
var_int#> [1]  1  6 10
var_dbl#> [1] 0.0 1.0 2.5 4.5
var_chr#> [1] "these are"    "some strings"
var_na#> [1] NA NA NA
var_null#> NULL
as.logical(var_int)#> [1] TRUE TRUE TRUE
as.logical(var_dbl)#> [1] FALSE  TRUE  TRUE  TRUE
as.logical(var_chr)#> [1] NA NA
as.logical(var_na)#> [1] NA NA NA
as.logical(var_null)#> logical(0)
- 如果被转换的对象是 double,会把所有元素向 0 取整;
 - 如果转换的对象是纯数字 character,会把该字符串先转换为 double,然后再按前一条规则转换;
 - 如果被转换对象是 logical,则会把
TRUE转换成1L,把FALSE转换成0L。 
例如:
var_lgl#> [1]  TRUE FALSE
var_int#> [1]  1  6 10
var_chr#> [1] "these are"    "some strings"
var_na#> [1] NA NA NA
var_null#> NULL
as.integer(var_lgl)#> [1] 1 0
as.integer(var_dbl)#> [1] 0 1 2 4
as.integer(var_chr)#> Warning: NAs introduced by coercion
#> [1] NA NA
as.integer(var_na)#> [1] NA NA NA
as.integer(var_null)#> integer(0)
as.integer("10.1")#> [1] 10
- 
as.double()与as.integer()类似,如果转换的对象是纯数字 character,会把该字符串转换为 double,例如: 
var_lgl#> [1]  TRUE FALSE
var_int#> [1]  1  6 10
var_chr#> [1] "these are"    "some strings"
var_na#> [1] NA NA NA
var_null#> NULL
as.double(var_lgl)#> [1] 1 0
as.double(var_int)#> [1]  1  6 10
as.double(var_chr)#> Warning: NAs introduced by coercion
#> [1] NA NA
as.double(var_na)#> [1] NA NA NA
as.double(var_null)#> numeric(0)
as.double("3.141592653")#> [1] 3.141593
注:as.double()与as.numeric()等价。
var_lgl#> [1]  TRUE FALSE
var_int#> [1]  1  6 10
var_dbl#> [1] 0.0 1.0 2.5 4.5
var_na#> [1] NA NA NA
var_null#> NULL
as.character(var_lgl)#> [1] "TRUE"  "FALSE"
as.character(var_int)#> [1] "1"  "6"  "10"
as.character(var_dbl)#> [1] "0"   "1"   "2.5" "4.5"
as.character(var_na)#> [1] NA NA NA
as.character(var_null)#> character(0)
几种特殊的 element type 如NA、NULL、NaN中只有NULL有对应的转换function as.null(name),不过较少使用,等价于name <- NULL。余下的特殊 element type 也都可以通过<-直接实现转换,例如:
a <- NA
b <- NaN7.1.2 Automatic conversion by R: coercion
被动转换,或强制转换(coercion)实际上就是 R 在一些必要的情况,帮我们自动完成了 element type 转换。那什么时候是必要的情况?如果使用c()合并element type 不同的 scalars 来创建一个新的 vector,那么就会一定会发生 coercion,因为在 R 中,vector 内所有 element 必须是相同 type。
coercion 遵循的优先级为 expression > character > double > integer > logical(包括NA)> raw。例如
typeof(c("a", expression(1 + 2)))#> [1] "expression"
typeof(c(1, expression(1 + 2)))#> [1] "expression"
typeof(c(1L, expression(1 + 2)))#> [1] "expression"
typeof(c(NA, expression(1 + 2)))#> [1] "expression"
typeof(c(raw(2), expression(1 + 2)))#> [1] "expression"
#> [1] "character"
#> [1] "character"
#> [1] "character"
#> [1] "character"
#> [1] "character"
#> [1] "double"
#> [1] "double"
#> [1] "double"
#> [1] "double"
#> [1] "integer"
#> [1] "integer"
#> [1] "integer"
#> [1] "logical"
#> [1] "logical"
coercion 的规则中不包括NULL,因为NULL本身没有任何内容,NULL在一个向量的任何位置都意味着这个向量没有这个位置。例如:
c(NULL, "a", NA)#> [1] "a" NA
c("a", NA, NULL)#> [1] "a" NA
c("a", NULL, NA)#> [1] "a" NA
coercion 容易在不经意间造成一些问题,例如,假设有两批数据,分别读入到 R 中,需要合并在一起计算均值。但其中一批是 character,导致合并时发生 coercion,求均值出错,代码为:
a <- matrix(1, 3, 3)
a#>      [,1] [,2] [,3]
#> [1,]    1    1    1
#> [2,]    1    1    1
#> [3,]    1    1    1
#>      [,1]
#> [1,] "1" 
#> [2,] "1" 
#> [3,] "1"
ab <- cbind(a, b)
ab#>      [,1] [,2] [,3] [,4]
#> [1,] "1"  "1"  "1"  "1" 
#> [2,] "1"  "1"  "1"  "1" 
#> [3,] "1"  "1"  "1"  "1"
mean(ab)#> Warning in mean.default(ab): argument is not numeric or
#> logical: returning NA
#> [1] NA
以上由 coercion 导致的错误通常都会表现为某个 function 报错,提示给这个 function 提供的某个 argument 的 element type 不符合要求。
coercion 不仅发生在使用c()将不同类型的 scalars 合并成 vector 的情况,还会发生在各种各样的运算中。例如,在需要 numeric object,如果提供的是 logical object,那么 R 就会将 logical object 转换成 integer,其中TRUE会被自动转换为1L,FALSE会被自动转换为0L;
1*TRUE  # 乘法运算需要 numeric value,但提供的是 TRUE,执行时被自动转换成 1L
#> [1] 1
1 + FALSE  # 同理,FALSE 在执行时会被自动转换成 0L
#> [1] 1活用 coercion 可以写出一些简便的代码,例如:
score <- sample(100, 60)
score#>  [1]  85  86  99  14  60  70  30  62  97  29  31  75  40  73
#> [15]  72  82  17  74  81  25  56  12  67  20  63 100  58  33
#> [29]   2  41  52  71  28  34  57  78  49  51  35  87  24  36
#> [43]  69  37  84  90  54  98  32  79  42  45  59  44  77  83
#> [57]  89   3  47  64
sum(score > 90)#> [1] 4
7.1.3 Frequently made mistake
因为 element type 并不匹配导致的 error 或 warning,多数情况下是由 coercion 导致的,这种问题通常表现为某个 function 弹出 error 或 warning,但问题的根源在使用这个 function 之前,所需 argument 的 element type 就是错的;
cor("1", "2")#> Error in cor("1", "2"): 'x' must be numeric
#> Warning in mean.default(c("1", "2")): argument is not
#> numeric or logical: returning NA
#> [1] NA
7.2 Structure type conversion
同样,不同的结构类型也可以使用as.fun()族转换,例如:
function | 
用途 | 
|---|---|
as.vector() | 
转换为 vector 类型 | 
as.matrix() | 
转换为 matrix 类型 | 
as.data.frame() | 
转换为 data.frame 类型 | 
as.list() | 
转换为 list 类型 | 
as.factor() | 
转换为 factor 类型 | 
但是,也不建议盲目使用,因为这些 function 的结果会比较难预判,例如:
- 转换对象是 vector:
 
var_dbl#> [1] 0.0 1.0 2.5 4.5
as.matrix(var_dbl)#>      [,1]
#> [1,]  0.0
#> [2,]  1.0
#> [3,]  2.5
#> [4,]  4.5
as.data.frame(var_dbl)
as.list(var_dbl)#> [[1]]
#> [1] 0
#> 
#> [[2]]
#> [1] 1
#> 
#> [[3]]
#> [1] 2.5
#> 
#> [[4]]
#> [1] 4.5
as.factor(var_dbl)#> [1] 0   1   2.5 4.5
#> Levels: 0 1 2.5 4.5
as.array(var_dbl)#> [1] 0.0 1.0 2.5 4.5
- 转换对象是 data.frame:
 
as.matrix(df1)#>      x   y  
#> [1,] "1" "a"
#> [2,] "2" "b"
#> [3,] "3" "c"
as.vector(df1)#> $x
#> [1] 1 2 3
#> 
#> $y
#> [1] "a" "b" "c"
as.list(df1)#> $x
#> [1] 1 2 3
#> 
#> $y
#> [1] "a" "b" "c"
as.factor(df1)#> Error in xtfrm.data.frame(x): cannot xtfrm data frames
注意,在低于 4.2.0 版本的 R 中,as.vector(x)如果x是 data.frame,直接返回x;在大于 4.2.0 版本的 R 中,返回一个 list,这更符合几种 structure types 间的关系,即 data.frame 本质是 list,而 list 本质是 vector。
df <- data.frame(x = c(1, 2, 3), y = c(4, 5, 6))
as.vector(df)R<4.2.0
R>=4.2.0
- 转换的的对象是 list:
 
str(l1)#> List of 7
#>  $ :List of 3
#>   ..$ : num 1
#>   ..$ : num 2
#>   ..$ : num 3
#>  $ : int [1:3] 1 2 3
#>  $ : chr "a"
#>  $ : logi [1:3] TRUE FALSE TRUE
#>  $ : num 2.3
#>  $ : logi [1:3] NA NA NA
#>  $ : num [1:2, 1:2] 1 2 3 4
as.vector(l1)#> [[1]]
#> [[1]][[1]]
#> [1] 1
#> 
#> [[1]][[2]]
#> [1] 2
#> 
#> [[1]][[3]]
#> [1] 3
#> 
#> 
#> [[2]]
#> [1] 1 2 3
#> 
#> [[3]]
#> [1] "a"
#> 
#> [[4]]
#> [1]  TRUE FALSE  TRUE
#> 
#> [[5]]
#> [1] 2.3
#> 
#> [[6]]
#> [1] NA NA NA
#> 
#> [[7]]
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
as.matrix(l1)#>      [,1]     
#> [1,] list,3   
#> [2,] integer,3
#> [3,] "a"      
#> [4,] logical,3
#> [5,] 2.3      
#> [6,] logical,3
#> [7,] numeric,4
as.array(l1)#> [[1]]
#> [[1]][[1]]
#> [1] 1
#> 
#> [[1]][[2]]
#> [1] 2
#> 
#> [[1]][[3]]
#> [1] 3
#> 
#> 
#> [[2]]
#> [1] 1 2 3
#> 
#> [[3]]
#> [1] "a"
#> 
#> [[4]]
#> [1]  TRUE FALSE  TRUE
#> 
#> [[5]]
#> [1] 2.3
#> 
#> [[6]]
#> [1] NA NA NA
#> 
#> [[7]]
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
- 转换的对象是 array:
 
a1#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    1    2
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]    3    4
#> 
#> , , 3
#> 
#>      [,1] [,2]
#> [1,]    5    6
as.vector(a1)#> [1] 1 2 3 4 5 6
as.matrix(a1)#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3
#> [4,]    4
#> [5,]    5
#> [6,]    6
as.data.frame(a1)
as.list(a1)#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] 3
#> 
#> [[4]]
#> [1] 4
#> 
#> [[5]]
#> [1] 5
#> 
#> [[6]]
#> [1] 6
as.factor(a1)#> [1] 1 2 3 4 5 6
#> Levels: 1 2 3 4 5 6
实际上,之前提到的创建 matrix 和 array 时使用的matrix()和array(),本质上是将 vector 转换为 matrix 和 array,如:
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    1    2
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]    3    4
#> 
#> , , 3
#> 
#>      [,1] [,2]
#> [1,]    5    6
如果涉及到转换目标是 matrix、array,且转换前后 object 维数不同(如 atomic vector 转 matrix,\(2\times3\) matrix 转\(1\times2\times3\) array,等等)时,请尽量使用上述两个 functions。
7.2.1 Conversion between matrix and data frame
matrix 和 data.frame 因为都是行 + 列的呈现形式,所以这两种 structures 互相转换不会改变原有结构。此外,由于大多数读取数据的 function 会自动讲读取的数据存成一个 data.frame,所以使用较为频繁的场景是 data.frame 转 matrix。
str(iris)#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
is.data.frame(iris)#> [1] TRUE
#>  chr [1:150, 1:5] "5.1" "4.9" "4.7" "4.6" "5.0" "5.4" ...
#>  - attr(*, "dimnames")=List of 2
#>   ..$ : NULL
#>   ..$ : chr [1:5] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" ...
7.2.2 By row and recycling rule
按列转换。当转换前后 object 的维数不同时,数据将会按照列转换,例如:
1 维转 2 维:
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
2 维转 1 维:
#> [1] 1 3 2 4
- 回收法则(recycling rule)。当转换的 vector 和被转换的 vector 长度不一致时,绝大多数情况下, R 会自动把短的 vector 扩展成和长的 vector 一样长度,相当于把短 vector 回收(recycle)重复利用。例如:
 
#>      [,1] [,2]
#> [1,]    1    1
#> [2,]    2    2
当长 vector 的长度并不是短 vector 的长度的整数倍时, R 依然会执行 recycling rule,但是会给出一个 warning,例如:
#> Warning in matrix(a, nrow = 4, ncol = 4): data length [3]
#> is not a sub-multiple or multiple of the number of rows [4]
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    2    3    1
#> [2,]    2    3    1    2
#> [3,]    3    1    2    3
#> [4,]    1    2    3    1
然而,如果是要将不同长度的 vector 作为 data.frame 的元素时,非整数倍的差异会直接导致报错,
data.frame(x = c(1, 2), y = c(1, 2, 3))#> Error in data.frame(x = c(1, 2), y = c(1, 2, 3)): arguments imply differing number of rows: 2, 3
Recycling rule 不仅限于转换数据的情况,任何涉及到需要操作的多个对象长度必须一致的情况,该法则都会发挥作用,例如:
#> [1]  2  8  6 16
a + 1#> [1] 2 3 4 5
按列转换和recycling rule这两个细节是 R 很重要的特点。在编程过程中如果能够灵活运用这两点,可以使用简短的代码达到一样的效果。在后续章节的学习过程中,还会讲到这两点在解决实际问题中的运用。
除此之外,还有很多as.fun(),在使用 Rstudio 时,可以通过输入as.并借助自动补全(auto completion)功能查看。
7.3 Recap
- 转换元素类型时,请确认转换的 object 是 atomic vector;
 - logical 和 integer 之间相互转换时,
TRUE对应1L,FALSE对应0L; - double 转 logical 时,非 
0对应TRUE,0对应FALSE; - double 转 integer 时,会把所有 elements 向下取整;
 - 只包含数字的 character 可以直接转为 double;
 - 只包含数字的 character 转为 integer 时会先转为 double,再由 double 转为 integer;
 - matrix 和 data.frame 因为呈现方式上的一致性,可以无障碍互相转换,所以
as.matrix()和as.data.frame()的使用较多; - 记住按列转换和recycling rule,尤其是后者。