R语言的数据集

Published on 2017 - 04 - 10

数据集的概念

数据集通常是由数据构成的一个矩形数组,行表示观测,列表示变量。表1提供了一个假想的病例数据集。

病人编号(PatientID) 入院时间(AdmDate) 年龄(Age) 糖尿病类型(Diabetes) 病情(Status)
1 10/15/2009 25 Type1 Poor
2 11/01/2009 34 Type2 Improved
3 10/21/2009 28 Type1 Excellent
4 10/28/2009 52 Type1 Poor

不同的行业对于数据集的行和列叫法不同。统计学家称它们为观测(observation)和变量(variable),数据库分析师则称其为记录(record)和字段(field),数据挖掘/机器学习学科的研究者则把它们叫做示例(example)和属性(attribute)。我们在通篇使用术语观测和变量

你可以清楚地看到此数据集的结构(本例中是一个矩形数组)以及其中包含的内容和数据类型。表1所示的数据集中,PatientID是行/实例标识符,AdmDate是日期型变量,Age是连续型变量,Diabetes是名义型变量,Status是有序型变量。

R中有许多用于存储数据的结构,包括标量、向量、数组、数据框和列表。表1实际上对应着R中的一个数据框。多样化的数据结构赋予了R极其灵活的数据处理能力。

R可以处理的数据类型(模式)包括数值型、字符型、逻辑型(TRUE/FALSE)、复数型(虚数)和原生型(字节)。在R中,PatientID、AdmDate和Age将为数值型变量,而Diabetes和Status则为字符型变量。另外,你需要分别告诉R:PatientID是实例标识符,AdmDate含有日期数据,Diabetes和Status分别是名义型和有序型变量。R将实例标识符称为rownames(行名),将类别型(包括名义型和有序型)变量称为因子(factors)。

数据结构

R拥有许多用于存储数据的对象类型,包括标量、向量、矩阵、数组、数据框和列表。它们在存储数据的类型、创建方式、结构复杂度,以及用于定位和访问其中个别元素的标记等方面均有所不同。图2-1给出了这些数据结构的一个示意图。

让我们从向量开始,逐个探究每一种数据结构。
一些定义
R中有一些术语较为独特,可能会对新用户造成困扰。

在R中,对象(object)是指可以赋值给变量的任何事物,包括常量、数据结构、函数,甚至图形。对象都拥有某种模式,描述了此对象是如何存储的,以及某个类,像print这样的泛型函数表明如何处理此对象。

与其他标准统计软件(如SAS、SPSS和Stata)中的数据集类似,数据框(data frame)是R中用于存储数据的一种结构:列表示变量,行表示观测。在同一个数据框中可以存储不同类型(如数值型、字符型)的变量。数据框将是你用来存储数据集的主要数据结构。

因子(factor)是名义型变量或有序型变量。它们在R中被特殊地存储和处理。
其他多数术语你应该比较熟悉了,它们基本都遵循统计和计算中术语的定义。

[图1 R中的数据结构]

向量

向量是用于存储数值型、字符型或逻辑型数据的一维数组。执行组合功能的函数c()可用来创建向量。各类向量如下例所示:

a <- c(1, 2, 5, 3, 6, -2, 4)
b <- c("one", "two", "three")
c <- c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE)

这里,a是数值型向量,b是字符型向量,而c是逻辑型向量。注意,单个向量中的数据必须拥有相同的类型或模式(数值型、字符型或逻辑型)。同一向量中无法混杂不同模式的数据。

由于R中内置了同名函数c(),最好不要在编码时使用c作为对象名,否则可能产生一些不易察觉的问题。

注意 标量是只含一个元素的向量,例如f <- 3、g <- "US"和h <- TRUE。它们用于保存常量。

通过在方括号中给定元素所处位置的数值,我们可以访问向量中的元素。例如,a[c(2, 4)]用于访问向量a中的第二个和第四个元素。更多示例如下:

> 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

最后一个语句中使用的冒号用于生成一个数值序列。例如,a <- c(2:6)等价于a <- c(2, 3, 4, 5, 6)

矩阵

矩阵是一个二维数组,只是每个元素都拥有相同的模式(数值型、字符型或逻辑型)。可通过函数matrix创建矩阵。一般使用格式为:

myymatrix <- matrix(vector, nrow=number_of_rows, ncol=number_of_columns,
                    byrow=logical_value, dimnames=list(
                    char_vector_rownames, char_vector_colnames))

其中vector包含了矩阵的元素,nrow和ncol用以指定行和列的维数,dimnames包含了可选的、以字符型向量表示的行名和列名。选项byrow则表明矩阵应当按行填充(byrow=TRUE)还是按列填充(byrow=FALSE),默认情况下按列填充。代码清单1中的代码演示了matrix函数的 用法。

  > y <- matrix(1:20, nrow=5, ncol=4)         # ❶ 创建一个5×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)        # ❷ 按行填充的2×2矩阵
> 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
> mymatrix <- matrix(cells, nrow=2, ncol=2, byrow=FALSE,        # ❸ 按列填充的2×2矩阵
                     dimnames=list(rnames, cnames))
> mymatrix
  C1 C2
R1  1 24
R2 26 68

我们首先创建了一个5×4的矩阵❶,接着创建了一个2×2的含列名标签的矩阵,并按行进行填充❷,最后创建了一个2×2的矩阵并按列进行了填充❸。

我们可以使用下标和方括号来选择矩阵中的行、列或元素。X[i,]指矩阵X中的第i 行,X[,j]指第j 列,X[i, j]指第i 行第j 个元素。选择多行或多列时,下标i 和j 可为数值型向量,如代码清单2所示。

> 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

首先,我们创建了一个内容为数字1到10的2×5矩阵。默认情况下,矩阵按列填充。然后,我们分别选择了第二行和第二列的元素。接着,又选择了第一行第四列的元素。最后选择了位于第一行第四、第五列的元素。

矩阵都是二维的,和向量类似,矩阵中也仅能包含一种数据类型。当维度超过2时,不妨使用数组。当有多种模式的数据时,不妨使用数据框。

数组

数组(array)与矩阵类似,但是维度可以大于2。数组可通过array函数创建,形式如下:

myarray <- array(vector, dimensions, dimnames)

其中vector包含了数组中的数据,dimensions是一个数值型向量,给出了各个维度下标的最大值,而dimnames是可选的、各维度名称标签的列表。代码清单3给出了一个创建三维 (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

如你所见,数组是矩阵的一个自然推广。它们在编写新的统计方法时可能很有用。像矩阵一样,数组中的数据也只能拥有一种模式。

从数组中选取元素的方式与矩阵相同。上例中,元素z[1,2,3]为15。

数据框

由于不同的列可以包含不同模式(数值型、字符型等)的数据,数据框的概念较矩阵来说更为一般。它与你通常在SAS、SPSS和Stata中看到的数据集类似。数据框将是你在R中最常处理的数据结构。

表1所示的病例数据集包含了数值型和字符型数据。由于数据有多种模式,无法将此数据集放入一个矩阵。在这种情况下,使用数据框是最佳选择。

数据框可通过函数data.frame()创建:

mydata <- data.frame(col1, col2, col3,)

其中的列向量col1, col2, col3,… 可为任何类型(如字符型、数值型或逻辑型)。每一列的名称可由函数names指定。代码清单4清晰地展示了相应用法。

> 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

每一列数据的模式必须唯一,不过你却可以将多个模式的不同列放到一起组成数据框。由于数据框与分析人员通常设想的数据集的形态较为接近,我们在讨论数据框时将交替使用术语列和变量。

选取数据框中元素的方式有若干种。你可以使用前述(如矩阵中的)下标记号,亦可直接指定列名。代码清单5使用之前创建的patientdata数据框演示了这些方式。

> 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          # ❶ 表示patientdata数据框中的变量age
[1] 25 34 28 52

第三个例子中的记号$是新出现的❶。它被用来选取一个给定数据框中的某个特定变量。例如,如果你想生成糖尿病类型变量diabetes和病情变量status的列联表,使用以下代码即可:

> table(patientdata$diabetes, patientdata$status)

        Excellent Improved Poor
  Type1         1        0    2
  Type2         0        1    0

在每个变量名前都键入一次patientdata$可能会让人生厌,所以不妨走一些捷径。可以联合使用函数attach()和detach()或单独使用函数with()来简化代码。

attach()、detach()和with()

函数attach()可将数据框添加到R的搜索路径中。R在遇到一个变量名以后,将检查搜索路径中的数据框,以定位到这个变量。可以使用以下代码获取每加仑行驶英里数(mpg)变量的描述性统计量,并分别绘制此变量与发动机排量(disp)和车身重量(wt)的散点图:

summary(mtcars$mpg)
plot(mtcars$mpg, mtcars$disp)
plot(mtcars$mpg, mtcars$wt)

以上代码也可写成:

attach(mtcars)
  summary(mpg)
  plot(mpg, disp)
  plot(mpg, wt)
detach(mtcars)

函数detach()将数据框从搜索路径中移除。值得注意的是,detach()并不会对数据框本身做任何处理。这句是可以省略的,但其实它应当被例行地放入代码中,因为这是一个好的编程习惯。

当名称相同的对象不止一个时,这种方法的局限性就很明显了。考虑以下代码:

> mpg <- c(25, 36, 47)
> attach(mtcars)
 
The following object(s) are masked _by_ ‘.GlobalEnv’:    mpg
> plot(mpg, wt)
Error in xy.coords(x, y, xlabel, ylabel, log) :
  ‘x’ and ‘y’ lengths differ
> mpg
[1] 25 36 47

这里,在数据框mtcars被绑定(attach)之前,我们的环境中已经有了一个名为mpg的对象。在这种情况下,原始对象将取得优先权,这与你想要的结果有所出入。由于mpg中有3个元素而disp中有32个元素,故plot语句出错。函数attach()和detach()最好在你分析一个单独的数据框,并且不太可能有多个同名对象时使用。任何情况下,都要当心那些告知某个对象已被屏蔽(masked)的警告。

除此之外,另一种方式是使用函数with()。你可以这样重写上例:

with(mtcars, {
  summary(mpg, disp, wt)
  plot(mpg, disp)
  plot(mpg, wt)
})

在这种情况下,大括号{}之间的语句都针对数据框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

相对于attach(),多数的R书籍更推荐使用with()。个人认为从根本上说,选择哪一个是自己的偏好问题,并且应当根据你的目的和对于这两个函数含义的理解而定。本书中我们会交替使用这两个函数。

实例标识符

在病例数据中,病人编号(patientID)用于区分数据集中不同的个体。在R中,实例标识符(case identifier)可通过数据框操作函数中的rowname选项指定。例如,语句:

patientdata <- data.frame(patientID, age, diabetes, status,
  row.names=patientID)

将patientID指定为R中标记各类打印输出和图形中实例名称所用的变量。

因子

如你所见,变量可归结为名义型、有序型或连续型变量。名义型变量是没有顺序之分的类别变量。糖尿病类型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 <- c("Type1", "Type2", "Type1", "Type1")

语句diabetes <- factor(diabetes)将此向量存储为(1, 2, 1, 1),并在内部将其关联为1=Type1和2=Type2(具体赋值根据字母顺序而定)。针对向量diabetes进行的任何分析都会将其作为名义型变量对待,并自动选择适合这一测量尺度的统计方法。

要表示有序型变量,需要为函数factor()指定参数ordered=TRUE。给定向量:

status <- c("Poor", "Improved", "Excellent", "Poor")

语句status <- factor(status, ordered=TRUE)会将向量编码为(3, 2, 1, 3),并在内部将这些值关联为1=Excellent、2=Improved以及3=Poor。另外,针对此向量进行的任何分析都会将其作为有序型变量对待,并自动选择合适的统计方法。

对于字符型向量,因子的水平默认依字母顺序创建。这对于因子status是有意义的,因为“Excellent”、“Improved”、“Poor”的排序方式恰好与逻辑顺序相一致。如果“Poor”被编码为“Ailing”,会有问题,因为顺序将为“Ailing”、“Excellent”、“Improved”。如果理想中的顺序是“Poor”、“Improved”、“Excellent”,则会出现类似的问题。按默认的字母顺序排序的因子很少能够让人满意。

你可以通过指定levels选项来覆盖默认排序。例如:

status <- factor(status, order=TRUE,
                 levels=c("Poor", "Improved", "Excellent"))

各水平的赋值将为1=Poor、2=Improved、3=Excellent。请保证指定的水平与数据中的真实值相匹配,因为任何在数据中出现而未在参数中列举的数据都将被设为缺失值。

代码清单6演示了普通因子和有序因子的不同是如何影响数据分析的。

> patientID <- c(1, 2, 3, 4)        # ❶ 以向量形式输入数据
> age <- c(25, 34, 28, 52)
> diabetes <- c("Type1", "Type2", "Type1", "Type1")
> status <- c("Poor", "Improved", "Excellent", "Poor")
> diabetes <- factor(diabetes)
> status <- factor(status, order=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

首先,以向量的形式输入了数据❶。然后,将diabetes和status分别指定为一个普通因子和一个有序型因子。最后,将数据合并为一个数据框。函数str(object)可提供R中某个对象(本例中为数据框)的信息❷。它清楚地显示diabetes是一个因子,而status是一个有序型因子,以及此数据框在内部是如何进行编码的。注意,函数summary()会区别对待各个变量❸。它显示了连续型变量age的最小值、最大值、均值和各四分位数,并显示了类别型变量diabetes和status(各水平)的频数值。

列表

列表(list)是R的数据类型中最为复杂的一种。一般来说,列表就是一些对象(或成分,component)的有序集合。列表允许你整合若干(可能无关的)对象到单个对象名下。例如,某个列表中可能是若干向量、矩阵、数据框,甚至其他列表的组合。可以使用函数list()创建列表:

mylist object1, object2,)

其中的对象可以是目前为止讲到的任何结构。你还可以为列表中的对象命名:

mylist name1=object1, name2=object2,)

代码清单7展示了一个例子。

> 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"]]均指那个含有四个元素的向量。由于两个原因,列表成为了R中的重要数据结构。首先,列表允许以一种简单的方式组织和重新调用不相干的信息。其次,许多R函数的运行结果都是以列表的形式返回的。需要取出其中哪些成分由分析人员决定。你将在后续各章发现许多返回列表的函数示例。

提醒程序员注意的一些事项

经验丰富的程序员通常会发现R语言的某些方面不太寻常。以下是这门语言中你需要了解的一些特性。

  • 对象名称中的句点(.)没有特殊意义。但美元符号($)却有着和其他语言中的句点类似的含义,即指定一个对象中的某些部分。例如,A$x是指数据框A中的变量x。
  • R不提供多行注释或块注释功能。你必须以#作为多行注释每行的开始。出于调试目的,你也可以把想让解释器忽略的代码放到语句if(FALSE){... }中。将FALSE改为TRUE即允许这块代码执行。
  • 将一个值赋给某个向量、矩阵、数组或列表中一个不存在的元素时,R将自动扩展这个数据结构以容纳新值。举例来说,考虑以下代码:
> x <- c(8, 6, 4)
> x[7] <- 10
> x
[1]  8  6  4 NA NA NA 10

通过赋值,向量x由三个元素扩展到了七个元素。
x <- x[1:3]会重新将其缩减回三个元素。

  • R中没有标量。标量以单元素向量的形式出现。
  • R中的下标不从0开始,而从1开始。在上述向量中,x[1]的值为8。
  • 变量无法被声明。它们在首次被赋值时生成。

数据的输入

现在你已经掌握了各种数据结构,可以放一些数据进去了。作为一名数据分析人员,你通常会面对来自多种数据源和数据格式的数据,你的任务是将这些数据导入你的工具,分析数据,并汇报分析结果。R提供了适用范围广泛的数据导入工具。向R中导入数据的权威指南参见可在http://cran.r-project.org/doc/manuals/R-data.pdf下载的R Data Import/Export手册。

如图2所示,R可从键盘、文本文件、Microsoft Excel和Access、流行的统计软件、特殊格式的文件,以及多种关系型数据库中导入数据。由于我们无从得知你的数据将来自何处,故会在下文论及各种数据源。读者按需参阅即可。

使用键盘输入数据

也许输入数据最简单的方式就是使用键盘了。R中的函数edit()会自动调用一个允许手动输入数据的文本编辑器。具体步骤如下:

  1. 创建一个空数据框(或矩阵),其中变量名和变量的模式需与理想中的最终数据集一致;
  2. 针对这个数据对象调用文本编辑器,输入你的数据,并将结果保存回此数据对象中。

在下例中,你将创建一个名为mydata的数据框,它含有三个变量:age(数值型)、gender(字符型)和weight(数值型)。然后你将调用文本编辑器,键入数据,最后保存结果。

mydata <- data.frame(age=numeric(0),
  gender=character(0), weight=numeric(0))
mydata <- edit(mydata)

类似于age=numeric(0)的赋值语句将创建一个指定模式但不含实际数据的变量。注意,编辑的结果需要赋值回对象本身。函数edit()事实上是在对象的一个副本上进行操作的。如果你不将其赋值到一个目标,你的所有修改将会全部丢失!

在Windows上调用函数edit()的结果如图3所示。

如图3所示,我已经自主添加了一些数据。单击列的标题,你就可以用编辑器修改变量名和变量类型(数值型、字符型)。你还可以通过单击未使用列的标题来添加新的变量。编辑器关闭后,结果会保存到之前赋值的对象中(本例中为mydata)。再次调用mydata <- edit(mydata),就能够编辑已经输入的数据并添加新的数据。语句mydata <- edit(mydata)的一种简捷的等价写法是fix(mydata)。

这种输入数据的方式对于小数据集很有效。对于较大的数据集,你所期望的也许是我们接下来要介绍的方式:从现有的文本文件、Excel电子表格、统计软件或数据库中导入数据。

从带分隔符的文本文件导入数据

你可以使用read.table()从带分隔符的文本文件中导入数据。此函数可读入一个表格格式的文件并将其保存为一个数据框。其语法如下:

mydataframe file, header=logical_value,
  sep="delimiter", row.names="name")

其中,file是一个带分隔符的ASCII文本文件,header是一个表明首行是否包含了变量名的逻辑值(TRUE或FALSE),sep用来指定分隔数据的分隔符,row.names是一个可选参数,用以指定一个或多个表示行标识符的变量。

举个例子,语句:

grades <- read.table("studentgrades.csv", header=TRUE, sep=",",
  row.names="STUDENTID")

从当前工作目录中读入了一个名为studentgrades.csv的逗号分隔文件,从文件的第一行取得了各变量名称,将变量STUDENTID指定为行标识符,最后将结果保存到了名为grades的数据 框中。

请注意,参数sep允许你导入那些使用逗号以外的符号来分隔行内数据的文件。你可以使用sep="\t"读取以制表符分隔的文件。此参数的默认值为sep="",即表示分隔符可为一个或多个空格、制表符、换行符或回车符。

默认情况下,字符型变量将转换为因子。我们并不总是希望程序这样做(例如处理一个含有被调查者评论的变量时)。有许多方法可以禁止这种转换行为。其中包括设置选项stringsAsFactors=FALSE,这将停止对所有字符型变量的此种转换。另一种方法是使用选项colClasses为每一列指定一个类,例如logical(逻辑型)、numeric(数值型)、character(字符型)、factor(因子)。

函数read.table()还拥有许多微调数据导入方式的追加选项。更多详情,请参阅help(read.table)。

注意 本文中的许多示例都是从用户计算机上已经存在的文件中导入数据。R也提供了若干种通过连接(connection)来访问数据的机制。例如,函数file()、gzfile()、bzfile()、xzfile()、unz()和url()可作为文件名参数使用。函数file()允许用户访问文件、剪贴板和C级别的标准输入。函数gzfile()、bzfile()、xzfile()和unz()允许用户读取压缩文件。函数url()能够让你通过一个含有http://、ftp://或file://的完整URL访问网络上的文件,还可以为HTTP和FTP连接指定代理。为了方便,(用""围住的)完整的URL也经常直接用来代替文件名使用。更多详情,参见help(file)。

导入Excel数据

读取一个Excel文件的最好方式,就是在Excel中将其导出为一个逗号分隔文件(csv),并使用前文描述的方式将其导入R中。在Windows系统中,你也可以使用RODBC包来访问Excel文件。电子表格的第一行应当包含变量/列的名称。

首先,下载并安装RODBC包。

install.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导入数据。更多详情,参见help(RODBC)。

Excel 2007使用了一种名为XLSX的文件格式,实质上是多个XML文件组成的压缩包。xlsx包可以用来读取这种格式的电子表格。在第一次使用此包之前请务必先下载并安装好。包中的函数read.xlsx()可将XLSX文件中的工作表导入为一个数据框。其最简单的调用格式是read.xlsx(file, n),其中file是Excel 2007工作簿的所在路径,n则为要导入的工作表序号。举例说明,在Windows上,以下代码:

library(xlsx)
workbook <- "c:/myworkbook.xlsx"
mydataframe <- read.xlsx(workbook, 1)

从位于C盘根目录的工作簿myworkbook.xlsx中导入了第一个工作表,并将其保存为一个数据框mydataframe。xlsx包不仅仅可以导入数据表,它还能够创建和操作XLSX文件。那些需要为R和Excel开发接口的程序员应当研究一下这个较新的包。

导入XML数据

以XML格式编码的数据正在逐渐增多。R中有若干用于处理XML文件的包。例如,由Duncan Temple Lang编写的XML包允许用户读取、写入和操作XML文件。XML格式本身已经超出了本书的范围。对使用R存取XML文档感兴趣的读者可以参阅www.omegahat.org/RSXML,从中可以找到若干份优秀的软件包文档。

从网页抓取数据

在Web数据抓取(Webscraping)的过程中,用户从互联网上提取嵌入在网页中的信息,并将其保存为R中的数据结构以做进一步的分析。完成这个任务的一种途径是使用函数readLines()下载网页,然后使用如grep()和gsub()一类的函数处理它。对于结构复杂的网页,可以使用RCurl包和XML包来提取其中想要的信息。更多信息和示例,请参考可在网站Programming with R 上找到的“Webscraping using readLines and RCurl”一文。

访问数据库管理系统

R中有多种面向关系型数据库管理系统(DBMS)的接口,包括Microsoft SQL Server、Microsoft Access、MySQL、Oracle、PostgreSQL、DB2、Sybase、Teradata以及SQLite。其中一些包通过原生的数据库驱动来提供访问功能,另一些则是通过ODBC或JDBC来实现访问的。使用R来访问存储在外部数据库中的数据是一种分析大数据集的有效手段(参见附录G),并且能够发挥SQL和R各自的优势。

ODBC接口

在R中通过RODBC包访问一个数据库也许是最流行的方式,这种方式允许R连接到任意一种拥有ODBC驱动的数据库,其实几乎就是市面上的所有数据库。

第一步是针对你的系统和数据库类型安装和配置合适的ODBC驱动——它们并不是R的一部分。如果你的机器尚未安装必要的驱动,上网搜索一下应该就可以找到。

针对选择的数据库安装并配置好驱动后,请安装RODBC包。你可以使用命令install.packages("RODBC")来安装它。

RODBC包中的主要函数列于表2中。

函  数 描  述
odbcConnect(dsn,uid="",pwd="") 建立一个到ODBC数据库的连接
sqlFetch(channel,sqltable) 读取ODBC数据库中的某个表到一个数据框中
sqlQuery(channel,query) 向ODBC数据库提交一个查询并返回结果
sqlSave(channel,mydf,tablename=sqtable,append=FALSE) 将数据框写入或更新(append=TRUE)到ODBC数据库的某个表中
sqlDrop(channel,sqtable) 删除ODBC数据库中的某个表
close(channel) 关闭连接

RODBC包允许R和一个通过ODBC连接的SQL数据库之间进行双向通信。这就意味着你不仅可以读取数据库中的数据到R中,同时也可以使用R修改数据库中的内容。假设你想将某个数据库中的两个表(Crime和Punishment)分别导入为R中的两个名为crimedat和pundat的数据框,可以通过如下代码完成这个任务:

library(RODBC)
myconn <-odbcConnect("mydsn", uid="Rob", pwd="aardvark")
crimedat <- sqlFetch(myconn, Crime)
pundat <- sqlQuery(myconn, "select * from Punishment")
close(myconn)

这里首先载入了RODBC包,并通过一个已注册的数据源名称(mydsn)和用户名(rob)以及密码(aardvark)打开了一个ODBC数据库连接。连接字符串被传递给sqlFetch,它将Crime表复制到R数据框crimedat中。然后我们对Punishment表执行了SQL语句select并将结果保存到数据框pundat中。最后,我们关闭了连接。

函数sqlQuery()非常强大,因为其中可以插入任意的有效SQL语句。这种灵活性赋予了你选择指定变量、对数据取子集、创建新变量,以及重编码和重命名现有变量的能力。

数据集的标注

为了使结果更易解读,数据分析人员通常会对数据集进行标注。通常这种标注包括为变量名添加描述性的标签,以及为类别型变量中的编码添加值标签。例如,对于变量age,你可能想附加一个描述更详细的标签“Age at hospitalization (in years)”(入院年龄)。对于编码为1或2的性别变量gender,你可能想将其关联到标签“male”和“female”上。

变量标签

遗憾的是,R处理变量标签的能力有限。一种解决方法是将变量标签作为变量名,然后通过位置下标来访问这个变量。考虑之前病例数据框的例子。名为age的第二列包含着个体首次入院时的年龄。代码:

names(patientdata)[2] <- "Age at hospitalization (in years)"

将age重命名为"Age at hospitalization (in years)"。很明显,新的变量名太长,不适合重复输入。作为替代,你可以使用patientdata[2]来引用这个变量,而在本应输出age的地方输出字符串"Age at hospitalization (in years)"。很显然,这个方法并不理想,如果你能尝试想出更好的命名(例如,admissionAge)可能会更好一点。

值标签

函数factor()可为类别型变量创建值标签。继续上例,假设你有一个名为gender的变量,其中1表示男性,2表示女性。你可以使用代码:

patientdata$gender <- factor(patientdata$gender,
                             levels = c(1,2),
                             labels = c("male", "female"))

来创建值标签。这里levels代表变量的实际值,而labels表示包含了理想值标签的字符型向量。

处理数据对象的实用函数

在本文末尾,我们来简要总结一下实用的数据对象处理函数(参见表3)。

函  数 用  途
length(object) 显示对象中元素/成分的数量
dim(object) 显示某个对象的维度
str(object) 显示某个对象的结构
class(object) 显示某个对象的类或类型
mode(object) 显示某个对象的模式
names(object) 显示某对象中各成分的名称
c(object, object,…) 将对象合并入一个向量
cbind(object, object, …) 按列合并对象
rbind(object, object, …) 按行合并对象
Object 输出某个对象
head(object) 列出某个对象的开始部分
tail(object) 列出某个对象的最后部分
ls() 显示当前的对象列表
rm(object, object, …) 删除一个或更多个对象。语句rm(list = ls())将删除当前工作环境中的几乎所有对象
newobject <- edit(object) 编辑对象并另存为newobject
fix(object) 直接编辑对象

参考文档