项目二、玩扑克牌
第三章 R对象
3.1 原子型向量(atomic vector)
在项目一中我们生成的die
就是一个原子型向量
die <- c(1,2,3,4,5,6);die
## 1 2 3 4 5 6
is.vector(die) #该函数表示测试某对象是否为原子型向量,是返回TRUE,否FALSE
## TRUE
length(die) #返回原子型向量的长度
## 6
R可以识别六种基本类型的原子型向量,分别是:双整型(double)、整型(integer)、字符型(character)、逻辑型(logical)、复数类型(complex)、原始类型(raw)
3.1.1 双整型
die <- c(1,2,3,4,5,6);die #die就是一个双整型向量
## 1 2 3 4 5 6
typeof(die) # 该函数可以返回向量的类型
## "double"
class(die) # 该函数可以返回向量的类型
## "numeric"
我们可以看到同样是查看对象类型但typeof
返回的是双整型,class
返回的是numeric
(数值型)。这其实是一类,只是不同的称呼。双整型是计算机科学的术语,但在数据科学,数值型更符合我们的逻辑。
3.1.2 整型
顾名思义,整型数据的数值不需要小数点成分。我们分析数据用不到这种类型的向量。这里解释下其与双整型的异同。
# 在R中明确设定整型的方法是在数值后加上大写字母L,否则R自动存储为双整型
int <- c(-1L, 2L, 3L); int
## -1 2 3
typeof(int)
## "integer"
那为什么要把数据存储为整型而不是双整型呐?我们用个例子来看下
sqrt(2)^2 - 2 # 提示:sqrt是平方根(square root)的意思,R中许多函数名字都是其英文的缩写,所以记忆这些函数还是有规律可循的~
## 4.440892e-16
round(sqrt(2)^2)-2 #函数round代表四舍五入取整
## 0
按我们正常思考上述结果都应该为0,但却不是,这是因为计算机给双整型对象分配64字节即可以精确到小数点后16位,根号2是无限不循环小数,计算机只能保留至小数点后16位,所以出现了上述的结果。这样的舍入误差也叫做浮点(floating-point)误差,这种情况下的运算叫做浮点运算。
3.1.3 字符型
字符加上双引号,再组合起来构成一个字符型向量。字符型向量中的单个元素称为字符串(string)
text <- c("Hello", "World")
text
## "Hello" "World"
typeof(text)
## "character"
typeof("Hello")
## "character"
一定记住字符串在键入和操作时加上双引号("")
3.1.4 逻辑型
存储TRUE
和FALSE
是R中布尔数据的表现形式。在比对数据时,逻辑型非常有用!
3 > 4
## FALSE
logic <- c(TRUE, FALSE, TRUE)
logic
## TRUE FALSE TRUE
typeof(logic)
## "logical"
typeof(F) # R会默认把T和F当作TRUE和FALSE的简写
## "logical"
3.1.5 复数类型和原始类型
分析数据基本用不到这两种类型,此处就不介绍了~
3.2 属性
属性是附加给原子型向量的额外信息,可以将属性赋予任意一个R对象
attributes(die) #因为die没有任何属性所以返回NULL,意思就是空值
## NULL
一个原子型向量最常见的三种属性是:名称(name)、维度(dim)和类(class)
3.2.1 名称属性
利用names()函数给die
赋予名称属性,这个字符向量的长度要与die
等长
names(die) <- c("one", "two", "three", "four", "five", "six")
names(die)
## "one" "two" "three" "four" "five" "six"
attributes(die)
## $names
## [1] "one" "two" "three" "four" "five" "six"
die # 我们可以发现显示die向量时,名称属性出现在了对应元素上方
## one two three four five six
## 1 2 3 4 5 6
die + 1 # 但名称并不会对向量中的实际值产生影响,比如向量+1,名称不会变的。
## one two three four five six
## 2 3 4 5 6 7
# 可以使用names()函数对名称属性批量修改或删除
names(die) <- c("uno", "dos", "tres", "quatro", "cinco", "seis")
die
## uno dos tres quatro cinco seis
## 1 2 3 4 5 6
# 删除名称属性值,只需将NULL赋予names函数
names(die) <- NULL
die
## 1 2 3 4 5 6
3.2.2 维度属性
原理同上,使用dim()
函数即可
dim(die) <- c(2,3); die
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6
3.3 矩阵
m <- matrix(die, nrow = 2)
m
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6
m <- matrix(die, nrow = 2, byrow = TRUE) # 上面结果可见R默认按列进行数据填充,这个参数byrow = TRUE代表按行填充
m
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
3.4 数组
array
函数生成一个n维数组
args(array) # 查看array函数的参数,发现只有3个
## function (data = NA, dim = length(data), dimnames = NULL)
# 利用arry创建一个三维数组
ar = array(c(11:14, 21:24, 31:34),dim = c(2,2,3));ar #2行2列3个切面
## , , 1
##
## [,1] [,2]
## [1,] 11 13
## [2,] 12 14
## , , 2
## [,1] [,2]
## [1,] 21 23
## [2,] 22 24
## , , 3
## [,1] [,2]
## [1,] 31 33
## [2,] 32 34
练习题:生成扑克牌矩阵
hand <- matrix(c("ace","king","queen","jack","ten",rep("spades",5)),
nrow = 5)
hand
## [,1] [,2]
## [1,] "ace" "spades"
## [2,] "king" "spades"
## [3,] "queen" "spades"
## [4,] "jack" "spades"
## [5,] "ten" "spades"
3.5 类
更改对象的维度并不会改变其类型,但会改变这个对象的class属性
die <- c(1,2,3,4,5,6)
typeof(die)
## "double"
class(die)
## "numeric"
dim(die) <- c(2,3)
typeof(die)
## "double"
class(die)
## "matrix"
运行attributes函数时,对象的class属性并非总是会显示,可以使用class函数专门搜索对象的class属性
die <- c(1,2,3,4,5,6)
dim(die) <- c(2,3)
attributes(die)
## $dim
## [1] 2 3
class(die)
## "matrix"
3.5.1 日期与时间
除了双整型、整型、字符型、逻辑型、复数类型和原始类型外,R的属性系统还能表示更多的数据类型。下面我们来了解下
now <- Sys.time() #该函数的目的是返回当前计算机的时间
now
## "2021-04-16 21:55:42 CST"
typeof(now)
## "double"
class(now)
## "POSIXct" "POSIXt"
没错,返回的感觉是一个字符串,但它的类型是双整型,它的类是POSIXct和POSIXt。POSIXct是一个广泛用于表示日期与时间的框架。在POSIXct框架下,使劲按被表示为自1970年1月1日零点(UTC时间)开始逝去的秒数。比如刚刚我电脑的时间相对这个时间已经过去了1618581342秒,因此在POSIXct下被存储为数值1618581342,所以typeof()
返回的是双整型。
R用单个元素1618581342的双整型向量生成了一个时间对象,为了看到这个向量的本来面目,我们可以把事件对象now的class属性移除。
unclass(now)
## 1618581342
这就很有趣,给一个双整型向量,然后R会将一个包含两个类(POSIXct和POSIXt)的class属性赋给该双整型向量,此属性提醒R,正在处理POSIXct时间,应以特殊方式处理,即返回给我们可以理解的字符串形式
second <- 1234567
second
## 1234567
class(second) <- c("POSIXct", "POSIXt")
second
## "1970-01-15 14:56:07 CST"
至此我们知道R中其实很有特殊的类(class)。如果需要详细了解某个类时查看其帮助文档即可
3.5.2 因子
因子在R中用来存储分类信息,我们可以把因子视为性别类似的概念;它只可以去某些特定的值(男性或女性),而这些值之间可能有一些特殊的顺序规定(比如女士优先)。该特性非常适合记录分类变量如肿瘤、非肿瘤。
gender <- factor(c('male','female','female','male'))
typeof(gender) #R会将向量中的值重新编码为一串整数值并存储在一个整型向量中
## "integer"
attributes(gender)
## $levels
## [1] "female" "male"
## $class
## [1] "factor"
unclass(gender) # unclass函数,可以看R到底如何存储因子的
## [1] 2 1 1 2
## attr(,"levels")
## [1] "female" "male"
gender #R显示因子因子时用了levels属性,即1显示为female,2显示为male
## [1] male female female male
## Levels: female male
上述结果可见,向factor
函数赋值一个向量可生成因子。R会将向量中的值重新编码为一串整数值并存储在一个整型向量中。此外R还会将一个levels属性和一个class属性添加到该整型向量中,levels
属性包含显示因子值的一组标签,而class
属性包含类:factor。
因子的存在,使统计模型中加入分类变量很简单,因为这些分类变量已经被编码成一些数值。
as.character(gender) #强制将因子转换为字符串
## "male" "female" "female" "male"
3.6 强制转换
向量、矩阵、数值均只能存储单一类型数据:字符串型>数值型>逻辑型
a <- c(1,“TRUE”)
a
## “1” “TRUE”
b <- c(1,TRUE) #逻辑型中默认TRUE=1, FALSE=0
b
## 1 1
sum(c(TRUE,TRUE,FALSE,FALSE)) #会变成sum(c(1,1,0,0)),就是计算包含几个TRUE值
## 2
mean(c(TRUE,TRUE,FALSE,FALSE)) #mean函数计算TRUE值所占比例
## 0.5
类型之间强制转换需要用到as
系列函数
as.character(1)
## "1"
as.logical(1)
## TRUE
as.numeric(FALSE)
## 0
3.7 列表
一维集合,列表将某些具体的值组织起来,而是组织R对象
list1 <- list(100:130, "R", list(TRUE, FALSE))
list1
## [[1]]
## [1] 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
## [19] 118 119 120 121 122 123 124 125 126 127 128 129 130
## [[2]]
## [1] "R"
## [[3]]
## [[3]][[1]]
## [1] TRUE
## [[3]][[2]]
## [1] FALSE
[[]]
代表来自列表中的第几个元素,[]
代表来着这个元素的哪一个子元素。
练习:创建点数为1的红桃A
card <- list('ace', 'hearts', 1)
card
## [[1]]
## [1] "ace"
## [[2]]
## [1] "hearts"
## [[3]]
## [1] 1
3.8 数据框
数据库是列表的二维版本,重点:数据框以列为单位组织数据。不同列可以包含不同的数据类型。但数据框中的每一列都必须具有相同的长度。
df <- data.frame(face = c("ace", "two", "six"),
suit = c("clubs", "clubs", "clubs"),
value = c(1, 2, 3))# 需确保每个向量长度相等,face,suit,value均为对象的names属性
df
## face suit value
## ace clubs 1
## two clubs 2
## six clubs 3
此时我们查看df
的类型会发现是一个列表,class
属性为数据框,实际上,每个数据框都有一个具有data.frame类的列表。可以用str
函数查看这个列表或者数据框中,哪些对象被组织到一起了
typeof(df)
## "list"
class(df)
## "data.frame"
str(df)
## 'data.frame': 3 obs. of 3 variables:
## $ face : Factor w/ 3 levels "ace","six","two": 1 3 2
## $ suit : Factor w/ 1 level "clubs": 1 1 1
## $ value: num 1 2 3
重点!!!我们发现R将字符串存储成了因子(因为不做特殊处理R就会自动这样做)。加上这行命令即可实现字符串形式存储
options(stringsAsFactors = FALSE)
df <- data.frame(face = c("ace", "two", "six"),
suit = c("clubs", "clubs", "clubs"),
value = c(1, 2, 3))
str(df)
## 'data.frame': 3 obs. of 3 variables:
## $ face : chr "ace" "two" "six"
## $ suit : chr "clubs" "clubs" "clubs"
## $ value: num 1 2 3
至此我们可以利用数据框来生成一副扑克牌
deck <- data.frame(
face = c("king", "queen", "jack", "ten", "nine", "eight", "seven", "six",
"five", "four", "three", "two", "ace", "king", "queen", "jack", "ten",
"nine", "eight", "seven", "six", "five", "four", "three", "two", "ace",
"king", "queen", "jack", "ten", "nine", "eight", "seven", "six", "five",
"four", "three", "two", "ace", "king", "queen", "jack", "ten", "nine",
"eight", "seven", "six", "five", "four", "three", "two", "ace"),
suit = c("spades", "spades", "spades", "spades", "spades", "spades",
"spades", "spades", "spades", "spades", "spades", "spades", "spades",
"clubs", "clubs", "clubs", "clubs", "clubs", "clubs", "clubs", "clubs",
"clubs", "clubs", "clubs", "clubs", "clubs", "diamonds", "diamonds",
"diamonds", "diamonds", "diamonds", "diamonds", "diamonds", "diamonds",
"diamonds", "diamonds", "diamonds", "diamonds", "diamonds", "hearts",
"hearts", "hearts", "hearts", "hearts", "hearts", "hearts", "hearts",
"hearts", "hearts", "hearts", "hearts", "hearts"),
value = c(13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
)
deck
## face suit value
## 1 king spades 13
## 2 queen spades 12
## 3 jack spades 11
## 4 ten spades 10
## 5 nine spades 9
## 6 eight spades 8
## 7 seven spades 7
## 8 six spades 6
## 9 five spades 5
## 10 four spades 4
## 等等
但每次想生成扑克牌都输入这么多的数据,非常繁琐且易出错,所以我们可以把这个数据存储为文件,用的时候读取即可。
3.9 保存和读取数据
write.csv(deck,file = 'deck.csv', row.names = F) #保存deck数据框,文件命名为deck.csv,不保存行名
deck <- read.csv('deck.csv',header = T)
head(deck) #查看前6行
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8
tail(deck) #查看最后6行
tail(deck, 10)#查看最后10行
3.10 总结
R中最常用的数据结构为向量、矩阵、数组、列表和数据框。注意区分他们的异同。