R语言笔记1:初识数据结构

简介

R语言是一套开源的数据分析解决方案。R语言中提供了多种存储数据的对象类型,包括标量(R语言中的标量是由向量的形式表示,可以说R语言没有标量),向量,矩阵,数组,数据框,列表还有因子(与前几者并非完全独立)。作为初学者,我主要关注这些结构的三个方面:创建,取值,额外的问题。

1.“标量”

所谓的标量就是只含一个元素的向量,多用于储存常量。示例如下:

> c <- 3
> c
[1] 3

可以看出来c是一个只存储了1个元素的向量。

2.向量

向量是用于储存数值型,字符型或者逻辑型的一维数组。数组中的值类型必须保持一致。最常见的创建向量的方式是通过函数 : c( ) 。值得注意的是,R的下标不从0开始而从1开始。

> #create a vector
> #vector(mode = "logical", length = 0)
> #c()
> v <- c(11,22,33,44,55,66)
> v
[1] 11 22 33 44 55 66

向量的创建

可以使用冒号生成数值序列来创建

> v <- c(1:5)
> v
[1]  1  2  3  4  5

也可以结合运算表达式来创建

> v <- c(1:5*3)
> v
[1]  3  6  9 12 15

有趣的是,下面这种写法的结果和上面的大相径庭,不仅仅是数值的大小上,原因与运算顺序有关,在此不再多叙。
v <- c(1:5**3)

获取值的方式

通过下标获取:

> v[3]
[1] 9

通过c函数的下标序列获取:

> v[c(1,3,5)]
[1]  3  9 15

通过等值序列获取:

> v[2:4]
[1]  6  9 12

通过c函数和等值序列获取:

> v[c(1:3)]
[1] 3 6 9

也可以结合一些运算获得值,在此不一一列举。

一些特殊情况

想要获得下标为0的元素不会报错,不会返回值:

> v[0]
numeric(0)

越界也不会报错,会返回NA(不存在的值):

> v[7]
[1] NA

使用等值序列的下标获得值时可以颠倒顺序,而且不会报错:

> v[7:2]
[1] NA NA 15 12  9  6

但是如果冒号左右缺失数据便会报错:

#错误的方式
v[3:]
v[:]
v[:2]

这些也是与Python的数组以及NumPy数组有区别的地方。

3.矩阵

矩阵其实是一个二维数组。一般创建格式为:
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,dimnames = NULL)

矩阵的创建

可以使用数学表达式或者等值序列进行创建。

> x <- matrix (1*2:11*2,nrow = 2)
> x
     [,1] [,2] [,3] [,4] [,5]
[1,]    4    8   12   16   20
[2,]    6   10   14   18   22

矩阵的创建默认是按列填充,将矩阵创建函数中的参数设为byrow = TRUE便可按行填充。dimnames用于声明行列名。参数位置不作要求。示例如下:

>  x <- matrix (1:10,nrow = 2,ncol =5,dimnames = list (c("R1","R2"),c("C1","C2","C3","C4","C5")),byrow =TRUE)  
> x
   C1 C2 C3 C4 C5
R1  1  2  3  4  5
R2  6  7  8  9 10

有趣的是,创建时规定了具体的nrow与ncol数值后如果实际传入的数值数小于nrow * ncol,则会用传入的值循环补上缺少的位置,但是会产生警告:

> x <- matrix (1*2:8*2,nrow = 2,ncol =5,dimnames = list (c("R1","R2"),c("C1","C2","C3","C4","C5")))  
Warning message:
In matrix(1 * 2:8 * 2, nrow = 2, ncol = 5, dimnames = list(c("R1",  :
  数据长度[7]不是矩阵行数[2]的整倍
> x
   C1 C2 C3 C4 C5
R1  4  8 12 16  6
R2  6 10 14  4  8

获取值的方式

以该矩阵为例:

> x <- matrix (1:10,nrow = 2,dimnames = list (c("R1","R2"),c("C1","C2","C3","C4","C5")))  
> x
   C1 C2 C3 C4 C5
R1  1  3  5  7  9
R2  2  4  6  8 10

单下标获取:根据所有数值中该值的位置获取

x[2]
[1] 2

双下标获取:根据二维坐标获取

> x[2,3]
[1] 6

结合c函数下标序列:

> x[1,c(3,5)]
C3 C5 
 5  9 

结合等值序列:

> x[1,c(3:5)]
C3 C4 C5 
 5  7  9 

获得行/列:

> x[1,]
C1 C2 C3 C4 C5 
 1  3  5  7  9 
> x[,3]
R1 R2 
 5  6 

一些特殊情况

同向量一样,越界会用NA填补,可以使用倒序等值序列,等值序列冒号两端必须要有数字:

> x[11]
[1] NA
> x[11:2]
 [1] NA 10  9  8  7  6  5  4  3  2

如果双下标确定的二维坐标实际不存在,则报错

> x[3,2]
Error in x[3, 2] : 下标出界

4.数组

数组是可以在多维层次上存储数据的对象。可以认为是多维的矩阵。
array(data = NA, dim = length(data), dimnames = NULL)

数组的创建

使用array函数创建,data参数代表传入的数据,dim表示维度,dimnames为维度名称。示例如下:

> dim1 <- c("A1","A2")
> dim2 <- c("B1","B2","B3")
> dim3 <- c("C1","C2","C3","C4")
> z <- array(1:24,c(2,3,4),dimnames = list(dim1,dim2,dim3))
> z
, , C1

   B1 B2 B3
A1  1  3  5
A2  2  4  6

, , C2

   B1 B2 B3
A1  7  9 11
A2  8 10 12

, , C3

   B1 B2 B3
A1 13 15 17
A2 14 16 18

, , C4

   B1 B2 B3
A1 19 21 23
A2 20 22 24

可以看出多维就是向量之间的组合,数值按行列填充,而且维度的层次是由里到外:2,3,4中2是行,3是列,4是外部的行。这与使用NumPy数组创建的矩阵以及部分语言中的利用数组创建的矩阵都有差别。
NumPy例子如下,可以看出有两行,每行内有三小行,四列:

>import numpy
>numpy.empty((2,3,4))
array([[[  3.10503618e+231,   1.29074048e-231,   2.20258734e-314,
           2.20282008e-314],
        [  2.20475013e-314,   2.20276932e-314,   2.20247903e-314,
           2.20272866e-314],
        [  2.20274681e-314,   2.20730875e-314,   2.20730883e-314,
           2.20250846e-314]],

       [[  2.20853208e-314,   2.20279100e-314,   2.20260412e-314,
           2.20320740e-314],
        [  2.20265303e-314,   2.20274598e-314,   2.20323984e-314,
           2.20250954e-314],
        [  2.20296227e-314,   2.20301979e-314,   2.22325913e-314,
           1.66880762e-308]]])

数组同矩阵一样,也会循环补上缺失值:

> dim1 <- c("A1","A2")
> dim2 <- c("B1","B2","B3")
> dim3 <- c("C1","C2","C3","C4")
> z <- array(1:8,c(2,3,4),dimnames = list(dim1,dim2,dim3))
> z
, , C1

   B1 B2 B3
A1  1  3  5
A2  2  4  6

, , C2

   B1 B2 B3
A1  7  1  3
A2  8  2  4

, , C3

   B1 B2 B3
A1  5  7  1
A2  6  8  2

, , C4

   B1 B2 B3
A1  3  5  7
A2  4  6  8

获取值的方式

下标数等于维度数(使用第一个例子作为示范):

> z[1,2,3]
[1] 15

缺失不同的下标可以获得不同维度的组合:

> z[1,,]
   C1 C2 C3 C4
B1  1  7 13 19
B2  3  9 15 21
B3  5 11 17 23
> z[1,2,]
C1 C2 C3 C4 
 3  9 15 21 
> z[,2,]
   C1 C2 C3 C4
A1  3  9 15 21
A2  4 10 16 22
> z[,,]
, , C1

   B1 B2 B3
A1  1  3  5
A2  2  4  6

, , C2

   B1 B2 B3
A1  7  9 11
A2  8 10 12

, , C3

   B1 B2 B3
A1 13 15 17
A2 14 16 18

, , C4

   B1 B2 B3
A1 19 21 23
A2 20 22 24

但是下标数目不对会报错:

> z[1,2]
Error in z[1, 2] : 量度数目不对
> z[1,,,]
Error in z[1, , , ] : 量度数目不对
> z[,,,]
Error in z[, , , ] : 量度数目不对

结合表达式或运算式:

> z[1,1:3,4]
B1 B2 B3 
19 21 23 
> z[1,c(1:3),4]
B1 B2 B3 
19 21 23 

5.数据框

数据框是表或者二维阵列状结构,也被称作表。其中每一列包含一个变量的值,并且每一行包含来自每一列的一组值。特点是列名非空,行名唯一,每列数据项个数相同。尽管每一列的模式必须唯一,但是数据框可以将不同模式的列组合在一起。
data.frame(..., row.names = NULL, check.rows = FALSE, check.names = TRUE, fix.empty.names = TRUE, stringsAsFactors = default.stringsAsFactors())

数据框的创建

> studentID <- c(1,2,3,4)
> age <- c(16,17,18,19)
> country <- c("China","USA","Canada","Japan")
> status <- c("Handsome","Poor","Smart","Poor")
> sd <- data.frame(studentID,age,country,status)
> sd
  studentID age country   status
1         1  16   China Handsome
2         2  17     USA     Poor
3         3  18  Canada    Smart
4         4  19   Japan     Poor

从这个简单的例子可以看出来数据框可以认为是含有相同个数数据项的向量的组合。check.rows与check.names等参数可以用来检查不一致或重复,详情可以查看R文档。

获取值的方式

单下标获取某一列:

> sd[3]
  country
1   China
2     USA
3  Canada
4   Japan

双下标获取单个数据项:

> sd[1,2]
[1] 16

单等值序列获取多列:

> sd[1:3]
  studentID age country
1         1  16   China
2         2  17     USA
3         3  18  Canada
4         4  19   Japan

双等值序列获取表的一部分:

> sd[1:3,2:4]
  age country   status
1  16   China Handsome
2  17     USA     Poor
3  18  Canada    Smart

取行/列/全部:

> sd[2,]
  studentID age country status
2         2  17     USA   Poor
> sd[,3]
[1] China  USA    Canada Japan 
Levels: Canada China Japan USA
> sd[,]
  studentID age country   status
1         1  16   China Handsome
2         2  17     USA     Poor
3         3  18  Canada    Smart
4         4  19   Japan     Poor

也可以使用 $ 取获得某一列:

> sd$age
[1] 16 17 18 19

也可以结合c函数:

> sd[c(1,2),c(3,4)]
  country   status
1   China Handsome
2     USA     Poor

额外的问题1:

关于多下标,有很多有趣的地方。这种表不是二维的吗,多下标取的是哪里的值呢?以上面的数据框为例,我们实践一下:
1.前两个下标是坐标

> sd[1,2,0]
  age
1  16
> sd[1,2,1]
[1] 16
> sd[1,2,2]
[1] 16
> sd[1,2,3]
[1] 16
> sd[1,2,4]
[1] 16
> sd[1,2,5]
[1] 16
> sd[1,2,555]
[1] 16
> sd[1,2,-1]
[1] 16
> sd[1,2,-2]
[1] 16

前两个下标是坐标,第三个下标取0时展示了该数值的行与列的位置,取非0的值时并无作用。

  1. 第一个和第三个数字是下标
> sd[1,0,2]
data frame with 0 columns and 1 row
> sd[1,1,2]
[1] 1

显然,R还是把前两个当成是二维坐标,最后的2并无作用。

> sd[1,-1,2]
$age
[1] 16

$country
[1] China
Levels: Canada China Japan USA

$status
[1] Handsome
Levels: Handsome Poor Smart

然而,这个输出十分有意思。让我们多实验几个数字。

> sd[1,-1,1]
$age
[1] 16

$country
[1] China
Levels: Canada China Japan USA

$status
[1] Handsome
Levels: Handsome Poor Smart

> sd[1,-1,-1]
$age
[1] 16

$country
[1] China
Levels: Canada China Japan USA

$status
[1] Handsome
Levels: Handsome Poor Smart

> sd[2,-1,2]
$age
[1] 17

$country
[1] USA
Levels: Canada China Japan USA

$status
[1] Poor
Levels: Handsome Poor Smart

> sd[2,-2,3]
$studentID
[1] 2

$country
[1] USA
Levels: Canada China Japan USA

$status
[1] Poor
Levels: Handsome Poor Smart

> sd[-1,2,]
[1] 17 18 19
> sd[-1,3,4]
[1] USA    Canada Japan 
Levels: Canada China Japan USA

> sd[-2,-1,3]
  age country   status
1  16   China Handsome
3  18  Canada    Smart
4  19   Japan     Poor
> sd[-2,-2,3]
  studentID country   status
1         1   China Handsome
3         3  Canada    Smart
4         4   Japan     Poor

可以看出来第三个下标完全没有用。。
第一个下标为x(正),第二个下标为y(负)时,会展示出第x行除去第y列属性的所有数值项。
第一个下标为x(负),第二个下标为y(正)时,会展示出第y列除去第x行属性的所有数值项。
第一个下标为x(负),第二个下标为y(负)时,会展示出整个表除去第x行以及第y列属性的所有数值项。
最后,四个下标会报错。

> sd[,,,,]
Error in `[.data.frame`(sd, , , , , ) : 参数没有用(, )
  1. 越界问题
    仅当单下标越界时会报错。双下标取值越界会返回NULL,和前面的数据结构返回NA不太一样。
> sd[0]
data frame with 0 columns and 4 rows
> sd[7]
Error in `[.data.frame`(sd, 7) : undefined columns selected
> sd[1,6]
NULL

另外,前两个下标是负数且均绝对值越界时,返回的是整个表(数据框)。
前两个下标是负数且前者绝对值越界时,返回的是表除去后者绝对值列数的所有数据值。
前两个下标是负数且后者绝对值越界时,返回的是表除去前者绝对值行数的所有数据值。

> sd[-6,-1]
  age country   status
1  16   China Handsome
2  17     USA     Poor
3  18  Canada    Smart
4  19   Japan     Poor
> sd[-5,-6]
  studentID age country   status
1         1  16   China Handsome
2         2  17     USA     Poor
3         3  18  Canada    Smart
4         4  19   Japan     Poor
> sd[-2,-7]
  studentID age country   status
1         1  16   China Handsome
3         3  18  Canada    Smart
4         4  19   Japan     Poor

语言表达不太好,大家可以自行理解。具体的原因等我参考R文档后再补充

额外的问题2:

  1. 绑定与解绑
    一直使用 $ 得到一列属性是一种简单但不简洁的做法,可以使用attach函数进行绑定操作。attach( ) 函数将数据框添加到R的搜索路径中,R在遇到一个变量名后,将检查搜索路径中的数据框。
> attach(sd)
The following objects are masked _by_ .GlobalEnv:

    age, country, status, studentID

> summary(age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  16.00   16.75   17.50   17.50   18.25   19.00 
> plot(studentID,age)
> detach(sd)

在上面这个例子中,绑定了sd后,summary函数使用的age就是特指sd中的age列。
detach( )将数据框从搜索路径中移除,可以省略,但使用它是一种好的习惯。(来也匆匆,去也冲冲)

  1. with函数
    attach有两个不太令人满意的地方:假如环境中已经有了一个名为age的对象,原始对象会取得优先权,绑定并没有实际效果;多层的绑定/解绑会使结构难以处理。
    with函数中{ }结构里所有的语句都只针对其中的数据框执行,无需担心名称冲突,赋值也同样如此,在结构体外不起作用,但是可以使用 <<-将对象保存在全局变量中。
> with(sd,{
+     info1 <- summary(age)
+     info2 <<- summary(age)
+     info1
+ })
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  16.00   16.75   17.50   17.50   18.25   19.00 

> info1
错误: 找不到对象'info1'
> info2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  16.00   16.75   17.50   17.50   18.25   19.00 

6.列表

列表包括不同类型的元素和对象,是他们的有序集合。
为什么要学会使用列表:

  • 许多R语言函数默认返回列表类型的对象。
  • 列表允许使用一种简单的方式组织和重新调试不想干的信息。

列表的创建:

由下面的例子可以看出列表就是不同成分见的有序组合。

> a <- c(1,2,3,4)
> b <- c("Type1","Type2","Type3")
> c <- matrix(1:10,nrow = 5)
> d <- "Come on boy"
> l <- list(a,b,c,d)
> l
[[1]]
[1] 1 2 3 4

[[2]]
[1] "Type1" "Type2" "Type3"

[[3]]
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8
[4,]    4    9
[5,]    5   10

[[4]]
[1] "Come on boy"

可以指明不同成分的名称,通过成分名访问数据。如果成分名是数字,需要加上引号,也就是说R认为成分名必须是字符串。

> a <- c(1,2,3,4)
> b <- c("Type1","Type2","Type3")
> c <- matrix(1:10,nrow = 5)
> d <- "Come on boy"
> l <- list(number = a,data =b,c,d)
> l
$number
[1] 1 2 3 4

$data
[1] "Type1" "Type2" "Type3"

[[3]]
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8
[4,]    4    9
[5,]    5   10

[[4]]
[1] "Come on boy"

上面的例子中,number成分存在一个对象,在列表中编号是 1,data成分存在一个对象,在列表中编号是2,编号3,4者无显式成分名。
个人认为成分名尽量与数据对象一对一,不建议以下这种形式:
l <- list(age =a,data =c(b,c,d))
列表的其他创建函数可见R文档。

获取值的方式:

通过单下标获取:

> l[1]
$number
[1] 1 2 3 4

> l[4]
[[1]]
[1] "Come on boy"

> l[]
$number
[1] 1 2 3 4

$data
[1] "Type1" "Type2" "Type3"

[[3]]
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8
[4,]    4    9
[5,]    5   10

[[4]]
[1] "Come on boy"


结合表达式:

> l[1:3]
$number
[1] 1 2 3 4

$data
[1] "Type1" "Type2" "Type3"

[[3]]
     [,1] [,2]
[1,]    1    6
[2,]    2    7
[3,]    3    8
[4,]    4    9
[5,]    5   10

> l[c(2,4)]
$data
[1] "Type1" "Type2" "Type3"

[[2]]
[1] "Come on boy"

通过成分名:
成分名作为下标中需要加上引号。

> l[data]
Error in l[data] : 类别为'closure'的下标不对
> l["data"]
$data
[1] "Type1" "Type2" "Type3"

> l$number
[1] 1 2 3 4

额外的问题:

除了0下标外不存在的数值项会视为NA:

> l[0]
named list()
> l[7]
$<NA>
NULL

> l["HI"]
$<NA>
NULL

还有一些奇怪的东西大家可以来总结以下

> l[][2]
$data
[1] "Type1" "Type2" "Type3"

> l[][2][]
$data
[1] "Type1" "Type2" "Type3"

> l[][2][][1]
$data
[1] "Type1" "Type2" "Type3"

文中出现的部分函数说明

以下为R文档简述

  • summary( ):summary is a generic function used to produce result summaries of the results of various model fitting functions. The function invokes particular methods which depend on the class of the first argument.
  • str( ):Compactly display the internal structure of an R object, a diagnostic function and an alternative to summary (and to some extent). Ideally, only one line for each ‘basic’ structure is displayed. It is especially well suited to compactly display the (abbreviated) contents of (possibly nested) lists. The idea is to give reasonable output for any R object. It calls args for (non-primitive) function objects.

参考及引用资料

本人不会将以下资料用于商业用途并对其于自己的帮助表示由衷的感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341