R语言教程-R编程1

13. 列表类型

R中列表(list)类型来保存不同类型的数据。
数据框也是列表的一种, 但是数据框要求各列等长, 而列表不要求。
用typeof()函数判断一个列表, 返回结果为list。 可以用is.list()函数判断某个对象是否列表类型。

列表生成,列表元素修改与取值

rec <- list(name="李明", age=28, scores=c(85,89,97))
rec
# 用typeof()函数判断一个列表
typeof(rec)
# is.list()函数判断某个对象是否列表类型
is.list(rec[3])
is.list(rec)

列表的一个元素也可以称为列表的一个“变量”, 单个列表元素必须用两重方括号格式访问

rec[[3]]
rec[[1]]
rec[["name"]]
rec[[3]][3]

如果使用单重方括号对列表取子集, 结果还是列表而不是列表元素

rec[1]
rec[3]
rec["age"]
# 列表的单个元素也可以用$格式访问
rec$age
# 用names()函数查看和修改元素名
names(rec)
names(rec)[3] <- "三科分数"
# 另一种写法
names(rec)[names(rec)=='name'] <- "姓名"
names(rec)
rec[["三科分数"]]
# 修改元素内容
rec[["三科分数"]][1] <- 94
rec

把某个列表元素赋值为NULL就删掉这个元素。

rec[["age"]] <- NULL
rec

在list()函数中允许定义元素为NULL,这样的元素是存在的

li <- list(a = 20, b="fba", c=NULL)
li

但是,要把已经存在的元素修改为NULL值而不是删除此元素, 或者给列表增加一个取值为NULL的元素, 这时需要用单重的方括号取子集, 这样的子集会保持其列表类型, 给这样的子列表赋值为list(NULL)

li['b'] <- list(NULL)
li[1] <- 21
li["d"] <- list(NULL)
li

列表类型转换

用as.list()把一个其它类型的对象转换成列表; 用unlist()函数把列表转换成基本向量。

lis1 <- as.list(1:4)
lis1
lis2 <- list("姓名"=1, test=2, y=c(3:5))
lis2
lis3 <- unlist(lis2)
lis3

返回列表的函数–strsplit()

strsplit()输入一个字符型向量并指定一个分隔符, 返回一个项数与字符型向量元素个数相同的列表。

x <- c('1, 2, 3', '11, 12, 13', '21, 22, 23', '31, 32, 33')
# 以逗号拆分
res <- strsplit(x, ','); res
# names()函数修改元素名
names(res)[1:4] <- c('第一', '第二', '第三', '第四')
res
# 使用sapply()函数进一步转换成数值型矩阵
res2 <- sapply(res,as.numeric)
res2
# 直接转换为数值会出错
res1 <- as.numeric(res)
res1


14 工作空间

R把在命令行定义的变量都保存到工作空间中, 在退出R时可以选择是否保存工作空间。 这也是R与其他如C、Java这样的语言的区别之一。

# 用ls()命令可以查看工作空间中的内容。
ls()

在命令行定义的变量称为“全局变量”, 在编程实际中, 全局变量是需要慎用的。

可以用rm()函数删除工作空间中的变量,

要避免工作空间杂乱, 最好的办法还是所有的运算都写到自定义函数中。 自定义函数中定义的变量都是临时的, 不会保存到工作空间中。如

# 定义一个sandbox()函数
sandbox <- function(){
cat('沙盘:回车退出。\n')
brower()
}

运行sandbox()函数,提示符变成了“Browser[n]”,其中n代表层次序号。 在这样的browser命令行中随意定义变量, 定义的变量不会保存到工作空间中。 用“Q”命令可以退出这个沙盘环境, 接连回车也可以退出。

15 R输入输出

# 输出
x <- c(1,3,5)
x #或者print()
cat("x=", x, "\n")

cat()默认显示在命令行窗口, 为了写入指定文件中, 在cat()调用中用file=选项, 这时如果已有文件会把原有内容覆盖, 为了在已有文件时不覆盖原有内容而是在末尾添加, 在cat()中使用append=TRUE选项。

cat("=== 结果文件 ===\n", file = "cat_result.txt")
# \n 会在结果会面有一个空行
cat("x =", x, "\n",file="cat_result.txt", append=T)

函数sink()可以用来把命令行窗口显示的运行结果转向保存到指定的文本文件中, 如果希望保存到文件的同时也在命令行窗口显示, 使用split=TRUE选项。

sink("file.txt", split=TRUE)
# 在此输入要保存命令结果的命令
sink() # 终止这样的命令结果输出记录

在R命令行环境中定义的变量、函数会保存在工作空间中, 并在退出R会话时可以保存到硬盘文件中。 用save()命令要求把指定的若干个变量(直接用名字,不需要表示成字符串) 保存到用file=指定的文件中, 随后可以用load()命令恢复到工作空间中。 虽然允许保存多个变量到同一文件中, 但尽可能仅保存一个变量, 而且使用变量名作为文件名。 用save()保存的R特殊格式的文件是通用的, 不依赖于硬件和操作系统。

scores <- c(89,94,97)
save(scores, file="scores.RData")
load("scores.RData")

对于一个数据框, 可以用write.csv()或readr::write_csv()将其保存为逗号分隔的文本文件, 这样的文件可以很容易地被其它软件识别访问, 如Microsoft Excel软件可以很容易地把这样的文件读成电子表格。

library(tibble)
library(readr)
da <- tibble('name'=c('liming','晓强','steven'),
'age'=c(18,24,25))
write_csv(da, path='write_csv file.csv')
# 用scan()函数可以输入文本文件中的数值向量
# 文件中数值之间以空格分开
cat(1:12, file='e:/shixq/R/scan.txt')
# 路径还可以写为'e:\\shixq\\R\\scan.txt'
x <- scan('e:/shixq/R/scan.txt',quiet = T)
x
M <- matrix(x,ncol = 3,byrow = T)
M

scan()中的quite=TRUE选项使得读入时不自动显示读入的数值项数。这种方法可读取较大的数据。
read.table()或readr::read_table()函数也可以读入这样的数据, 但是会保存成数据框而不是矩阵, 而且read.table()函数在读入大规模的矩阵时效率很低。

读取CSV文件

对于保存在文本文件中的电子表格数据, R可以用read.csv(), read.table(), read.delim(), read.fwf()等函数读入, 但是建议在readr包的支持下用read_csv(), read_table2(), read_delim(), read_fwf()等函数读入, 这些将读入的数据框保存为tibble类型, tibble是数据框的一个变种, 改善了数据框的一些不适当的设计。 readr的读入速度比基本R软件的read.csv()等函数的速度快得多, 速度可以相差10倍, 也不自动将字符型列转换成因子, 不自动修改变量名为合法变量名, 不设置行名。

对于中小规模的数据, CSV格式作为文件交换格式比较合适, 兼容性强, 各种数据管理软件与统计软件都可以很容易地读入和生成这样格式的文件, 但是特别大型的数据读入效率很低

CSV格式的文件用逗号分隔开同一行的数据项, 一般第一行是各列的列名(变量名)。

  • 数值型数据:直接表示
  • 字符型数据:可以用双引号或者但引号包裹,或者不包裹
  • 数据本身含有逗号或者引号的情况:需要加引号
library(tibble)
library(readr)
d <- read_csv("testcsv.csv")

read_csv()还可以从字符串读入一个数据框

d.small <- read_csv("name,x,y
john, 22, 45
kim, 54, 87
sandy, 34, 99")
d.small
# 指定列名的写法
d.small <- read_csv("john, 22, 45
kim, 54, 87
sandy, 34, 99
", col_names=c("name", "x", "y") )
d.small

read_csv()将空缺的值读入为缺失值, 将“NA”也读入为缺失值。

CSV文件是文本文件,是有编码问题的, 尤其是中文内容的文件。 readr包的默认编码是UTF-8编码。如果以GBK等其他形式编码,则要加上Locale参数和locale()函数
如:一个.csv文件以GBK保存,文件内容如下

gbk_csv <- c("序号,收缩压
1,145
5,110
6, 未测
9,150
10, 拒绝
15,115")
d1 <- read_csv(gbk_csv,locale=locale(encoding="GBK"))
d1

对每列的类型, readr用前1000行猜测合理的类型, 并在读取后显示猜测的每列类型。

但是有可能类型改变发生在1000行之后。 col_types选项可以指定每一列的类型, 如"col_double()", “col_integer()”, “col_character()”, “col_factor()”, “col_date()”, "col_datetime"等。 cols()函数可以用来规定各列类型, 并且有一个.default参数指定缺省类型。 对因子,需要在col_factor()中用lelvels=指定因子水平。

d2 <- read_csv(gbk_csv, locale=locale(encoding="GBK"),
col_types=cols(`序号` = col_integer(),`收缩压` = col_character())
)
d2

当猜测的文件类型有问题的时候, 可以先将所有列都读成字符型, 然后用type_convert()函数转换,

d <- read_csv("filename.csv",
col_types=cols(.default = col_character()))
d <- type_convert(d)
# 生成表格的md格式
knitr::kable(d2)
# 例如读入一个包含名字,性别,年龄,身高和体重的class.csv数据
#其中年龄,身高和体重可默认是定为col_double()类型
ct <- cols(
.default = col_double(),
name=col_character(),
sex=col_factor(levels=c("M", "F"))
)
d.class <- read_csv('class.csv', col_types=ct)
# 查看每列数据的指定类型(col_double()等)
str(d.class)

其中str()函数可以显示数据框的行数(obs.)和变量数(variables), 以及每个变量(列)的类属等信息
除了read_csv()函数以外, R扩展包readr还提供了其它的从文本数据读入数据框的函数:

  • read_table2()读入用空格作为间隔的文本文件, 同一行的两个数据项之间可以用一个或多个空格分隔, 不需要空格个数相同, 也不需要上下对齐。
  • read_tsv()读入用制表符分隔的文件。
  • read_fwf()读入上下对齐的文本文件。
  • read_lines()函数将文本文件各行读入为一个字符型向量。
  • read_file()将文件内容读入成一整个字符串
  • read_file_raw()可以不管文件编码将文件读入为一个二进制字符串。
    对特别大的文本格式数据, data.table扩展包的fread()读入速度更快。
    readr包的write_excel_csv()函数将tibble保存为csv文件, 总是使用UTF-8编码,结果可以被MS Office读取。

Excel表访问

为了把Microsoft Excel格式的数据读入到R中, 最容易的办法是在Excel软件中把数据表转存为CSV格式, 然后用read.csv()读取。

把R的数据框保存为Excel格式, 只要用write.csv()把数据框保存成CSV格式

library(tibble)
d1 <- tibble("学号"=c("101", "103", "104"),
"数学"=c(85, 60, 73),
"语文"=c(90, 78, 80))

write.csv(d1, file="writecsv_test.csv", row.names=FALSE)
# 读取查看
library(readr)
d4 <- read_csv("writecsv_test.csv",locale=locale(encoding = "GBK"), col_types = cols(`学号`= col_character()))
d4
# 查看d4结构和变量属性
str(d4)

使用剪贴板

为了把Excel软件中数据表的选中区域读入到R中, 可以借助于剪贴板。 在Excel中复制选中的区域,然后在R中用

# 从excel粘贴数据
mypast_EXcel <- read.delim("clipboard")
mypast_EXcel

网页上类似于excel表格形式的数据也可以用read.delim(“clipboard”)读取

# 从网页生成的表格(markdown生成)粘贴数据
mypast_web <- read.delim("clipboard")
mypast_web

经过read.delim()函数把选中部分转换成一个R的数据框。 如果复制的区域不含列名, 应加上header=FALSE选项。

这种方法也可以从R中复制数据到剪贴板中,然后可以把数据再粘贴到EXCEL中即可,例如

write.table(iris, file="clipboard", sep = "\t", col.names = NA)

这里把指定的数据框(这里是iris)写入到了剪贴板。
函数把指定的数据框写入到指定的文件中, 其中的col.names=NA选项是一个特殊的约定, 这时保存的文件中第一行是列名,如果有行名的话,行名所在的列对应的列名是空白的

利用readxl扩展包

readxl扩展包的readxl()函数利用独立的C和C++库函数读入.xls和.xlsx格式的Excel文件。一般格式为

read_excel(path, sheet = 1, col_names = TRUE, 
col_types = NULL, na = "", skip = 0)

结果返回读入的表格为一个数据框。

  • path: 要读入的Excel文件名,可以是全路径,路径格式要符合所用操作系统要求。
  • sheet: 要读入哪一个工作簿(sheet),可以是整数序号,也可以是工作簿名称的字符串。
  • col_names: 是否用第一行内容作为列名,缺省为是。
  • col_types:
    和read_csv()中的col_types=clos(列名= blank/date/text/numeric),其中如果列名为中文,要使用``包裹。
    可以在读入时人为指定各列的数据类型,缺省时从各列内容自动判断,有可能会不够准确。人为指定时,指定一个对应于各列的字符型向量,元素可取值为:
    • blank: 自动判断该列;
    • numeric: 数值型;
    • date: 日期;
    • text: 字符型。

可以利用RODBC访问Excel文件,这里不演示,用其他方法代替此方法。
参考:利用RODBC访问Excel文件

d1 <- data.frame("学号"=c("101", "103", "104"),
"数学"=c(85, 60, 73),
"语文"=c(90, 78, 80))
d2 <- data.frame("学号"=c("101", "103", "104"),
"性别"=c("女", "男", "男"))

fname <- "testwrite.xls"
if(file.exists(fname)) file.remove(fname)

# install.packages("RODBC")
library(RODBC)
# odbcConnectExcel is only usable with 32-bit Windows
con <- odbcConnectExcel(fname, readOnly=FALSE)
res <- sqlSave(con, d1, tablename="成绩",
rownames=F, colnames=F, safer=T)
res <- sqlSave(con, d2, tablename="性别",
rownames=F, colnames=F, safer=T)
close(con)

require(RODBC)
con <- odbcConnectExcel('testwrite.xls')
rd1 <- sqlFetch(con, sqtable='成绩')
close(con)

MySQL数据库访问

平常基本上不用MySQL数据库,等用到时再实践

文件访问

输入输出可以针对命令行,针对文件,R支持扩展的文件类型, 称为“连接(connection)”。

函数file()生成到一个普通文件的连接, 函数url()生成一个到指定的URL的连接,函数gzfile, bzfile, xzfile, unz支持对 压缩过的文件的访问不是压缩包,只对一个文件压缩)。
这些函数的大概用法:

file("path", open="", blocking=T,
encoding = getOption("encoding"),
raw = FALSE)

url(description, open = "", blocking = TRUE,
encoding = getOption("encoding"))

textConnection(description, open="r",
local = FALSE,
encoding = c("", "bytes", "UTF-8"))

gzfile(description, open = "",
encoding = getOption("encoding"),
compression = 6)

bzfile(description, open = "",
encoding = getOption("encoding"),
compression = 9)

xzfile(description, open = "",
encoding = getOption("encoding"),
compression = 6)

unz(description, filename, open = "",
encoding = getOption("encoding"))

文件打开或者写入类型:

  • r:文本型只读;
  • w:文本型只写;
  • a:文本型末尾添加;
  • rb:二进制只读;
  • wb:二进制只写;
  • ab:二进制末尾添加;
  • r+r+b:允许读和写;
  • w+w+b:允许读和写,但刚打开时清空文件;
  • a+a+b:末尾添加并允许读。

文本文件访问

函数readLines(), scan()可以从一个文本型连接读取。

# 以行读取数据
D <- readLines(file('class.csv'))
print(D)

用writeLines函数可以把一个字符型向量各元素作为不同行写入一个文本型连接。

# 取D的第一行
vnames <- strsplit(D, ',')[[1]]
# con参数是指定一个写入文件名。
writeLines(vnames, con='class-names.txt')

结果为:

name
sex
age
height
weight

字符型连接

fstr <-
"name,score
王芳,78
孙莉,85
张聪,80
"
# 函数textConnection打开一个字符串用于读取或写入
d <- read.csv(textConnection(fstr), header=T)
print(d)
tc <- textConnection("sres", open="w")
cat('Trial of text connection.\n', file=tc)
cat(1:10, '\n', file=tc, append=T)
close(tc)
print(sres)

写入用的textConnection 的第一个参数是保存了将要写入的字符型变量名的字符串, 而不是变量名本身, 第二个参数表明是写入操作, 使用完毕需要用close关闭。

中文编码问题

可以用iconvlist()查看R支持的编码名称
如果文件为GBK编码,则可以使用一些编辑器如notepad++,sublime text3等转换为UTF-8格式,或者在读入时用参数指明文件类型为GBK。
如上面提到的例子,假设gbk_csv的文件内容在excel中保存时是GBK格式,则用locale=locale(encoding=“GBK”)指定。

gbk_csv <- c("序号,收缩压
1,145
5,110
6, 未测
9,150
10, 拒绝
15,115")
d1 <- read_csv(gbk_csv,locale=locale(encoding="GBK"))
d1

用基本R的读取函数读取

读取UTF-8编码无BOM的文件时, 在read.csv()和read.table()等函数中加fileEncoding="UTF-8"选项可以纠正编码问题;
UTF-8有BOM标志的文本文件不能被read.csv()识别

读取UTF-8编码无BOM或者有BOM的文件时, 在readLines()函数中加encoding="UTF-8"选项可以纠正编码问题。

用readr包读取

readr包的read_csv()、read_table2()、read_lines()函数默认从UTF-8编码的文件中读取, 无BOM或者有BOM都可以。

但是,对GBK编码的文件,不能直接读取,为了读取GBK(或GB18030)编码的文件, 需要在read_csv()和read_lines()函数中加入 locale=locale(encoding=“GBK”)选项

输出文件的编码

write.csv()、writeLines()生成的含有中文的文件的编码默认为操作系统的默认中文编码, 这里是GB18030。

readr的write_csv()、write_lines()函数生成的含有中文的文件的编码默认UTF-8无BOM。

library(tibble)
library(readr)
write_csv(tibble("姓名"=c("张三", "李四")), "tmp.csv")
# 查看数据
read_csv("tmp.csv")

结果生成的文件编码为UTF-8无BOM, 这样的文件可以被R的readr::read_csv()正确读取, 但是不能被MS Excel软件正确读取。
write_excel_csv()可以生成带有UTF-8有BOM的CSV文件, 这样的文件可以被MS Office正确识别:

write_excel_csv(tibble("姓名"=c("张三", "李四")), "tmp1.csv")
# 查看数据
read_csv("tmp1.csv")

目录和文件管理

目录和文件管理函数:

  • getwd():返回当前工作目录。
  • setwd(path):设置当前工作目录。
  • list.files()dir():查看目录中内容。 list.files(pattern=’.*[.]r$’)可以列出所有以“.r”结尾的文件。dir()缺省为查看当前工作目录下的所有文件。
  • file.info(filenames):显示文件的详细信息。
  • file.exists():查看文件是否存在。
  • file.access():考察文件的访问权限。
  • create.dir():新建目录。
  • file.create():生成文件。
  • file.remove()unlink():删除文件。unlink()可以删除目录。
  • file.rename():为文件改名。
  • file.append():把两个文件相连。
  • file.copy():复制文件。