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 <- NaN
7.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"
typeof(c("a", 1))
#> [1] "character"
typeof(c("a", 1L))
#> [1] "character"
typeof(c("a", TRUE))
#> [1] "character"
typeof(c("a", NA))
#> [1] "character"
typeof(c("a", raw(2)))
#> [1] "character"
typeof(c(1, 1L))
#> [1] "double"
typeof(c(1, TRUE))
#> [1] "double"
typeof(c(1, NA))
#> [1] "double"
typeof(c(1, raw(2)))
#> [1] "double"
typeof(c(1L, TRUE))
#> [1] "integer"
typeof(c(1L, NA))
#> [1] "integer"
typeof(c(1L, raw(2)))
#> [1] "integer"
typeof(c(TRUE, raw(2)))
#> [1] "logical"
typeof(c(NA, raw(2)))
#> [1] "logical"
coercion 的规则中不包括NULL
,因为NULL
本身没有任何内容,NULL
在一个向量的任何位置都意味着这个向量没有这个位置。例如:
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
b <- matrix(c("1", "1", "1"), 3, 1)
b
#> [,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 可以写出一些简便的代码,例如:
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)
#> var_dbl
#> 1 0.0
#> 2 1.0
#> 3 2.5
#> 4 4.5
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)
#> V1 V2 V3 V4 V5 V6
#> 1 1 2 3 4 5 6
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,如:
matrix(data = c(1, 2, 3, 4), nrow = 2, ncol = 2)
#> [,1] [,2]
#> [1,] 1 3
#> [2,] 2 4
array(data = c(1, 2, 3, 4, 5, 6), dim = c(1, 2, 3))
#> , , 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
str(as.matrix(iris))
#> 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 维:
2 维转 1 维:
- 回收法则(recycling rule)。当转换的 vector 和被转换的 vector 长度不一致时,绝大多数情况下, R 会自动把短的 vector 扩展成和长的 vector 一样长度,相当于把短 vector 回收(recycle)重复利用。例如:
当长 vector 的长度并不是短 vector 的长度的整数倍时, R 依然会执行 recycling rule,但是会给出一个 warning,例如:
a <- c(1, 2, 3)
matrix(a, nrow = 4, ncol = 4)
#> 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 不仅限于转换数据的情况,任何涉及到需要操作的多个对象长度必须一致的情况,该法则都会发挥作用,例如:
按列转换和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,尤其是后者。