本文对应《R语言编程艺术》第2章:向量;第3章:矩阵和数组;第4章:列表;第5章:数据框;第6章:因子和表

=========================================================================

R语言最基本的数据类型就是向量(vector),单个数值和矩阵都是向量的一种特例。

声明:R中不需要声明变量,但是注意函数式语言的特性,如果读写向量中的元素时,R事先不知道对象是向量的话,则函数没有执行的对象。如下代码是无法工作的:

y[1] <- 5
y[2] <- 12

  

循环补齐:

在对两个向量使用运算符时,如果要求这两个向量具有相同的长度,R会自动循环补齐(recycle),即重复较短的向量,直到它与另一个向量长度相匹配。

需要注意的是,矩阵实际上是一个长向量,(1, 2, 3, 4, 5, 6)转成矩阵形式则是:

[   1   4

2   5

3   6    ]

常用的向量运算:

向量运算和逻辑运算:注意到R是函数式语言,每一个运算符都是函数,因此不管+-*/都是元素与元素逐一运算,特别注意*与线性代数中的矩阵运算不同,也是元素和元素逐一相乘。

向量索引:索引格式为:向量1[向量2],返回的结果为向量1中索引为向量2的那些元素,注意元素允许重复。负数下标代表想把相应元素剔除。

用:运算符创建向量:生成指定范围内数值构成的向量。注意:运算符的优先级高于一般运算符,具体的优先级情况可以在命令窗口中输入?Syntax查看。

用seq()创建向量:生成等差序列

x <- c()
#比较下面两句代码
for(i in 1:length(x))
for(i in seq(x))
#第一句返回的i = c(0, 1),显然与希望中的不同
#第二句返回的i为NULL

  

使用rep()重复向量常数:通过调用rep(x, times),即可创建times*length(x)个元素的向量,由x重复times次构成;或者通过调用rep(x, each),可创建each*length(x)个元素的向量,由x交替重复each次构成。

> rep(c(5, 12, 13), 3)
> [1] 5 12 13 5 12 13 5 12 13
> rep(c(5, 12, 13), each = 2)
> [1] 5 5 12 12 13 13

  

使用all()和any()

这两个函数分别报告其参数是否至少有一个或全部为TRUE

向量化运算符:

向量输入,向量输出:很多函数与运算符都是向量化的,注意灵活应用以提高代码效率。这种没有标量的语言(标量实际上是长度为1的向量)因此带来一些代码安全性问题:自定义的函数在需要输入为标量的时候,输入的是向量也会有返回值,但是却没有任何提示。这就需要在设计函数时考虑这个问题,进行输入是否非法的判断。

f <- function(x, c){
if (length(c) != 1) stop(“vector c not allowed”)
return((x + c) ^ 2)
}

  

向量输入,矩阵输出:当使用的函数在输入一个值的返回值本身就是向量时,输入一个向量时返回的就应该是矩阵了。而直接使用函数进行运算,得到的只是一个一维向量,需要对结果使用matrix()函数进行重新整合。但是有另一种方法可以用,就是sapply()函数(simplify apply的缩写),调用格式为sapply(x, f)输入向量x对其中的每一个元素应用f()函数,并将结果转化为矩阵。

NA与NULL值:

NA存在但未知的值;NULL表示不存在的值,是R的一种特殊对象,没有模式。

筛选(filtering):

生成筛选索引:条件,最终靠布尔值

使用subset()函数筛选:与靠条件生成筛选索引的区别在于处理NA值的方式,普通处理会保留NA值,subset()函数会移除NA值

选择函数which():与subset()函数类似,但是返回值是符合条件值的位置(即索引编号)

向量化的ifelse()函数:

调用形式:ifelse(b, u, v),其中b为布尔值向量,u, v为向量。函数返回值为向量,如果b[i]为真,则返回值的第i个元素为u[i]如果b[i]为假,则返回值的第i个元素为v[i]。

可以利用ifelse()函数对向量进行重编码,对于2种以上的编码方式,可以考虑嵌套:

#ifelse()函数的嵌套,将g中M, F, I分别重编码为1, 2, 3
g <- c(“M”, “F”, “F”, “I”, “M”, “M”, “F”)
ifelse(g == “M”, 1, ifelse(g == “F”, 2, 3))

  

测试向量相等:

考虑如下代码:

x <- 1:2
y <- c(1, 2) x == y
#返回值:TRUE TRUE 因为“==”是函数,返回向量化的结果
all(x == y)
#返回值:TRUE 因为all判断向量是否都为TRUE
identical(x, y)
#返回值:FALSE 因为identical()函数判断两个对象是否完全一样
typeof(x) #integer
typeof(y) #double

  

向量元素的名称:

name()函数可以指定或查询向量元素的名称:

x <- c(1, 2, 4)
#命名
names(x) <- c(“a”, “b”, “ab”)
#查询
name(x)
#返回值 “a” “b” “ab”

  

名称可以用于索引向量中的元素

关于c()函数的一些需要注意事项:

如果传递到c()函数中的参数有不同类型,则它们将被降级为同一类型,该类型最大限度地保留它们的共同特性;

c()函数对向量有扁平化的效果:

c(5, 2, c(1.5, 6))
# [1] 5.0 2.0 1.5 6.0

  

=========================================================================

向量的特例:矩阵与数组

矩阵是一种特殊的向量,与向量相比,包含了两个附加的属性:行数和列数;而数组是更一般的矩阵,高维数组包含了不止行数和列数两个属性。

创建矩阵:

考虑以下代码:

> y <- matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2)
> y [, 1] [, 2] [1, ] 1 3 [2, ] 2 4 > m <- matrix(c(1, 2, 3, 4), nrow = 2, byrow = TRUE)
> m [, 1] [, 2] [1, ] 1 2 [2, ] 3 4

  

需要注意的是,在产生矩阵m的时候,数据按行填充(即数据输入顺序),而R在存储时仍然是按列存储。

一般矩阵运算:

线性代数运算:注意矩阵乘法使用”%*%”

矩阵索引:类似于向量索引用法,可以对子矩阵进行提取、赋值以及删除。

矩阵元素筛选:与向量的筛选类似,通过条件计算布尔值进行筛选,需要注意避免意外降维。

对矩阵的行和列调用函数:

使用apply()函数:调用一般格式:

apply(m, dimcode, f, fargs)

  

apply()函数调用的函数返回的是一个包含k个元素的向量,那么默认返回的结果就有k行,必要时可以使用转置函数t()对结果进行处理。m是矩阵;dimcode是维度编号:1对行应用函数,2对列应用函数;f是应用的函数;fargs是f的可选参数集。

注意apply()函数不一定能使程序运行加快。其优点在于使程序紧凑,便于阅读和修改,并且避免产生使用循环语句时可能带来的bug。

增加或删除矩阵的行或列:

若要删除,将对应行或列赋值为NULL即可,或者使用”-”加索引(参考向量的用法);若要增加行或列,使用rbind()函数或者cbind()函数。

注意不要在循环中使用rbind()函数或者cbind()函数,因为重复创建新矩阵会减低程序速度,所以这种做法不可取。较好的解决办法是在循环开始前创建一个大矩阵,循环过程中逐行逐列对矩阵进行赋值,这样就避免了循环过程中每次进行耗时的矩阵内存分配。

向量与矩阵的差异:

矩阵也是向量,因此可以用length()函数求长度。另一方面,从面向对象编程的角度来说,矩阵类(matrix class)是实际存在的。可以用dim()函数访问矩阵类的属性(行数和列数),可以用nrow()和ncol()函数分别访问矩阵的行数和列数(实际上都是对dim()函数的简单封装)。这两个函数一般用于写以矩阵为参数的通用库函数,可以不需额外参数输入矩阵的行数和列数。

避免意外降维:

两种方式:若使用索引方式提取子矩阵,设置参数drop = FALSE即可(注意”[”实际上也是函数,drop是这个函数的一个参数);若选择先提取子矩阵再处理,可以使用as.matrix()函数将被降维成向量的对象转成矩阵对象。

z <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8), nrow = 4)
#索引方式设置drop参数防止降维
r <- z[2, , drop = FALSE]
#使用as.matrix()函数
u <- z[2, ]
v <- as.matrix(u)

  

矩阵的行和列的命名问题:

rownames()函数与colnames()函数

高维数组:

以一个简单的三维数组为例:

#先生成两个矩阵,作为数组的第一层和第二层
firsttest <- matrix(c(46, 21, 50, 30, 25, 50), nrow = 3)
secondtest <- matrix(c(46, 41, 50, 43, 35, 50), nrow = 3)
#生成一个三维数组,dim参数的三个数字分别代表行数、列数和层数
tests <- array(data = c(firsttest, secondtest), dim = c(3, 2, 2))

  

=========================================================================

数据框与面向对象编程的基础:列表

R中的列表与Python中的字典、Perl中的哈希表、C中的结构体(struct)类型类似。

创建列表:

#创建一个简单的列表
j <- list(name = “Joe”, salary = 55000, union = TRUE)
#使用标签的时候,在不引起歧义的情况下,可以简写
j$sal
#列表实际也是向量,可以使用vector()函数创建
z <- vector(mode = “list”)
z[[“abc”]] <- 3

  

列表的常规操作:

列表索引:注意以下代码:

#提取列表组件三种方法
j1 <- j$salary
j2 <- j[[“salary”]]
j3 <- j[[2]]
#以上方法效果相同,都是提取列表j中的第二个组件,返回值的类型是组件本身的类型 #提取子列表
j4 <- j[salary]
j5 <- j[2]
#以上两种方法效果相同,提取了列表j的一个子列表,返回值的类型是列表

  

增加或删除列表元素:

增加列表元素:直接使用索引即可增加列表组件,具体有5种方式见上面代码,既可以添加单个组件,也可以添加子列表分别作为列表的多个组件。

删除列表元素:直接将待删除的组件赋值为NULL即可。注意删除中间组件的时候,后面的组件的索引全部减1

获取列表长度:

length()函数可以得到列表组件的个数。因为列表是向量。

访问列表元素和值:

函数names()可以获取列表各元素的标签;

函数unlist()可以获取列表的值,返回值是一个向量,类型最大程度保留所有元素的共同特性。一般来说,各种类型的优先级排序是NULL<raw<逻辑类型<整型<实数类型<复数类型<列表<表达式(把配对列表(pairlist)当作普通列表)

在列表上使用apply系列函数:

lapply()函数和sapply()函数的使用:

lapply()(代表list apply)函数与矩阵的apply()函数用法类似,对列表(或强制转换成列表的向量)的每个组件执行给定的函数,并返回另一个列表。

在某些情况下,lapply()函数返回的列表可以转化为矩阵或向量的形式。这时候可以选择使用sapply()(代表simplified [l]apply)

递归型列表:

列表是递归的(recursive),即列表的组件也可以是列表。

拼接函数c()有一个可选参数recursive,决定在拼接列表的时候,是否吧原列表“压平”,就是把所有组件的元素都提取出来,组合成一个向量。

> c(list(a = 1, b = 2, c = list(d = 5, e = 9)))
$a [1] 1 $b [1] 2 $c
$c$d [1] 5 $c$e [1] 9 > c(list(a = 1, b = 2, c = list(d = 5, e = 9)), recursive = TRUE)
a b c.d c.e 1 2 5 9

  

=========================================================================

数据框

从直观上看,数据框类似矩阵,有行和列两个维度,然而数据框与矩阵不同的是,数据框的每一列可以是不同模式。就技术层面而言,数据框是每个组件长度都相等的列表。

创建数据框:

注意参数stringsAsFactors = FALSE的使用。

访问数据框:三种方式:

#类似列表的方式访问组件
d[[1]]
d$kids
#类似矩阵的方式按列访问
d[, 1]

  

str()函数可以查看数据框的内部结构。注意以上三种方式的返回一致,都是对数据框的某列进行访问。

一般来说,采用名称索引的方式更加安全,但是在写R包时常常采用矩阵式记号。

其他矩阵式操作:

提取子数据框:数据框可以看做是行和列组成的,因此可以按行或列提取子数据框。同样的,如果需要避免意外降维,需要设定drop = FALSE

a <- examquiz[2:5, 2]
b <- examquiz[2:5, 2, drop = FALSE]
class(a)
# "numeric"
class(b)
# "data.frame"

  

使用rbind()和cbind()等函数:添加新行的时候,添加的行可以是数据框也可以是列表,要求行数相同。添加新列,可以利用数据框的列表属性添加,注意如果新增列长度与数据框不同会自动循环补齐。缺失值的处理:有时需要显式地设置na.rm = TRUE明确处理缺失值,否则会使函数返回结果也是NA。灵活使用subset()函数进行条件筛选,默认na.rm = TRUE。另外如果只是删除缺失值,使用complete.cases()函数可以作为条件筛选完整观测。

使用apply()函数:如果数据框的每一列的数据类型相同,可以对数据框使用apply()函数(此时可以将数据框看作是矩阵)。

合并数据框:

merge()函数,可以将两张表根据某个共同变量的值组合到一起。

需要注意的是,选择匹配变量时要小心,当一个变量内有重复值的时候,很有可能产生错误的结果(相当于原本的一对一变成了一对多)。

应用于数据框的函数:

在数据框上应用lapply()和sapply()函数:数据框是列表的特例,数据框的列构成了列表的组件。在数据框上应用lapply()函数,指定的函数是f()。f()函数会作用于数据框的每一列,然后将返回值置于一个列表中。

=========================================================================

因子和表

因子(factor)的设计思想来源于统计学中的名义变量(nominal variables),或称之为分类变量(categorical variables),这种变量的值本质不是数字,而是对应为分类。

本章的表是频数表和列联表的总称,将探讨一些常用运算。

因子与水平:

R中,因子可以简单地看作一个附加了更多信息的向量。这额外的信息包括向量中不同值的记录,称为“水平”(level)。

因子的长度定义为数据的长度,而不是水平的个数。

如果预测到未来有其他水平,需要提前插入,否则后面通过插入新的数据来插入新的水平是行不通的。

因子的常用函数:

tapply()函数:调用方式:tapply(x, f, g)。其中x为因子向量;f为因子或因子列表;g为函数。tapply()函数执行的操作是:(暂时)将x分组,每组对应一个因子水平(或在多重因子的情况下对应一组因子水平的组合),得到x的子向量,然后这些子向量应用函数g()。

> #tapply()应用示例
> ages <- c(25, 26, 55, 37, 21, 42)
> affils <- c(“R”, “D”, “D”, “R”, “U”, “D”)
> tapply(ages, affils, mean)
D R U 41 31 21

  

以上是一种简单的形式,只靠一组因子进行分类。如果需要两种以上的因子组合作为控制条件,只需要把f替换成由因子组合成的列表:

#假设数据框d中含有三列,income, gender, over25,以后两者为控制条件对income应用mean函数
tapply(d$income, list(d$gender, d$over25), mean)

  

split()函数:将向量分割为组,相当于tapply()函数的第一步,而省略了后续的应用函数操作。基本调用形式:split(x, f),其中x可以是向量或数据框(tapply()函数中不可以是数据框),f为因子或因子列表。返回值是一个列表。

by()函数:与tapply()函数在某种程度上类似,都是先分组,再对每组调用函数。tapply()函数要求输入数据必须为向量,而by()函数可以是数据框或矩阵。

#以Gender为控制条件,分别对第2列和第3列进行回归分析
aba <- read.csv(“abalone.data”, header = TRUE)
by(aba, aba$Gender, function(m) lm(m[, 2] ~ m[, 3]))

  

表的操作:通常使用table()函数创建表(频数表与列联表)

计算边界值:addmargins()函数

获得维度名称和水平值:dimnames()函数

表也可以采用数据框的形式表达,使用as.data.frame()函数即可

其他与因子和表有关的函数:

aggregate()函数:对分组中的每一个变量调用tapply()函数

cut()函数:是生成因子的一种常用方法,尤其是常用于表的操作。调用方式:cut(x, b, labels = FALSE)输入向量x,由向量b定义一组区间,返回x每个元素落入区间组成的向量。简单来说,就是重编码,这里区间b一般是左开右闭区间。

R语言编程艺术(2)R中的数据结构的更多相关文章

  1. R语言编程艺术(5)R语言编程进阶

    本文对应<R语言编程艺术> 第14章:性能提升:速度和内存: 第15章:R与其他语言的接口: 第16章:R语言并行计算 ================================== ...

  2. R语言编程艺术(4)R对数据、文件、字符串以及图形的处理

    本文对应<R语言编程艺术> 第8章:数学运算与模拟: 第10章:输入与输出: 第11章:字符串操作: 第12章:绘图 =================================== ...

  3. R语言编程艺术(3)R语言编程基础

    本文对应<R语言编程艺术> 第7章:R语言编程结构: 第9章:面向对象的编程: 第13章:调试 ============================================== ...

  4. R语言编程艺术(1)快速入门

    这本书与手上其他的R语言参考书不同,主要从编程角度阐释R语言,而不是从统计角度.因为之前并没有深刻考虑这些,因此写出的代码往往是一条条命令的集合,并不像是“程序”,因此,希望通过学习这本书,能提高编程 ...

  5. <R语言编程艺术>的一个错误以及矩阵相加

    R语言编程艺术讲矩阵这节时,举了个随机噪声模糊罗斯福总统画像的例子.但是里面似乎有个错误,例子本意是区域外的值保持不变,而选定区域的值加一个随机值,但是实际情况是两个行列不相等的矩阵相加,会报错,如果 ...

  6. R语言编程艺术# 矩阵(matrix)和数组(array)

    矩阵(matrix)是一种特殊的向量,包含两个附加的属性:行数和列数.所以矩阵也是和向量一样,有模式(数据类型)的概念.(但反过来,向量却不能看作是只有一列或一行的矩阵. 数组(array)是R里更一 ...

  7. R语言编程艺术#02#矩阵(matrix)和数组(array)

    矩阵(matrix)是一种特殊的向量,包含两个附加的属性:行数和列数.所以矩阵也是和向量一样,有模式(数据类型)的概念.(但反过来,向量却不能看作是只有一列或一行的矩阵. 数组(array)是R里更一 ...

  8. R语言编程艺术# 数据类型向量(vector)

    R语言最基本的数据类型-向量(vector) 1.插入向量元素,同一向量中的所有的元素必须是相同的模式(数据类型),如整型.数值型(浮点数).字符型(字符串).逻辑型.复数型等.查看变量的类型可以用t ...

  9. R语言编程艺术#03#列表(list)

    向量的元素要求都是同类型的,而列表(list)与向量不同,可以组合多个不同类型的对象.类似于C语言中的结构体(struct)类型. 1.创建列表 从技术上讲,列表就是向理.之前我们接触过的普通向量都称 ...

随机推荐

  1. 小记 百度地图 soso地图 经纬度偏移

    项目里遇到了这么个问题,数据库原有数据是微信上用的,所以是soso地图坐标, 但是现在要做百度地图,坐标偏移严重,网上找了也没说偏移多少,自己手动测试10多分钟,得到个大概值,反正差不多就行了. so ...

  2. 凸优化(Convex Optimization)浅析

    本博客已经迁往http://www.kemaswill.com/, 博客园这边也会继续更新, 欢迎关注~ 在机器学习中, 很多情况下我们都需要求得一个 问题的全局最优值(global optimum) ...

  3. 在Emacs中画思维导图

    是的,你没有看错.其实,不只画思维导图,画结构图.流程图等,都可以.那怎么办呢?就是借助 Graphviz . Graphviz 可以方便地表达概念之间的联系,因此用它画思维导图是可行的,再加上它是个 ...

  4. kafka入门(1)- 基本概念

    Kafka is a distributed,partitioned,replicated commit logservice Kafka提供了类似于JMS的特性,但是在设计实现上完全不同,并不是JM ...

  5. SpringBoot与异步任务、定时任务、邮件任务

    异步任务 在需要开启异步的服务加上注解:@Async @Service public class AsyncService { //告诉SpringBoot这是一个异步任务,SpringBoot会自动 ...

  6. python概念-各类绑定的概念和property的变态一面

    # 编辑者:闫龙 # 1.什么是绑定到对象的方法,如何定义,如何调用,给谁用?有什么特性 #在类中定义的(self)方法都是绑定到对象的方法 #定义 class a: def b(self):#绑定到 ...

  7. JS设计模式——6.方法的链式调用

    什么是链式调用 这个很容易理解,例如: $(this).setStyle('color', 'red').show(); 分解链式调用 链式调用其实是两个部分: 1.操作对象(也就是被操作的DOM元素 ...

  8. Servlet笔记6--Servlet程序改进

    第一步改进,GenericServlet: 我们目前所有放入Servlet类直接实现了javax.servlet.Servlet接口,但是这个接口中有很多方法是目前不需要的,我们可能只需要编写serv ...

  9. 通过PDB文件实现非嵌入式的c++反射

    上一篇blog我阐述了一种实现非嵌入式的反射的基本思路.相比于通过宏和模板实现,这种非嵌入的反射的优点是不需要写额外的代码来记录meta信息. 首先,为了在c++中实现反射系统,我认为需要解决以下两个 ...

  10. mysql命令补全工具

    需要在linux中下载mysql插件. 安装mysql插件 yum -y install epel-release python-pip python-devel pip install mycli ...