《数据驱动的网络分析》——6.2 R语言基础知识

简介:

本节书摘来自异步社区《数据驱动的网络分析》一书中的第6章,第6.2节,作者: 【美】Michael Collins 更多章节内容可以访问云栖社区“异步社区”公众号查看。

6.2 R语言基础知识

本节是R语言的速成教程。R是一种特性丰富的语言,我也只是略懂一二。但是,在本节结束时,你就能够编写简单的R程序,在命令行上运行,并将其保存为一个库。

6.2.1 R提示符
启动R,将会显示一个窗口和命令提示符。图6-1展示了一个R控制台的例子。如图所示,控制台主要是一个大的文本窗口,顶部的一系列按钮提供了辅助功能。注意按钮栏下的两个文本框,第一个显示当前工作目录,第二个是帮助功能。R有很好的文档,所以一定要习惯使用帮助框。


611b79a9a5235630c13db875e6a36c25d24f4406

在图6-1中,我输入了几条简单的命令:

> s<-'Hi There'
> x<- 3 + 11 + (3 * log(exp(2)))
> print(s)
[1] "Hi There"
> print(x)
[1] 20

R的命令行提示符是>,你可以在提示符后面手工输入命令,如果命令只完成了一部分(例如,有左括号而没有右括号),下一个提示符将变成加号,并持续到命令结束。

> s<- 3 * (
+ 5 + 11
+ + 2
+ )
> s
[1] 54

注意,当R返回一个值(例如,上例中的s输出),它将打印“[1]”。方括号中的值是数组索引,如果数组分布到多行,每行的开始将打印相应的索引。

> s<-seq(1,20)
> s
 [1] 1 2 3 4 5 6 7 8 9 10 11 12
[13] 13 14 15 16 17 18 19 20

帮助可以使用help(term)或?term访问,可以用help.search()或??在帮助中搜索。

可以使用开关图标或者操作系统对应的退出命令(Command-Q或Ctrl-Q)退出R。如果你使用纯命令行R(不使用图形界面),可以使用Ctrl-D组合键或者在命令行上键入q()结束会话。

R终止时,会询问是否保存工作区。工作区文件可以在会话之后重新加载,继续终止时正在完成的任何工作。

6.2.2 R变量
R支持一些不同的数据类型,包括标量整数、字符数据(字符串)、布尔和浮点值、矢量、矩阵和列表。标量类型(如下面的例子中所示)可以用←(“取值”)、=和→运算符赋值。R的赋值运算符中包含了一些复杂的作用域,对于我们的目的(以及几乎所有R编程),R风格指南建议使用←运算符代替=符号。

> #直接赋值
> a<-1
> b<-1.0
> c<-'A String'
> d<-T
> #将e赋值为d
> e<-d
> e
[1] TRUE
> d
[1] TRUE
> #现在我们重新为d赋值,可以看到,d变化但是e仍保持不变
> d<-2
> d
[1] 2
> e
[1] TRUE

R矢量(vector)是有序的一个或者多个同类值:字符、逻辑或者字符串。矢量可以用c函数或者一些其他的函数创建,是R中最常用的元素:我们在前面使用的标量值从技术上说是长度为1的矢量值。1

> # 整数矢量的一个例子
> int.vec<-c(1,2,3,4,5)
> int.vec
[1] 1 2 3 4 5
> #在必要的时候,浮点数会被转换为整数,整数也可能被转换为浮点数
> float.vec<-c(1,2.0,3)
> float.vec
[1] 1 2 3
> float.vec<-c(1,2.45,3)
> float.vec
[1] 1.00 2.45 3.00
> #矢量也可以包含逻辑值
> logical.vec<-c(T,F,F,T)
> logical.vec
[1] TRUE FALSE FALSE TRUE
> #如果被加入到数值型矢量,逻辑值会被转换为整数
> mixed.vec<-c(1,2,FALSE,TRUE)
> mixed.vec
[1] 1 2 0 1
> #字符矢量由一个或者多个字符串组成,注意,每个字符串是一个元素。
> char.vec <- c("One","Two","Three")
> char.vec
[1] "One"  "Two"   "Three"
> # Length显示矢量的长度
> length(int.vec)
[1] 5
> #注意,字符矢量的长度是字符串的总数,而不是单独字符的总数
> length(char.vec)
[1] 3

注意字符矢量的长度:在R中,字符串不管字符数量多少,都被当作单个元素。有一些函数可以访问字符串——nchar获得字符串长度,substr和strsplit从字符串提取元素——但是单独的字符串不能像Python中那样直接访问。

R提供了许多矢量算术函数。矢量可以加或者乘以另一个矢量,如果它们的大小相等,结果是逐个元素计算得出的。如果其中一个矢量较小,它将被重复,形成相同大小的矢量。(如果一个矢量的长度不是另一个矢量长度的因数,会出现错误)这也适用于单元素矢量:将单一元素多次加上较长矢量中的元素。

矢量可以索引。单个元素可以用方括号访问,v[k]是v的第k个元素。矢量还支持范围切片,如v[a:b]。负数索引将把该索引代表的元素从矢量中删除,如下面的代码块:

> #我们首先根据两个矢量创建一个新的矢量
> v1 <- c(1,2,3,4,5)
> v2 <- c(6,7,8,9,10)
> v3 <- c(v1,v2)
> v3
 [1] 1 2 3 4 5 6 7 8 9 10
> #注意,这里没有发生嵌套
> #基本运算——乘法和加法
> 2 * v1
 [1] 2 4 6 8 10
> 2 * v3
[1] 2 4 6 8 10 12 14 16 18 20
> 1 + v1
[1] 2 3 4 5 6
> v1 * v2
#乘法
[1] 6 14 24 36 50
#范围切片
> v3[1:3]
[1] 1 2 3
#这和v3[1]相等
> v3[1:1]
[1] 1
> v3[2:4]
[1] 2 3 4
# 反转范围,使矢量转向
> v3[3:1]
[1] 3 2 1
#使用负数删除元素
> v3[-3]
[1] 1 2 4 5 6 7 8 9 10
> v3[-1:-3]
[1] 4 5 6 7 8 9 10
> #你可以用逻辑矢量作为选择器,选择返回索引值为真的元素
> v3[c(T,F)]
[1] 1 3 5 7 9

R可以用matrix函数,从矢量中构建矩阵。和矢量一样,矩阵可以进行加法和乘法运算(和自身、矢量以及其他矩阵),也可以使用不同的方法进行选择和切片,如:

> #矩阵用matrix构造,下面是基本形式,注意,列先被填充。
> s<-matrix(v3,nrow=2,ncol=5)
> s
   [,1] [,2] [,3] [,4] [,5]
[1,]   1   3  5   7   9
[2,]   2   4  6   8  10
> # 加上单个元素
> s + 3
   [,1] [,2] [,3] [,4] [,5]
[1,]  4  6  8  10  12
[2,]  5  7  9  11  13
> #乘法
> s * 2
   [,1] [,2] [,3] [,4] [,5]
[1,]  2  6  10  14  18
[2,]  4  8  12  16  20
> #乘以一个矩阵
> s * s
   [,1] [,2] [,3] [,4] [,5]
[1,]  1  9  25  49  81
[2,]  4  16  36  64 100
> #加上一个矢量,注意,加法先从列开始
> s + v3
   [,1] [,2] [,3] [,4] [,5]
[1,]  2  6  10  14  18
[2,]  4  8  12  16  20
> #加上一个较小的矢量,注意,它在矩阵中循环,先从列开始
> s + v1
   [,1] [,2] [,3] [,4] [,5]
[1,]  2  6  10  9  13
[2,]  4  8  7  11  15
> #切片:逗号的使用让大多数人觉得奇怪
> #逗号之前是行,逗号之后是列。
> #返回的结果是一个矢量,这就是“列”现在是横向的原因。
> s[,1]
[1] 1 2
> s[1,]
[1] 1 3 5 7 9
> #访问单个元素
> s[1,1]
[1] 1
> #现在,我将访问第1行第1列和第2列的元素;同样,得到一个矢量
> s[1,1:2]
[1] 1 3
> #第1列第1行和第2行的元素
> s[1:2,1]
   [1] 1 2
> #现在,因为我提取两个矢量,所以得到一个矩阵
> s[1:2,1:2]
   [,1] [,2]
[1,]  1  3
[2,]  2  4
> #使用布尔值选择,第一个值是提取的行
> s[c(T,F)]
[1] 1 3 5 7 9
> s[c(F,T)]
[1] 2 4 6 8 10
> #如果我指定另一个矢量,将选取列
> s[c(F,T),c(T,F,T,T,F)]
[1] 2 6 8

R列表(List)实际上是一个矢量,其中每个元素都可以由列表组成。和矩阵一样,列表可以用特殊的命令构造,可以像矢量那样切片,但是单个元素使用两个方括号访问。更有趣的是,列表可以命名,单独的数量可以指定一个名称,然后用$运算符访问。

#查看前面建立的矢量元素
> v3
 [1]  1  2  3  4  5  6  7  8  9  10
> v4
[1] "Hi"   "There" "Kids"
> #创建一个列表;注意,我们可以添加任意数量的元素。
> #添加的每个元素是一个新的索引。
> list.a <- list(v3,v4,c('What','The'),11)
> #输出列表;注意双括号中的列表索引。
> list.a
[[1]]
 [1]  1  2  3  4  5  6  7  8  9  10

[[2]]
[1] "Hi"   "There" "Kids"

[[3]]
[1] "What"  "The"

[[4]]
[1] 11
> #列表不支持矢量运算。
> list.a + 1
Error in list.a + 1 : non-numeric argument to binary operator
> #单独的元素可以用索引检查。单个方括号返回一个列表。
> list.a[1]
[[1]]
 [1]  1  2  3  4  5  6  7  8  9  10
> #双方括号返回元素本身;注意,列表索引([[1]])在这里不会出现
> list_a[[1]]
 [1]  1  2  3  4  5  6  7  8  9  10
> #单括号返回一个列表,双括号返回单元素列表的第一个元素。
> list_a[1][[1]]
 [1]  1  2  3  4  5  6  7  8  9  10
> #使用双括号访问,然后在矢量中使用单括号。
> list_a[[1]][1]
[1] 1
> list_a[[2]][2]
[1] "There"
> #我们可以修改结果。
> list_a[[2]][2] <- 'Wow'
> #现在,我们将创建一个命名列表。
> list_b <- list(values=v1,rant=v2,miscellany=c(1,2,3,4,5,9,10))
> #参数名称变成列表元素名,参数是列表的实际元素。
> list_b
$values
[1] 1 2 3 4 5

$rant
[1] 6 7 8 9 10

$miscellany
[1] 1 2 3 4 5 9 10

> #命名元素用$符号访问。
> list_b$miscellany
[1] 1 2 3 4 5 9 10
> #访问之后,你可以使用标准的切片。
> list_b$miscellany[2]
[1] 2
> #注意,索引和名称指向相同的值
> list_b[[3]]
[1] 1 2 3 4 5 9 10

理解列表语法对于数据帧很重要,我们将在后面更深入地探讨。

6.2.3 编写函数
R函数通过把function命令绑定到一个符号来创建,例如:

> add_elements <- function(a,b) a + b
> add_elements(2,3)
[1] 5
> simple_math <- function(x,y,z) {
+  t <- c(x,y)
+  z * t
+ }

注意花括号。在R中,花括号用于容纳多个表达式,并返回最后一条语句的结果。花括号可以在没有函数或者任何其他内容的情况下使用,例如:

> { 8 + 7
+ 9 + 2
+ c('hi','there')
+ }
[1] "hi"  "there"

所以,在simple_math函数中,括号里的结果顺序求值,返回最终结果。最终结果不一定和代码块中靠前的语句有任何关系。R可以使用return语句控制函数的终止和返回,但是如果结果很明显,一般的惯例是不使用它。

正如例中所示,函数参数在函数语句中定义。参数可以使用=号提供默认值。定义默认值的参数都是可选的。参数可以通过顺序或者明确使用参数名赋值,如:

#创建具有可选参数的函数
> test<-function(x,y=10) { x + y }
#如果没有传递参数,R将使用默认值
> test(1)
[1] 11
> #参数和值都按照位置设置
> test(1,5)
[1] 6
> #也可以用参数名赋值
> test(1,y=9)
[1] 10
> #对所有变量赋值
> test(x=3,y=3)
[1] 6
> #名称优先于位置
> test(y=8,x=4)
[1] 12
> #没有默认值的参数必须赋值
> test()
Error in x + y : 'x' is missing

R的函数型特征使你可以将函数看作可以操纵、求值的对象,在必要时应用。函数可以传递给其他函数作为参数,也可以使用apply和Reduce函数,支持更复杂的求值。

> #创建一个供其他函数调用的函数
> inc.func<-function(x) { x + 1 }
> dual.func<-function(y) { y(2) }
> dual.func(inc.func)
[1] 3
> #根据输入类型(矩阵、列表、矢量)和输出类型,R有不同的apply函数
> test.vec<-1:20
> test.vec
 [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
> #在匿名函数上运行sapply;注意,该函数没有绑定到某个对象;它在运行期间存在。
> #我可以轻松地调用sapply(c,inc.func),使用上面定义的inc.func函数。
> sapply(test.vec,function(x) x+2)
 [1] 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
> #sapply是经典的映射函数,Reduce是经典的合并/归约函数,将矢量简化为单个数值。
> #在本例中,传递的函数将a和b合并起来,将整数1添加到20中间,得到210
> #注意,Reduce首字母采用大写
> Reduce(function(a,b) a+b,1:20)
[1] 210

关于R中的循环,要注意一点:众所周知,R循环(特别是for循环)的速度缓慢。许多在Python或者C中完成的for循环在R中使用许多函数型结构完成,其中最常用的是sapply和Reduce。

6.2.4 条件与循环
R的基本条件语句是if…then…else,else if用于表示多条语句。if语句本身是一个函数,返回一个可以求取的数值:

> #打印输出一个字符串的简单if/then语句
> if (a == b) print("Equivalent") else print("Not Equivalent")
[1] "Not Equivalent"
> #我们可以直接返回值
> if (a==b) "Equivalent" else "Not Equivalent"
[1] "Not Equivalent"
# if/then是一个函数,所以我们可以将其放入另一个函数,或者另一条 if/then语句中
> if((if (a!=b) "Equivalent" else "Not Equivalent") == \
   "Not Equivalent") print("Really not equivalent")
> a<-45
> #用else if链接多条if/then语句
> if (a == 5) "Equal to five" else if (a == 20) "Equal to twenty" \
 else if (a == 45) "Equal to forty five" else "Odd beastie"
[1] "Equal to forty five"
> a<-5
> if (a == 5) "Equal to five" else if (a == 20) "Equal to twenty" \
 else if (a == 45) "Equal to forty five" else "Odd beastie"
[1] "Equal to five"
> a<-97
> if (a == 5) "Equal to five" else if (a == 20) "Equal to twenty" \
 else if (a == 45) "Equal to forty five" else "Odd beastie"
[1] "Odd beastie"

R提供了switch语句,作为多个if/then子句的紧凑替代品。开关语句对整数比较使用位置型参数,对文本比较则采用可选的参数赋值:

> # switch的第一个参数为数字时,它返回索引对应于该数字的参数,所以下面的语句将返回
> #第二个参数“Is”
> switch(2,"This","Is","A","Test")
[1] "Is"
> proto<-'tcp'
> #如果参数是命名的,这些文本字符串用于匹配
> switch(proto,tcp=6,udp=17,icmp=1)
[1] 6
> #最后一个参数是默认参数
> proto<-'unknown'
> switch(proto, tcp=6,udp=17,icmp=1, -1)
[1] -1
> #为了重复使用switch语句,将其绑定到一个函数
> proto<-function(x) { switch(x, tcp=6,udp=17,icmp=1)}
> proto('tcp')
[1] 6
> proto('udp')
[1] 17
> proto('icmp')
[1] 1

R有3种循环结构:repeat默认提供无限循环,while每次循环都进行条件求值,for在一个矢量上循环。循环的内部操作由break(终止循环)和next(跳过一次循环)控制,如:

> # repeat循环,注意,除非循环中有break语句,否则repeat循环无限运行。
> #如果没有指定条件,它将永远运行下去。
> i<-0
> repeat {
+  i <- i + 1
+  print(i)
+  if (i > 4) break;
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
> # while循环的功能完全相同,这种循环不需要break语句
> i <- 1
> while( i < 6) {
+   print(i)
+   i <- i + 1
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
> # for循环最为紧凑
> s<-1:5
> for(i in s) print(i)
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

虽然R提供了这些循环结构,但是最好是避免循环,而用sapply等函数型操作代替。R不是通用的编程语言,它的设计目的是为统计分析人员提供丰富的操作工具包。R包含了大量经过优化的函数和用于操纵数据的其他工具。我们将在本章后面介绍一些这方面的功能,但是好的R参考来源是很宝贵的。

相关文章
|
8月前
|
存储
R语言笔记丨因子、数据框基础知识
R语言笔记丨因子、数据框基础知识
|
8月前
|
Linux
R语言笔记丨从零学起?环境安装、基础知识、运算法则、数据类型(下)
R语言笔记丨从零学起?环境安装、基础知识、运算法则、数据类型(下)
|
8月前
R语言笔记丨字符串和列表必学基础知识
R语言笔记丨字符串和列表必学基础知识
|
8月前
|
机器学习/深度学习 数据挖掘 Linux
R语言笔记丨从零学起?环境安装、基础知识、运算法则、数据类型(上)
R语言笔记丨从零学起?环境安装、基础知识、运算法则、数据类型
|
3天前
|
数据可视化
【视频】R语言生存分析原理与晚期肺癌患者分析案例|数据分享-4
【视频】R语言生存分析原理与晚期肺癌患者分析案例|数据分享
33 1
|
18小时前
|
移动开发
R语言线性回归模型拟合诊断异常值分析家庭燃气消耗量和卡路里实例带自测题
R语言线性回归模型拟合诊断异常值分析家庭燃气消耗量和卡路里实例带自测题
14 5
|
19小时前
|
存储 算法 数据可视化
R语言用隐马尔可夫Profile HMM模型进行生物序列分析和模拟可视化
R语言用隐马尔可夫Profile HMM模型进行生物序列分析和模拟可视化
18 11
|
19小时前
R语言股票市场指数:ARMA-GARCH模型和对数收益率数据探索性分析(下)
R语言股票市场指数:ARMA-GARCH模型和对数收益率数据探索性分析
|
19小时前
|
数据可视化 算法
R语言coda贝叶斯MCMC Metropolis-Hastings采样链分析和收敛诊断可视化
R语言coda贝叶斯MCMC Metropolis-Hastings采样链分析和收敛诊断可视化
|
1天前
|
机器学习/深度学习 数据挖掘 数据建模
R语言用lme4多层次(混合效应)广义线性模型(GLM),逻辑回归分析教育留级调查数据(下)
R语言用lme4多层次(混合效应)广义线性模型(GLM),逻辑回归分析教育留级调查数据
38 9

热门文章

最新文章