# 数据结构
数据结构是一种组织和存储数据的方式,它使得数据可以更高效地访问和操作。数据结构通常是由基本数据类型组合而成的。例如,数组、链表、栈、队列、树和图等都是数据结构。
R中的数据结构包括 **原子向量**(atomic vector)和**泛型向量**(generic vector)。
- 原子向量是基本数据类型(numeric,character,logical,complex 等)的数组(一维`vector` ,二维`matrix` ,多维`array` )。
- 泛型向量是原子向量的集合,即列表`list` 。
## 属性
在R中任何一个**对象**都具有属性:
- names,dimnames
- length
- dimensions(例如matrices,arrays)
- class
- 自定义属性
在R中有多种方式查看对象的属性,
- `class()` 函数,从面向对象编程的角度,知道其`class` 属性后就可以通过`methods()` 函数查找相应的泛型函数(generic function)对其操作。
```{r}
m1 <- matrix(1:6,nrow = 2)
m1
class(m1)
methods(class="matrix")
```
- `attributes()` 主要是用于列出对象所有已有的属性。
```{r}
attributes(m1)
attr(x = m1,which = "dim") <- c(3,2)
m1
# 或者
a <- structure(
m1,
dim=c(1,6)
)
str(attributes(a))
a
```
大多数操作都会丢失大多数属性。
只有两个属性是默认保留的:
1. *"names"*,一个字符向量,为每个元素命名。
```{r}
# When creating it:
x <- c(a = 1, b = 2, c = 3)
x
attributes(x)
names(x)
# By assigning a character vector to names()
x <- 1:3
names(x) <- c("a", "b", "c")
# Inline, with setNames():
x <- setNames(1:3, c("a", "b", "c"))
```
2. *"dim"*,dimensions 的缩写,整数向量,用于将向量转换为矩阵或数组。
对于原子向量,dimension 属性通常用于创建矩阵或数组。
```{r}
z <- 1:6
dim(z) <- c(3, 2, 1)
z
class(z)
attributes(z)
dim(z)
```
对于列表,dimension 属性可用于创建列表矩阵或列表数组:
```{r}
l <- list(1:5, "a", TRUE, 1.0)
dim(l) <- c(2, 2)
l
class(l)
attributes(l)
l[[1,1]]
```
| 向量 | 矩阵 | 数组 |
|-----------------|-----------------------|----------------|
| names() | rownames(),colnames() | dimnames() |
| length() | nrow(),ncol() | dim() |
| c() | rbind(),cbind() | abind::abind() |
| --- | t() | aperm() |
| is.null(dim(x)) | is.matrix() | is.array() |
## 原子向量
![](images/summary-tree-atomic.png) {fig-align="center" width="50%"}
### vector
向量是一组有序元素的集合。一个 vector 可以包含任意数量的元素。但是,向量的所有元素必须属于同一类型。
```{r}
# 单元素向量
"a" == c ("a" )
is.vector ("a" )
# 函数c() Combine Values into a Vector or List
c ("a" ,"b" ,"c" )
c (list (1 ),list (T)) |> is.vector ()
```
### matricx
矩阵是一个具有维度属性(dim)的原子向量,所有元素必须是同一类型。
```{r}
num<- c (16 ,22 ,24 ,28 )
rnames<- c ("R1" ,"R2" )
cnames<- c ("C1" ,"C2" )
m<- matrix (num,nrow= 2 ,ncol= 2 ,byrow= TRUE ,dimnames= list (rnames,cnames))
m
class (m)
attributes (m)
dim (m)
rownames (m)
colnames (m)
```
#### 稀疏矩阵 Sparse matrix
稀疏矩阵的典型构造方式是通过三元组。
```{r}
library (Matrix)
i <- c (1 , 3 : 8 ) # 行指标
j <- c (2 , 9 , 6 : 10 ) # 列指标
x <- 7 * (1 : 7 ) # 数据
sparseMatrix (i, j, x = x)
```
稀疏矩阵对象仅存储非零元素,更节省内存
```{r}
N = 100
m = diag (1 , N, N)
sp = sparseMatrix (1 : N, 1 : N, x = 1 )
object.size (m)
object.size (sp)
```
### array
数组也是一个具有维度属性(dim)的原子向量,所有元素必须是同一类型。
```{r}
v<- 1 : 24
dim1<- c ("A1" ,"A2" ,"A3" )
dim2<- c ("B1" ,"B2" ,"B3" ,"B4" )
dim3<- c ("C1" ,"C2" )
array_3d<- array (v,c (3 ,4 ,2 ),dimnames = list (dim1,dim2,dim3))
array_3d
class (array_3d)
attributes (array_3d)
dim (array_3d)
dimnames (array_3d)
```
## S3类原子向量
要保留其他属性,需要创建S3 类。
base R 中使用的四个重要的 S3类原子向量:
1. 分类数据,其中值来自**factor**向量中记录的一组固定水平。
2. 日期(具有日期分辨率),记录在**Date**向量中。
3. 日期时间(具有秒或亚秒分辨率),存储在 **POSIXct** 向量中。
4. 持续时间,存储在**difftime**向量中。
![](images/summary-tree-s3-baseR.png) {fig-align="center" width="50%"}
### factor
因子,建立在具有两个属性(class,levels)的 ***integer***向量之上。
在内存中以整数向量`c(1,2,3,...,k)` 存储。
```{r}
# 存储形式
x <- factor (c ("a" , "b" , "b" , "a" ))
x
typeof (x)
str (x)
attributes (x)
levels (x)
class (x)
# 名义变量 nominal variable
diabetes<- c ("t1" ,"t2" ,"t1" ,"t1" )
attributes (diabetes)
diabetes<- factor (diabetes)
attributes (diabetes)
# 顺序变量 ordinal variable 默认水平根据字母顺序而定
status<- c ("poor" ,"better" ,"best" ,"poor" )
status<- factor (status,ordered = TRUE )
str (status)
status<- factor (status,ordered = TRUE ,levels = c ("poor" ,"better" ,"best" ))
str (status)
#改变外在标签
sex<- c (1 ,2 ,2 ,1 )
sex
sex<- factor (sex,levels= c (1 ,2 ),labels = c ("男" ,"女" ))
str (sex)
sex
age <- c (29 , 44 , 45 , 68 , 99 )
# 连续型变量→因子
cut (
age,
breaks = c (0 , 18 , 45 , 65 , Inf ),
labels = c ("minor" , "young" , "middle_age" , "elder" ),
include.lowest = TRUE ,
right = TRUE
)
```
### Date
日期向量建立在 ***double*** 向量之上,具有`class "Date"` 属性。
日期 默认格式:`"%Y-%m-%d" xxxx-xx-xx,例如:2023-03-15`
```{r}
today <- Sys.Date ()
today
typeof (today)
str (today)
unclass (today)
attributes (today)
# 双精度值(通过删除class属性来查看)表示自 1970 年 1 月 1 日以来的天数
date <- as.Date ("1970-02-01" )
unclass (date)
as.Date (c ("02 14-2002" ,"01 04-2013" ),"%m %d-%Y" ) #以"%m %d-%Y"格式读入
format (Sys.Date (),"%Y/%m/%d" ) #以"%Y/%m/%d"格式输出
```
### Datetime
POSIXct 和 POSIXlt
"POSIX"是可移植操作系统接口( Portable Operating System Interface)的缩写,这是一个跨平台标准系列。"ct"代表calendar time(C 中的类型),"lt"代表local time(C 中的类型)。
POSIXct 向量建立在 ***double***向量之上,其中值表示自 1970-01-01 以来的秒数
```{r}
dttm <- as.POSIXct ("2024-04-20 15:45" , tz = "Asia/Shanghai" )
dttm
typeof (dttm)
str (dttm)
attributes (dttm)
unclass (dttm)
```
### Durations
持续时间(表示日期对或日期时间对之间的时间量)存储在difftimes中。Difftimes 建立在 ***double*** 之上,并且具有确定整数应如何解释的属性。
```{r}
units_1 <- as.difftime (1 , units = "weeks" ) #units = c("auto", "secs", "mins", "hours","days", "weeks"))
units_1
typeof (units_1)
attributes (units_1)
unclass (units_1)
units (units_1) <- "days"
attributes (units_1)
```
## 泛型向量
从技术上讲,列表的每个元素实际上是相同的类型,因为每个元素实际上是对另一个对象的引用 @fig-list ,该对象可以是任何类型。
### list
列表不存储值本身,而是存储对它们的引用:
```{r}
l1 <- list (1 ,2 ,3 )
typeof (l1)
```
![](images/list.png) {#fig-list fig-align="center" width="50%"}
列表,`list(name1=object1,name2=object2,...)`
```{r}
list1 <- list (
title = "My list" ,
matr = matrix (
c ("a1" , "b1" , "a2" , "b2" ),
nrow = 2 ,
ncol = 2 ,
byrow = TRUE ,
dimnames = list (c ("X1" , "X2" ), c ("Y1" , "Y2" ))
),
df = data.frame (
id = matrix (
c ("Lisa" , "BOb" , "John" , "Jule" ),
nrow = 4 ,
ncol = 1 ,
byrow = TRUE
),
int = c (3 , 5 , 7 , 9 ),
TF = c (T, T, T, F)
),
list = list (a = c (1 , 2 , 3 ), b = c ("A" , "B" ))
)
list1
typeof (list1)
attributes (list1)
class (list1)
names (list1)
```
### data frame/tibble
建立在`"list"` 之上的两个最重要的 S3 类是data.frame 和 tibble
![](images/summary-tree-s3-df_tbl.png) {fig-align="center" width="30%"}
数据框是列向量具有 `names` ,`row.names` ,`class = "data.frame"` 属性的命名列表,是一种特殊的列表,每个原子向量的长度必须相同。
```{r}
id<- c (001231 ,001241 ,001413 ,001244 )
age<- c (21 ,14 ,52 ,15 )
diabetes<- c ("t1" ,"t2" ,"t1" ,"t1" )
status<- c ("poor" ,"better" ,"best" ,"poor" )
df1<- data.frame (patientID= id,age,diabetes,status,row.names = c (1 ,2 ,3 ,4 ),
stringsAsFactors = FALSE ) # 4个列向量组成数据框
df1
typeof (df1)
attributes (df1)
colnames (df1)
rownames (df1)
```
### tibble
```{r}
library (tibble)
tibble (
x = c (1 , 2 , 5 ),
y = c ("h" , "m" , "g" ),
z = c (0.08 , 0.83 , 0.60 )
)
tribble (
~ x, ~ y, ~ z,
1 , "h" , 0.08 ,
2 , "m" , 0.83 ,
5 , "g" , 0.60
)
```
`tibblle` 与`data frame` 共享相同的结构。区别是`class` 属性更多,不会自动进行强制类型转换,不会自动转换非法名称(自动反引号非法名称), tibbles 只能循环较短的长度为 1 的向量,允许引用在构造过程中创建的变量。
```{r error=TRUE}
df2 <- tibble(x = 1:3, y = letters[1:3])
typeof(df2)
attributes(df2)
names(tibble(`1` = 1))
tibble(x = 1:4, y = 1)
tibble(x = 1:4, y = 1:2)
tibble(
x = 1:3,
y = x * 2
)
```
#### 行名→列
```{r}
df3 <- data.frame (
age = c (35 , 27 , 18 ),
hair = c ("blond" , "brown" , "black" ),
row.names = c ("Bob" , "Susan" , "Sam" )
)
df3 |> rownames_to_column (var = "name" )
as_tibble (df3, rownames = "name" )
```