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

  1. 云栖社区>
  2. 博客>
  3. 正文

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

异步社区 2017-05-02 16:07:00 浏览989
展开阅读全文

本节书摘来自异步社区《数据驱动的网络分析》一书中的第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参考来源是很宝贵的。

网友评论

登录后评论
0/500
评论
异步社区
+ 关注