vlambda博客
学习文章列表

数据科学02 | R语言程序设计-数据结构与函数

往期回顾:

1. 准备R

➢安装R和Rstudio

➢设置工作目录  

查看工作目录:

getwd()[1"/Users/UserName"

更改工作目录:

setwd("~/Documents"dir() #查看当前目录文件

➢在R的文本编辑器编写代码

代码标准

・通常使用文本文件/文本编辑器・缩进代码

仅靠缩进就能理解程序是按何种顺序运行的。

・限制代码的宽度

缩进可能会使代码无限制向右排,需要限制代码的宽度,一般将文本的列数限制在大约80列。 

・限制函数的长度

有利于函数的易读性和调试。 

2. 数据类型和结构

在R中,对象(object)是指可以赋值给变量的任何事物,包括常量、数据结构、函数,甚至图形。

➢基本数据类型

・字符型character ・数值型numeric ・整数型integer ・逻辑型logic ・复数型complex

➢数据结构

・向量vector

存储数值型、字符型或逻辑型数据的一维数组。

・矩阵matrix

二维数组,每个元素都拥有相同的类型(数值型、字符型或逻辑型)。

・数组array

与矩阵类似,但是维度可以大于2。

・数据框dataframe

用来存储表格数据,每一列数据都可以是不同的类型。

・列表list

可以是几个向量、矩阵、数据框,甚至其他列表的集合。

・因子factor

类别变量和有序类别变量,可以分类数据。

3. 控制结构

1.if, else,测试逻辑条件

if(<condition1>){      ##do somethingelse {      ##do something else
if(<condition1>){      ##do somethingelse if(<condition2>){      ##do something differentelse {      ##do something different}
#else 不是必需if(<condition1>){      ##do something}if(<condition2>){      ##do something}

2.for,执行固定次数的循环

x <- c("a", "b", "c", "d")for (i in 1:4) { print(x[i])}[1] "a"[1] "b"[1] "c"[1] "d"
for (in seq_along(x)) { #seq_along函数创建一个与向量等长的整数数列 print(x[i])}[1] "a"[1] "b"[1] "c"[1] "d"
for (letter in x) { print(letter)}[1] "a"[1] "b"[1] "c"[1] "d"
for (i in 1:4) print(x[i])[1] "a"[1] "b"[1] "c"[1] "d"

输出结果都一样。

x <- matrix(1:6,2,3)for(i in seq_len(nrow(x))) { for(j in seq_len(ncol(x))) { print(x[i, j]) } }[1] 1[1] 3[1] 5[1] 2[1] 4[1] 6

for可以嵌套,但一般少于3层,否则不易解释。

3.while,条件成立时执行循环

count <- 0 while(count < 5) { print(count)  count <- count + 1}[1] 0[1] 1[1] 2[1] 3[1] 4

4.repeat,执行无限循环

x0<-1 tol <- 1e-8repeat { x1 <- computeEstimate() if(abs(x1 - x0) < tol) {  break }else{  x0<-x1 } }

repeat循环易陷入死循环,需要注意!(在数据处理中不常用)

5.break,终止并跳出repeat循环

6.next,跳过循环的当前迭代

for(i in 1:100) {  if(i <= 20) { ##跳过前20次迭代 next } ## Do something}

7.return,退出并返回值

4. 函数function

R语言用 function() 创建函数。

f <- function(<arguments>) { ## Do something}

在R里函数可以作为对象处理,可以将函数作为参数传递给其它函数,也可以嵌套。

➢参数

形式参数是函数定义包含的参数。・缺省值:参数的默认值,调用函数并不需要指定每个参数的值。 

➢参数匹配规则

函数参数可以根据位置或者名字来匹配——编写和调用函数的关键。 

例:函数sd() ,可以计算数据的标准差

formals(sd) $x
$na.rm[1] FALSE

formals()函数返回函数所有形式参数组成的列表。

  1. sd() 读入名为 x 的向量

  2. 参数na.rm判断是否移除缺失值NA,na.rm缺省值是FALSE,即默认缺失值不参与计算  

mydata <- rnorm(100)#生成100个服从正态分布的随机数,且没有缺失值数据sd(mydata) #没有对参数进行了命名,默认将 mydata 作为第一个参数传递给函数[1] 0.9131906sd(x = mydata) #对参数进行了命名[1] 0.9131906sd(x = mydata, na.rm = FALSE) #命名所有参数[1] 0.9131906sd(na.rm = FALSE, x = mydata) #给变量命名的时候,不需要将它们按照特定顺序排列[1] 0.9131906sd(na.rm = FALSE, mydata) #只命名了第一个参数,命名的参数会按照命名来设定它们的值[1] 0.9131906#可以认为已命名的参数被移出了参数列表,然后其它参数值按照排列的先后顺序与函数参数匹配

不推荐改变参数的排列顺序,容易引起误解。  

对参数极多的函数混合使用位置匹配和命名。 

例:lm()函数,把数据拟合到线性模型  

args(lm) function (formula, data, subset, weights, na.action, method = "qr", model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, contrasts = NULL, offset, ...) NULL

args()可以查看函数的所有参数及其对应的缺省值。

  1. 前五个参数没有缺省值,使用者必须指定它们的值

  2. 参数 method、model 和 x 都有缺省值

比较两个等价的函数调用方式:

#方式一:lm(data = mydata, y ~ x, model = FALSE, 1:100)
#方式二:lm(y ~ x, mydata, 1:100, model = FALSE)

方式一:

  1. 将data参数按照命名来匹配,移出参数列表

  2. y ~ x 因为没有特定的名字,将赋值给第一个尚未配对的参数,即formula 

  3. model 参数按命名匹配,移出参数列表

  4. 1:100 将会被赋值给下一个未匹配的参数subset

用这种方式调用 lm() 会让人觉得混乱。通常使用方式二调用lm()。

R中的参数匹配顺序:

  1. 首先检查完全匹配,指定一个参数名,它会检查是否有参数跟该命名完全匹配

  2. 如果找不到完全匹配的参数,它将查找是否有模糊匹配的参数

  3. 如果都没有,则再进行位置匹配

➢惰性求值 (Lazy Evaluation)仅在函数参数被调用时进行求值。

f <- function(a, b) {  a^2} #函数定义两个参数a和b,但只使用了一个参数af(2)[1] 4
f <- function(a, b) { print(a) print(b) }f(45)[1] 45Error in print(b) : 缺少参数"b",也没有缺省值#没有给b赋值,参数b也没有缺省值,错误是在输出45之后出现的

➢特殊参数:...参数
可以将原函数的一些参数迅速传递给另一个函数。 

运用:
・拓展一个函数,更改其中一小部分或更改一些缺省值

例:定义myplot()函数,拓展plot()函数

myplot <- function(x, y, type = "l", ...) {  plot(x, y, type = type, ...)}

1.重复使用原 plot() 函数使用的参数,比如 x 和 y;2.type参数的缺省值可以修改;3.plot() 函数的其它参数可以用 ... 来传递,不需要在扩展函数中重新输入或者创建。

・可以用在泛型函数中

泛型函数 (generic function)函数自身不做任何运算,根据数据类型使用合适的方法,这种类型函数的设置常常要用到...参数,使得附加参数可以传递给方法。

・当传递到函数的参数数量不能事先确定的时候

例:paste() 函数paste() 函数将一组字符串连起来生成新的字符向量,无法预先注明有多少参数需要连接。

args(paste)function (..., sep = " ", collapse = NULL)

1. ...参数 ,各种字符向量对象;2. sep参数,缺省值为空格,用分隔符连接字符;3. sep参数和collapse参数在 ... 参数之后。 

任何出现在 ... 之后的参数列表需要明确地给出名称,而且不能进行模糊匹配!  

paste("a", "b", sep = ":")[1] "a:b"paste("a", "b", se = ":")[1] "a b :"

paste() 函数中模糊匹配无效,R 并不知道需要将信息传递给 ... 参数,还是其他参数。

5. 日期和时间

R 用了一种特殊的数据类型来表示日期和时间。 

  • 日期用 Date 类表示

  • 时间由POSIXct和POSIXlt表示  

  • 日期不包括时间,只表示某年某月某日,日期以距离1970年1月1日的天数来存储数据

  • 时间以距离1970年1月1日的秒数存储数据

as.Date() 函数将字符串格式的日期转换成Date类型。

x <- as.Date("1970-01-01")x[1] "1970-01-01" #x不是character类,是Date类
unclass(x) #去除对象x的类[1] 0 #日期以距1970年1月1日的天数来存储的,而1970年1月1日与x表示的日期相差0天
unclass(as.Date("1970-01-02"))[1] 1 unclass(as.Date("1969-12-31"))[1] -1

POSIX 规定了特定类型的计算机处理事件和数据表示的方法。POSIX 标准里有一个标准族来规定如何表示日期和时间。 

  • POSIXct 类的时间用非常大的整数表示,常作为数据框储存的时间数据的类。

  • POSIXlt类实际上把时间当作列表来存储

Sys.time() 函数返回系统当前的时间。

x <- Sys.time()x[1"2020-01-10 01:33:02 CST"#以年月日的格式输出,紧接着是时分秒的格式,接着是时区class(x)[1] "POSIXct" "POSIXt" #时间为POSIXct类unclass(x)[1] 1578591183#对POSIXct类的对象x去除类,得到了非常大的整数,是自1970年1月1日至今的秒数

as.POSIXlt() 或 as.POSIXct() 函数可以在 POSIXlt 类和 POSIXct 类之间转换。

x <- Sys.time()x[1] "2020-01-10 01:20:26 CST"
p <- as.POSIXlt(x) #转换成POSIXlt类names(unclass(p))[1] "sec" "min" "hour" "mday" "mon" "year" "wday" "yday" "isdst" [10]"zone" "gmtoff"#POSIXlt实际上是一个列表,去除类之后查看元素名
p$sec #查看元素[1] 26.97712

strptime() 函数可以把字符串格式的日期转换成日期或时间对象。

datestring <- c("January 10, 2012 10:40", "December 9, 2011 9:10") x <- strptime(datestring, "%B %d, %Y %H:%M")x[1] "2012-01-10 10:40:00 CST" "2011-12-09 09:10:00 CST"class(x)[1] "POSIXlt" "POSIXt"

对日期或时间类的数据进行运算可以很方便。不能把不同的类混淆在一起计算。

x <- as.Date("2012-01-01")y <- strptime("9 Jan 2011 11:34:21", "%d %b %Y %H:%M:%S") 
x-yError in x - y : 二进列运算符中有非数值参数此外: Warning message:不可兼容的方法("-.Date", "-.POSIXt")和"-"
x <- as.POSIXlt(x)x-yTime difference of 356.3 days

日期和时间能计算不易理清的时间,例如闰年、闰秒、夏令时以及时区。

x <- as.Date("2012-03-01") y <- as.Date("2012-02-28") x-yTime difference of 2 days#2012年是闰年,两个日期相差2天
x <- as.POSIXct("2012-10-25 01:00:00")y <- as.POSIXct("2012-10-25 06:00:00", tz = "GMT") y-xTime difference of 13 hours#时区不同,实际相差13小时

对日期和时间都通用的泛型函数:

・weekdays() 函数:给定的日期或时间是星期几 ・months() 函数:给定的日期或时间是在几月 ・quarters() 函数:给定的日期或时间处于第几季度

编辑:李雪纯 冯文清
校审:张健 罗鹏