2.2 数据结构
R拥有许多用于存储数据的对象类型,包括标量、向量、矩阵、数组、数据框和列表。
2.2.1 向量
向量是存储数值型、字符型、逻辑型数据的一维数组。执行组合功能的函数c()可以创建向量。
> a <- c(1, 2, 5, 3, 6, -2, 4)
> b <- c("one", "two", "three")
> c <- c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE)
单个向量中的数据必须具有相同的数据类型和模式。
标量是只含一个元素的向量。如:
> f <- 3
> g <- "US"
> h <- TRUE
访问向量中的元素
> a <- c(1, 2, 5, 3, 6, -2, 4)
> a[3]
[1] 5
> a[c(1,3,5)]
[1] 1 5 6
> a[2:6]
[1] 2 5 3 6 -2
2.2.2 矩阵
矩阵是一个二维数组,每个元素都具有相同的数据类型和模式。可通过函数matrix()创建矩阵。
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
dimnames = NULL)
创建矩阵
> y <- matrix(1:20, nrow=5, ncol=4)
> y
[,1] [,2] [,3] [,4]
[1,] 1 6 11 16
[2,] 2 7 12 17
[3,] 3 8 13 18
[4,] 4 9 14 19
[5,] 5 10 15 20
> cells <- c(1, 26, 24, 68)
> rnames <- c("R1", "R2")
> cnames <- c("C1", "C2")
> mymatrix <- matrix(cells, nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(rnames, cnames))
> mymatrix
C1 C2
R1 1 26
R2 24 68
矩阵下标的使用
> x <- matrix(1:10, nrow=2)
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 7 9
[2,] 2 4 6 8 10
> x[2,]
[1] 2 4 6 8 10
> x[,2]
[1] 3 4
> x[1, 4]
[1] 7
> x[1, c(4, 5)]
[1] 7 9
2.2.3 数组
数组与矩阵类似,但是维度可以大于2。数组可以通过array函数创建,形式如下:
array(data = NA, dim = length(data), dimnames = NULL)
创建一个2×3×4的数组
> dim1 <- c("A1", "A2")
> dim2 <- c("B1", "B2", "B3")
> dim3 <- c("C1", "C2", "C3", "C4")
> Z <- array(1:24, c(2, 3, 4), dimnames = list(dim1, dim2, dim3))
> Z
, , C1
B1 B2 B3
A1 1 3 5
A2 2 4 6
, , C2
B1 B2 B3
A1 7 9 11
A2 8 10 12
, , C3
B1 B2 B3
A1 13 15 17
A2 14 16 18
, , C4
B1 B2 B3
A1 19 21 23
A2 20 22 24
2.2.4 数据框
数据框不同的列可以包含不同模式(数值型,字符型)的数据,较矩阵来说更为一般。
数据框可以通过函数data.frame()创建
mydata <- data.frame(col1, col2, col3, ...)
其中列向量可以为任意类型,每一列的名称可由函数names指定。
创建一个数据框
> patientID <- c(1, 2, 3, 4)
> age <- c(25, 34, 28, 52)
> diabetes <- c("Type1", "Type2", "Type1", "Type1")
> status <- c("Poor", "Improved", "Excellent", "Poor")
> patientdata <- data.frame(patientID, age, diabetes, status)
> patientdata
patientID age diabetes status
1 1 25 Type1 Poor
2 2 34 Type2 Improved
3 3 28 Type1 Excellent
4 4 52 Type1 Poor
选取数据框中的元素
> patientdata[1:2]
patientID age
1 1 25
2 2 34
3 3 28
4 4 52
> patientdata[c("diabetes", "status")]
diabetes status
1 Type1 Poor
2 Type2 Improved
3 Type1 Excellent
4 Type1 Poor
> patientdata$age
[1] 25 34 28 52
第三个例子中的$用来选取给定数据框中的某个特定变量。
在每个变量名前都键入一次patientdata$可能会让人生厌,可使用函数attach()和detach()或单独使用函数with()来简化代码。
1.attach()、 detach()和with()
函数attach()将数据框添加到R的搜索路径中。R在遇到一个变量名后,将检查搜索路径中的数据框,以定位到这个变量。
以mtcars数据为例
> summary(mtcars$mpg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.40 15.43 19.20 20.09 22.80 33.90
> plot(mtcars$mpg, mtcars$disp)
> plot(mtcars$mpg, mtcars$wt)
上面的代码也可以写成
> attach(mtcars)
> summary(mpg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.40 15.43 19.20 20.09 22.80 33.90
> plot(mpg,disp)
> plot(mpg,wt)
> detach(mtcars)
detach()将数据框从搜索路径中移除。
当存在多个具有相同名称的对象时,这种方法的局限性就比较明显了。
除此之外,另一种方式是使用函数with()。
> with(mtcars, {
summary(mpg, disp, wt)
plot(mpg, wt)
plot(mpg, disp)})
在这种情况下,大括号{}之间的语句都针对数据框mtcars执行,这样就无须担心名称冲突了。如果仅有一条语句(例如summary(mpg)),那么大括号{}可以省略。
函数with()的局限性在于,赋值仅在此函数的括号内生效。考虑以下代码:
> with(mtcars, {
+ stats <- summary(mpg)
+ stats})
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.40 15.43 19.20 20.09 22.80 33.90
> stats
Error: object 'stats' not found
如果你需要创建在with()结构以外存在的对象,使用特殊赋值符<<-替代标准赋值符(<-)即可,它可将对象保存到with()之外的全局环境中。这一点可通过以下代码阐明:
> with(mtcars, {
+ nokeepstats <- summary(mpg)
+ keepstats <<- summary(mpg)
+ })
> nokeepstats
Error: object 'nokeepstats' not found
> keepstats
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.40 15.43 19.20 20.09 22.80 33.90
2.实例标识符
在病例数据中,病人编号(patientID)用于区分数据集中不同的个体。在R中,实例标识符(case identifier)可通过数据框操作函数中的rowname选项指定。例如,语句:
> patientdata <- data.frame(patientID, age, diabetes, status, row.names = patientID)
将patientID指定为R中标记各类打印输出和图形中实例名称所用的变量。
2.2.5 因子(factor)
如你所见,变量可归结为名义型、有序型或连续型变量。名义型变量是没有顺序之分的类别变量。糖尿病类型Diabetes(Type1、Type2)是名义型变量的一例。即使在数据中Type1编码为1而Type2编码为2,这也并不意味着二者是有序的。有序型变量表示一种顺序关系,而非数量关系。病情Status(poor, improved, excellent)是顺序型变量的一个上佳示例。我们明白,病情为poor(较差)病人的状态不如improved(病情好转)的病人,但并不知道相差多少。连续型变量可以呈现为某个范围内的任意值,并同时表示了顺序和数量。年龄Age就是一个连续型变量,它能够表示像14.5或22.8这样的值以及其间的其他任意值。很清楚,15岁的人比14岁的人年长一岁。
类别(名义型)变量和有序类别(有序型)变量在R中称为因子(factor)。因子在R中非常重要,因为它决定了数据的分析方式以及如何进行视觉呈现。你将在本书中通篇看到这样的例子。
函数factor()以一个整数向量的形式存储类别值,整数的取值范围是[1... k ](其中k 是名义型变量中唯一值的个数),同时一个由字符串(原始值)组成的内部向量将映射到这些整数上。
> diabetes <- factor(diabetes)
> status <- factor(status, ordered = TRUE)
> patientdata <- data.frame(patientID, age, diabetes, status)
> str(patientdata)
'data.frame': 4 obs. of 4 variables:
$ patientID: num 1 2 3 4
$ age : num 25 34 28 52
$ diabetes : Factor w/ 2 levels "Type1","Type2": 1 2 1 1
$ status : Ord.factor w/ 3 levels "Excellent"<"Improved"<..: 3 2 1 3
> summary(patientdata)
patientID age diabetes status
Min. :1.00 Min. :25.00 Type1:3 Excellent:1
1st Qu.:1.75 1st Qu.:27.25 Type2:1 Improved :1
Median :2.50 Median :31.00 Poor :2
Mean :2.50 Mean :34.75
3rd Qu.:3.25 3rd Qu.:38.50
Max. :4.00 Max. :52.00
对于字符型向量,因子的水平默认依字母顺序创建。这对于因子status是有意义的,因为“Excellent”、“Improved”、“Poor”的排序方式恰好与逻辑顺序相一致。如果“Poor”被编码为“Ailing”,会有问题,因为顺序将为“Ailing”、“Excellent”、“Improved”。如果理想中的顺序是“Poor”、“Improved”、“Excellent”,则会出现类似的问题。按默认的字母顺序排序的因子很少能够让人满意。你可以通过指定levels选项来覆盖默认排序。
> status <- factor(status, order=TRUE, levels = c("Poor", "Improved", "Excellent"))
2.2.6 列表
列表(list)是R的数据类型中最为复杂的一种。一般来说,列表就是一些对象(或成分,component)的有序集合。列表允许你整合若干(可能无关的)对象到单个对象名下。例如,某个列表中可能是若干向量、矩阵、数据框,甚至其他列表的组合。可以使用函数list()创建列表。
创建一个列表
> g <- "My First List"
> h <- c(25, 26, 18, 39)
> j <- matrix(1:10, nrow=5)
> k <- c("one", "two", "three")
> mylist <- list(title=g, ages=h, j , k)
> mylist
$`title`
[1] "My First List"
$ages
[1] 25 26 18 39
[[3]]
[,1] [,2]
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
[[4]]
[1] "one" "two" "three"
> mylist[[2]]
[1] 25 26 18 39
> mylist[["ages"]]
[1] 25 26 18 39
可以通过在双重方括号中指明代表某个成分的数字或名称来访问列表中的元素。此例中,mylist[[2]]和mylist[["ages"]]均指那个含有四个元素的向量.
2.3 数据的输入
2.3.1 使用键盘输入数据
R中的函数edit()会自动调用一个允许手动输入数据的文本编辑器。
具体步骤如下:
(1) 创建一个空数据框(或矩阵),其中变量名和变量的模式需与理想中的最终数据集一致;
(2) 针对这个数据对象调用文本编辑器,输入你的数据,并将结果保存回此数据对象中。
> mydata <- data.frame(age=numeric(0), gender=character(0), weight=numeric(0))
> mydata <- edit(mydata)
类似于age=numeric(0)的赋值语句将创建一个指定模式但不含实际数据的变量。注意,编辑的结果需要赋值回对象本身。函数edit()事实上是在对象的一个副本上进行操作的。如果你不将其赋值到一个目标,你的所有修改将会全部丢失!
2.3.2 从带分隔符的文本文件导入数据
使用read.table()从带分隔符的文本文件中导入数据。
read.table(file, header = FALSE, sep = "", quote = "\"'",
dec = ".", numerals = c("allow.loss", "warn.loss", "no.loss"),
row.names, col.names, as.is = !stringsAsFactors,
na.strings = "NA", colClasses = NA, nrows = -1,
skip = 0, check.names = TRUE, fill = !blank.lines.skip,
strip.white = FALSE, blank.lines.skip = TRUE,
comment.char = "#",
allowEscapes = FALSE, flush = FALSE,
stringsAsFactors = default.stringsAsFactors(),
fileEncoding = "", encoding = "unknown", text, skipNul = FALSE)
其中,file是一个带分隔符的ASCII文本文件,header是一个表明首行是否包含了变量名的逻辑值(TRUE或FALSE),sep用来指定分隔数据的分隔符,row.names是一个可选参数,用以指定一个或多个表示行标识符的变量。
2.3.3 导入EXCEL数据
读取一个Excel文件的最好方式,就是在Excel中将其导出为一个逗号分隔文件(csv),并使用前文描述的方式将其导入R中。在Windows系统中,你也可以使用RODBC包来访问Excel文件。
电子表格的第一行应当包含变量/列的名称。
> intall.packages("RODBC")
> library(RODBC)
> channel <- odbcConnectExcel("myfile.xls")
> mydataframe <- sqlFetch(channel, "mysheet")
> odbcClose(channel)
这里的myfile.xls是一个Excel文件,mysheet是要从这个工作簿中读取工作表的名称,channel是一个由odbcConnectExcel()返回的RODBC连接对象,mydataframe是返回的数据框。RODBC也可用于从Microsoft Access导入数据.
2.3.11 访问数据库管理接口
R中有多种面向关系型数据库管理系统(DBMS)的接口,包括Microsoft SQL Server、MicrosoftAccess、MySQL、Oracle、PostgreSQL、DB2、Sybase、Teradata以及SQLite。其中一些包通过原生的数据库驱动来提供访问功能,另一些则是通过ODBC或JDBC来实现访问的。使用R来访问存
储在外部数据库中的数据是一种分析大数据集的有效手段,并且能够发挥SQL和R各自的优势。
-
ODBC接口
在R中通过RODBC包访问一个数据库也许是最流行的方式,这种方式允许R连接到任意一种拥有ODBC驱动的数据库,其实几乎就是市面上的所有数据库。
第一步是针对你的系统和数据库类型安装和配置合适的ODBC驱动——它们并不是R的一部分。如果你的机器尚未安装必要的驱动,上网搜索一下应该就可以找到。
针对选择的数据库安装并配置好驱动后, 请安装RODBC 包。你可以使用命令install.packages("RODBC")来安装它。
RODBC包中的主要函数列于表2-2中。