文章和代码已经归档至【Github 仓库:https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 R 语言 也可获取。
用 R 基本包
在实际的数据分析中,分析者往往需要花费大量的精力在数据的准备上,将数据转换为分析所需要的形式。遗憾的是,大多数统计学教材很少涉及这一重要问题。整理数据是统计学的任务之一。我们开始关注 R 中最常用的数据格式——数据框的基本操作。我们将首先使用基本包处理数据框。
先加载 epiDisplay
包里的一个小型数据集 Familydata
。
library(epiDisplay)
data("Familydata")
复制代码
1.查看数据框里的内容
如果数据框较小,比如本例(只有 6 个变量,11 条记录),只需输入数据框名即可查看其全部内容,这等价于调用函数 print( ) 显示对象的内容。
# 输入数据框名即可查看其全部内容,这等价于调用函数 print( ) 显示对象的内容。
Familydata
# 用函数 `head( )` 只显示其前几行
head(Familydata)
# 用函数 `tail( )` 显示其最后几行
tail(Familydata)
# 可以加参数指定到底几行
tail(Familydata,7) # 显示尾7行
# 列出所有变量名(列名)
names(Familydata)
复制代码
另一个可以用来方便地探索数据框结构的函数是 str( )
。
str(Familydata)
# ==============显示结果=============
# 首先给出了对象的类型(这里是数据框“data.frame”)、观测数和变量的个数;
'data.frame': 11 obs. of 6 variables:
# 接着给出了数据框中每个变量的变量名和类型,以及变量的前几个取值
$ code : chr "K" "J" "A" "I" ...
$ age : int 6 16 80 18 69 72 46 42 58 47 ...
$ ht : int 120 172 163 158 153 148 160 163 170 155 ...
$ wt : int 22 52 71 51 51 60 50 55 67 53 ...
$ money: int 5 50 100 200 300 500 500 600 2000 2000 ...
# 对于因子型变量还给出了因子的水平;
$ sex : Factor w/ 2 levels "F","M": 1 2 2 1 1 1 1 1 2 1 ...
# 最后给出了数据框的一些属性:
# 数据标签(“datalabel”)
- attr(*, "datalabel")= chr "Anthropometric and financial data of a hypothetical family"
- # 数据建立时间(“time.stamp”)
- attr(*, "time.stamp")= chr "23 Nov 2006 17:15"
- attr(*, "formats")= chr [1:6] "%9s" "%8.0g" "%8.0g" "%8.0g" ...
- attr(*, "types")= int [1:6] 128 98 105 98 105 108
# 变量标签(“var.labels”)
- attr(*, "val.labels")= chr [1:6] "" "" "" "" ...
- attr(*, "var.labels")= chr [1:6] "" "Age(yr)" "Ht(cm.)" "Wt(kg.)" ...
# 版本号(“version”)
- attr(*, "version")= int 7
- attr(*, "label.table")=List of 6
..$ sex1: Named num [1:2] 1 2
.. ..- attr(*, "names")= chr [1:2] "F" "M"
..$ : NULL
..$ : NULL
..$ : NULL
..$ : NULL
..$ : NULL
复制代码
这些属性可以增强用户对数据集的理解。要想显示数据框属性的全部信息,可以使用 attributes( )
函数,该函数的输出是一个列表。
attributes(Familydata)
# ================== 输出 ======================
$names
'code''age''ht''wt''money''sex'
$row.names
1234567891011
$class
'data.frame'
$datalabel
'Anthropometric and financial data of a hypothetical family'
$time.stamp
'23 Nov 2006 17:15'
$formats
'%9s''%8.0g''%8.0g''%8.0g''%8.0g''%8.0g'
$types
1289810598105108
$val.labels
'''''''''''sex1'
$var.labels
'''Age(yr)''Ht(cm.)''Wt(kg.)''Pocket money(B.)'''
$version
7
$label.table
$sex1
F1M2
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
[[5]]
NULL
[[6]]
NULL
复制代码
也可以修改和自定义这些属性。例如,从上面的输出可以看到,第一个变量和最后一个变量没有定义标签。现在为这两个变量添加标签:
attr(Familydata, "var.labels")[1] <- "Identification number"
attr(Familydata, "var.labels")[6] <- "Gender"
attributes(Familydata)$var.labels
# 'Identification number''Age(yr)''Ht(cm.)''Wt(kg.)''Pocket money(B.)''Gender'
复制代码
给变量添加标签能帮助我们更好地理解变量的含义。此外,后面用到的 epiDisplay
包里有些函数的输出还能直接使用这些变量标签。
2.选取数据框的子集
与矩阵类似,我们可以用索引下标的方式选取数据框的子集。
# 选择数据框 Familydata 的第 3 列
Familydata[, 3]
# 也可以使用$变量名的方式
Familydata$ht
# 要提取一个以上的变量,可以使用变量的索引号或名字。例如,只显示变量ht、wt 和 sex 的前 3 条记录,可以输入:
Familydata[1:3, c(3, 4, 6)]
# 等价于
Familydata[1:3, c("ht", "wt", "sex")]
复制代码
下标中的索引还可以是一个条件语句。例如,要选择性别为女性的数据,可以输入:
Familydata[Familydata$sex == "F", ] # 注意逗号跟双等号
复制代码
另一种选择数据框的子集的方法是使用 subset( )
函数。
subset(Familydata, sex == "F")
# 若只选择女性中的变量 ht 和 wt
subset(Familydata, sex == "F", select = c(ht, wt))
复制代码
注意,该命令只是选择一个子集来显示,不会对原来的数据框产生任何影响。如果还要进一步使用该子集,需要把它存为一个新的对象。
在机器学习领域,经常需要从数据集里随机抽取一部分样本。例如,我们想把一个大的数据集随机分成两份,其中一份用于构建预测模型,另一份用于验证模型的预测精度。函数 sample( )
可用于随机抽样,下面的命令从数据框 Familydata
里随机抽取一个大小为 3 的样本:
sample.rows <- sample(1:nrow(Familydata), size = 3, replace = FALSE)
sample.rows
# 5 4 1
复制代码
函数 sample( ) 中的第一个参数是一个由要从中抽样的元素组成的向量,在这里是从 1 到数据框中的总观测数;第二个参数 size 是要抽取的元素的数量;第三个参数 replace 用于设定是否放回抽样,默认为 FALSE(不放回抽样)。
函数 sample( ) 的返回值可用于选择数据框中的行。由于随机种子数的不同,每次运行得到的结果很可能不一样。
3.将数据框按照某个变量的值排序:order( )
有时我们想将数据框按照某个变量的值的大小进行排序显示,这可以借助函数 order( )
实现。例如,将数据框 Familydata 以变量 age 的值从小到大显示,可以使用下面的命令:
# ,前表示条件 ,后表示显示的列
Familydata[order(Familydata$age), ] # 默认升序
# 降序写法
Familydata[order(Familydata$age, decreasing = TRUE), ]
# 等价于age取反结果
Familydata[order(-Familydata$age), ]
复制代码
4.查找和删除重复数据:duplicated( )
原始数据集里经常会有重复的行。如果不是重复测量的数据,数据集的每一行应该是某一个对象的观测,而且数据集里通常有一个用于识别个体的变量(比如 id)。
数据集 Familydata 中的变量 code 就是个体识别号,下面检查该变量有无重复值:
duplicated(Familydata$code)
# FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# 函数 duplicated( ) 的返回值是逻辑值 TRUE 或 FALSE,这里全为 FASLE,表明变量 code 没有重复值。
复制代码
如果数据框的行数较多,逐一查看这些逻辑值会很麻烦。此时,可以将函数 any( )
作用于函数 duplicated( ) 的输出结果:
any(duplicated(Familydata$code))
# FALSE
复制代码
或者使用函数 table( )
,还能得到重复值的个数:
table(duplicated(Familydata$code))
# FALSE
# 11
复制代码
删除重复行
为了阐明怎样删除重复的行,下面建立一个数据框 Familydata1,将原数据框 Familydata 的第 2 行添加在其第 12 行:
Familydata1 <- Familydata
Familydata1[12, ] <- Familydata[2, ]
Familydata1
复制代码
使用函数 which( )
可以找出变量 code 的重复值所在的行:
which(duplicated(Familydata1$code))
复制代码
然后,删除重复的行:
# 将不重复的新建对象即可
unique.code.data <- Familydata1[!duplicated(Familydata1$code), ]
unique.code.data
复制代码
identical
查看对象是否完全一样
# 用identical查看两个对象是否完全一样
identical(unique.code.data, Familydata)
# TRUE
复制代码
5.在数据框中添加和删除变量
在处理数据框时,我们经常需要创建新的变量并把它添加到现有的数据框中。例如,建立一个新的变量 log10money
,其值等于变量 money 以 10 为底的对数。最直接地,可以输入:
Familydata$log10money <- log10(Familydata$money)
# 或者可以使用 `transform( )` 函数:
Familydata <- transform(Familydata, log10money = log10(money))
names(Familydata)
# 'code''age''ht''wt''money''sex''log10money'
复制代码
与添加变量相反,如果想从数据框中删除一个变量,只需在方括号内下标号的前面添加一个减号。例如:
请注意,该命令只显示所需的子集,对数据框本身不会产生影响。
但是赋一个空值(NULL)给数据框中的变量等同于删除该变量,并且是会永久删除数据框中的变量:
Familydata$log10money <- NULL
colnames(Familydata)
复制代码
6.把数据框添加到搜索路径
在前面查看和使用数据框中的变量时,我们需要在变量名前面加上数据框名和符号 $
。这种方式有时候会显得比较烦琐,尤其是数据框和变量的名字都很长的时候。此时,函数 attach( )
或者函数 with( )
可以用来简化代码。
函数 attach( )
可以将数据框添加到搜索路径中。输入以下命令:
然后用函数 search( )
查看搜索路径中的所有对象:
search()
#'.GlobalEnv''Familydata''package:epiDisplay''package:nnet''package:MASS''package:survival''package:foreign''package:repr''jupyter:irkernel''package:stats''package:graphics''package:grDevices''package:utils''package:datasets''package:methods''Autoloads''package:base'
复制代码
现在搜索路径中的第二个位置存放了数据框 Familydata。由于数据框已经在搜索路径中了,而变量 age 又在该数据框里,所以现在可以直接使用变量 age 了。
summary(age)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 6.00 30.00 47.00 45.73 63.50 80.00
复制代码
把一个数据框放入搜索路径类似于使用函数 library( ) 加载一个包。调入搜索路径的数据框和加载的包都会被自动读入 R,并一直存放在内存中直至它们被移出(detach( )
)。
使用函数 attach( )
虽然会在输入代码时带来一些便利,但同时也会带来一些问题。例如,重复加载数据框可能会最终导致系统资源过度负荷。另外,如果全局环境中或多个数据框中有相同的变量名,容易使用户产生混淆。因此,有些 R 的使用者尽量避免使用函数 attach( ),而使用函数 with( )
。
以 datasets
包里的数据集 infert
为例:
with(infert, summary(age))
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 21.00 28.00 31.00 31.50 35.25 44.00
复制代码
函数 with( ) 的局限性在于,对于多次使用数据框,我们必须重复使用函数 with( )。此外,赋值仅在此函数内部生效,例如:
with(infert, {
m <- mean(age)}
)
m
# ERROR:object 'm' not found
复制代码
选择哪一种数据处理方式取决于分析者的偏好。例如《R 语言医学数据分析实战》推荐的做法是:
在开启一个新的分析项目时,首先使用命令 rm(list = ls( )
) 从 R 工作环境中清除所有对象;
在分析过程中用函数 detach( )
将不再需要使用的数据框从搜索路径中移出;
不要定义与已经存在于搜索路径中的数据框同名的新对象;
评论