R中常见的5种数据类型:
| 1 维 | 原子向量 | 列表 |
| 2 维 | 矩阵 | 数据框 |
| 多 维 | 数组 | —— |
2.1 向量
下面构造向量数据,包括:原子向量、列表
| # 构造原子向量 (myVector <- 1:3) |
[1] 1 2 3
| # 列表也是向量 (myList <- list(10, 'A', pi)) |
[[1]]
[1] 10
[[2]] [1] “A”
[[3]] [1] 3.141593
向量有3个属性:元素类型、元素个数、附加的元数据
| # 原子向量的类型 typeof(myVector) |
[1] “integer”
| # 列表的类型 typeof(myList) |
[1] “list”
| # 原子向量的元素个数 length(myVector) |
[1] 3
| # 列表的元素个数 length(myList) |
[1] 3
| # 原子向量的所有属性,附加的元数据 attributes(myVector) |
NULL
| # 列表的所有属性 attributes(myList) |
NULL
attr()可以给向量添加属性,或获取某个具体的属性值
| # 给原子向量添加属性 attr(myVector, 'author') <- 'Forton' attributes(myVector) |
$author [1] “Forton”
| # 给列表添加属性 attr(myList, 'created_date') <- Sys.Date() attributes(myList) |
$created_date
[1] “2026-06-11”
判断是否向量:is.vector()只能准确判断不带属性的对象是否向量
| # 当对象没有添加属性时,确实能返回正确的结果 is.vector(1:3) |
[1] TRUE
| # 但如果向量添加了属性,则总是会返回false,得到错误结果 is.vector(myVector) is.vector(myList) |
[1] FALSE
[1] FALSE
正确判断对象是否为向量的方法:检测是否为原子向量或列表
| # 检测原子向量或列表 is.atomic(myVector) || is.list(myVector) is.atomic(myList) || is.list(myList) |
[1] TRUE
[1] TRUE
2.1.1 原子向量的种类
| 常见4种 | 整数、浮点数、字符、逻辑 |
| 罕见2种 | 复合型(复数)、原始型(raw) |
原子向量总是被展开的,即使用c()嵌套
| # 嵌套的原子向量 c(1, c(2, c(3,4))) |
[1] 1 2 3 4
另外,NA是长度为1的逻辑向量
可用typeof()判断向量类型,其实就是向量元素的类型
| # 返回向量类型 myReal <- c(1.1, 2.2, 3.3) typeof(myReal) |
[1] “double”
当然,也可以直接测试向量是否为某个具体类型
| # 判断是否为实数 is.numeric(myReal) is.numeric(myVector) |
[1] TRUE
[1] TRUE
| # 判断是否为字符 is.character(myVector) |
[1] FALSE
以下表格对比不同测试函数的异同:
| is.list() | is.character() | 必须是原子向量才能通过 is.character()测试,而原子向量必然不是列表;同理,列表必然不是原子向量;这两个测试函数的结果是互斥的。 |
| is.vector() | is.numeric() | 只有原子向量才能通过这两函数的测试,但带属性值的原子向量也无法通过 is.vector()测试,而数值型的原子向量总是能通过 is.numeric()测试。这两个测试函数的结果并不互斥。 |
以下是几个类型测试案例:
| # 是原子向量,就肯定不是列表;反在亦然 myLetters <- c('a','b','c') is.character(myLetters) |
[1] TRUE
| # 不是列表 is.list(myLetters) |
[1] FALSE
| # 测试带属性值的数值型原子向量 attr(myReal,'version') <- 1.0 is.vector(myReal) |
[1] FALSE
| # 实数型向量 is.numeric(myReal) |
[1] TRUE
有些函数或算法会强制执行不同数据类型之间的转换(详细转换规则见“2.1.3节”)
| # 逻辑值TRUE底层存储为1, 而FALSE底层存储为0 # 故下面等同于sum(c(1,0,1)) sum(c(T,F,T)) |
[1] 2
| # 任何元素同字符在一起,都会被转换为字符 c('A', 1) |
[1] “A” “1”
| # 显示地将浮点数PI转换为整数 as.integer(pi) |
[1] 3
| # 逻辑判断,1等同于TRUE 1 | F |
[1] TRUE
2.1.2 列表
列表也是一种向量,它的元素可以是任意类型
| # 构造列表 myList <- list(1:3, 'B', c(T,T,F), c(5.6, 7.8)) str(myList) |
List of 4 $ : int [1:3] 1 2 3 $ : chr “B” $ : logi [1:3] TRUE TRUE FALSE $ : num [1:2] 5.6 7.8 列表的元素也可以是列表(称为子列表),如下两个例子:
| # 元素是列表,称为子列表 y <- list(list(1,2), list(3,4,5), c(6,7)) str(y) |
List of 3 $ :List of 2 ..$ : num 1 ..$ : num 2 $ :List of 3 ..$ : num 3 ..$ : num 4 ..$ : num 5 $ : num [1:2] 6 7
| # 层层嵌套的列表 myList2 <- list(list(list(list('C')))) str(myList2) |
List of 1 $ :List of 1 ..$ :List of 1 .. ..$ :List of 1 .. .. ..$ : chr “C”
因此列表也被称为递归向量
| # 测试是否递归向量 is.recursive(myList2) |
[1] TRUE
| # 普通列表也是递归向量 is.recursive(myList) |
[1] TRUE
c()代表的单词是combine,其含义是Combine Values into a Vector or List(将值组合成向量或列表),其会将多个列表合并成一个大列表
| # 里面的原子向量会强制转换为列表 x <- c(list(1,2), list(3,4,5), c(6,7)) str(x) |
List of 7 $ : num 1 $ : num 2 $ : num 3 $ : num 4 $ : num 5 $ : num 6 $ : num 7
列表的格式转换,unlist()将列表转换为原子向量
| # 若列表元素是不同数据类型,则转换规则与c()一致,详细规则见下文 unlist(y) |
[1] 1 2 3 4 5 6 7
列表转换为原子向量存在两种方法,以下是异同对比:
| as.vector() |
1. 只剥离外层列表结构,但不递归处理内部嵌套 2. 尽可能不改变底层表示 3. 结果仍是一个列表(可能去除属性值),列表也是一种向量 |
| unlist() |
1. 专为递归地将列表拆解为原子向量而设计 2. 会遍历所有嵌套层级 3. 必要时进行类型转换(使用与 c()相同的规则) |
| 结论 | unlist()会递归压平列表,返回原子列表 |
请看下面的示例:
| # 先构造一个列表 z <- list('a',1,2.3) # 尝试转换为原子向量,但这是没有效果的,得到的还是列表 |
[[1]]
[1] “a”
[[2]] [1] 1
[[3]] [1] 2.3
| # 这才是正确的递归转换 unlist(z) |
[1] “a” “1” “2.3”
原子向量也可以转换为列表
| # 原子向量转换为列表 as.list(c('A','B','C')) |
[[1]]
[1] “A”
[[2]] [1] “B”
[[3]] [1] “C”
测试一个对象是否为列表
| # 测试是否列表 is.list(x) |
2.1.3 数据类型强制转换
转换级别排序是:逻辑 < 整数 < 实数 < 字符
强制转换时,总会转换至元素中最高级的格式,如下例子:
| # 逻辑值F转换为整数0 c(1, F) |
[1] 1 0
| # 逻辑值T转换为整数,1后面的L代表这是一个(长)整数 c(T, 1L) |
[1] 1 1
| # 整数1转换为字符 c('a', 1) |
[1] “a” “1”
若c()参数中存在列表,则会组合成大列表(同样触发强制类型转换)
| # 有一个元素是列表 c(list(1), 'a') |
[[1]]
[1] 1
[[2]] [1] “a”
不同类型向量比较也会触发强制类型转换
| # 左边1转换为字符'1' 1 == '1' |
[1] TRUE
| # 右边F转换为整数0 -1 < F |
[1] TRUE
| # 右边转换为字符'2',同:'one' < '2' 'one' < 2 |
[1] FALSE
2.2 属性
所有对象都可以通过附加任意属性来存储元数据
| attr() | 访问对象的单一属性 |
| attributes() | 访问对象的所有属性 |
| # 构造对象 myObject <- 1:10 # 添加属性 # 查看所有属性 |
List of 2 $ create_date: Date[1:1], format: “2026-06-11” $ author : chr “Forton”
structure()可以返回属性被修改后的新对象
| # 修改属性后的新对象 myObject2 <- structure(myObject, author='Lily', create_date=as.Date('2000-01-01')) myObject2 |
[1] 1 2 3 4 5 6 7 8 9 10 attr(,”create_date”) [1] “2000-01-01” attr(,”author”) [1] “Lily”
修改后返回一个新的对象副本,而原对象不受影响
| # 原来对象不受影响 myObject |
[1] 1 2 3 4 5 6 7 8 9 10 attr(,”create_date”) [1] “2026-06-11” attr(,”author”) [1] “Forton”
另外,comment(注释)等一部分属性被认为不属于数据的一部分,大部分打印方法不会显示该属性
| # 未能显示comment(注释)属性 myComment <- structure(1:5, comment='我的属性') myComment |
[1] 1 2 3 4 5
但comment属性真实存在,只是默认不打印显示而已
| # 但其真实存在 attributes(myComment) |
$comment [1] “我的属性”
| # 也可直接访问comment()属性 comment(myComment) |
[1] “我的属性”
复制向量后,大多数属性会消失,但也有例外
| # []是提取函数,除names属性外,其余属性均丢弃, # 因为子集可能不再适合拥有一些属性了(比如一些时间变量的属性) # 通过索引取子集会触发写时复制机制, # 也即:两个变量初始时共享同一片内存,但更新其中一个变量后会导致两个向量不再一致, # 此时R会将更新的向量复制到新内存中 # 故子集本质上可认为是新向量,原来的大部分属性会消失 attributes(myObject[1:3]) |
NULL
| # 对向量进行运算,返回一个新的向量,属性同样不会被保留 attributes(sum(myObject)) |
NULL
使用赋值符号“<-”复制向量时,所有属性都会被保留
“<-”是替换操作,其语义是:修改myObject某些元素,并返回与原对象类型、属性相同的新对象(属性也是对象的一部分)
| # 当myObject未被其它变量共享(引用计数为1),则原地修改数据 # 当myObject被其它变量引用,也即被复制(引用计数大于1),则复制数据和新属性到新内存,再修改副本元素 myObject[1] <- -1 myObject |
[1] -1 2 3 4 5 6 7 8 9 10 attr(,”create_date”) [1] “2026-06-11” attr(,”author”) [1] “Forton”
但有 3 个属性不会在复制向量时消失,如下所示:
| name | 每个元素对应名字的字符向量 | names() |
| dimension | 维度,将向量转变为矩阵或数组 | dim() |
| class | 类,实现S3对象系统(S3介绍见后文) | class() |
name属性:创建或更新向量中的名称属性
| # 创建向量时命名 c(a=1, b=2, c=3) |
a b c 1 2 3
| # 修改现有向量的名字 names(myVector) <- c('X','Y','Z') myVector |
X Y Z 1 2 3 attr(,”author”) [1] “Forton”
| # 复制向量的同时修改名字 setNames(1:3, c('a','b','c')) |
a b c 1 2 3
name属性:取消部分或全部向量元素名称的示例
| # 有些元素可以没有名字 names(c(a=1,2,3)) |
[1] “a” “” “”
| # 复制没有名字的新向量 unname(c(a=1,b=2,c=3)) |
[1] 1 2 3
| # 取消向量中的全部名称 names(myVector) <- NULL myVector |
[1] 1 2 3 attr(,”author”) [1] “Forton”
2.2.1 因子
因子是只能包含预先定义值的向量,常用于分类数据
因子底层是整型向量,但具有两个属性:class(factor类)和levels(定义因子所有可能的取值)
| # class定义了该对象属于因子类 myFactor <- factor(c('a','b','b','a','c')) class(myFactor) |
[1] “factor”
| # 水平值,也即因子全部可能取的值 levels(myFactor) |
[1] “a” “b” “c”
| # 取赋值不存在于因子中,则只能得到NA myFactor[6] <- 'z' myFactor |
Warning in [<-.factor(*tmp*, 6, value = “z”): invalid factor level, NA generated
[1] a b b a c <NA> Levels: a b c
合并因子,会自动合并水平值
| c(factor('x'), factor('y')) |
[1] x y Levels: x y
汇总各因子出现的频次
| # 可以通过因子观察所有可能的取值 sex.char <- c('m','m','m') table(sex.char) |
sex.char m 3
| # 即使某因子未取值,也能统计进来 sex.factor <- factor(sex.char, levels=c('f','m')) table(sex.factor) |
sex.factor f m 0 3
读取数据时,有时数值字段会变成因子字段(往往由特殊字符代表的缺失值引起)
此时可以指定哪些字符代表NA值
另外,有些数据加载函数会自动将字符型向量转换为因子向量,可关闭这种转换
| # 模拟读取文本文件 csv.data <- read.csv(text='value\\n12\\n1\\n-\\n9', na.strings='-', stringsAsFactors=F) typeof(csv.data$value) |
[1] “integer”
| # 再确认字段的class属性 class(csv.data$value) |
[1] “integer”
| # 查看取值 csv.data$value |
[1] 12 1 NA 9
因子的底层是整型数值,不同处理函数对待因子的行为模式是不同的,有些函数会将因子转换为字符串,有些会报错,还有些会转换为整型。使用因子时需特别注意!
当翻转因子水平时,其显示值也会翻转,如下所示:
| # 翻转水平标签的示例 myLetters <- factor(letters) levels(myLetters) <- rev(levels(myLetters)) myLetters |
[1] z y x w v u t s r q p o n m l k j i h g f e d c b a Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
当创建因子时,比如上例中执行:myLetters <- factor(letters)
1)R创建了一个整数向量,其值为 1, 2, 3, …, 26,该整数向量是levels的索引
2)同时保存一个属性levels=c(“a”, “b”, …, “z”)
当显示因子myLetters时,R查找每个整数值(索引)对应在levels中的标签,然后显示标签。而字符“a”从来没有作为字符存储在每个位置中,它只是在显示时通过映射计算出来而已。
上例子,当翻转水平标签时:
1)原始因子是 1…26 对应标签’a’…’z’,其中1对应’a’, 2 对应’b’,以此类推
2)翻转水平标签,所翻转的只是标签(变为 z…a),但整数编码(也即索引)不变,故变成了:1对应’z’,2对应’y’,以此类推
3)翻转之后,字母’a’的整数编码变为26, 被排到最后;而字母’z’的整数编码1, 被排到最前面
4)这种翻转并未改变原始数据的位置顺序,故 1…26 的整数数据就被显示为 z…a 了
对比下面的示例,区分翻转整型编号与水平标签的差异:
| # 翻转整型编号 # 对于水平标签来说,a…z分别对应编号1…26 # 而由于因子的整型编号被翻转,第一个元素z对应编号26, 而编号26又反过来显示为标签z (myLetter2 <- rev(factor(letters))) |
[1] z y x w v u t s r q p o n m l k j i h g f e d c b a Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
| # 查看每个位置的整型数字,也即每个位置对应在levels中的索引 as.integer(myLetter2) |
[1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 [26] 1
| # 作为对比:翻转水平标签 # 水平标签被翻转,故a…z分别对应编号26…1 # 因子第一个元素a对应编号26, 而编号26又反过来显示为标签a (myLetters3 <- factor(letters, levels=rev(letters))) |
[1] a b c d e f g h i j k l m n o p q r s t u v w x y z Levels: z y x w v u t s r q p o n m l k j i h g f e d c b a
| # 查看底层的整型数值(也即对应levels中的索引) as.integer(myLetters3) |
[1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 [26] 1
2.3 矩阵和数组
给原子向量添加维度(dim)属性后,就成为数组(array);而矩阵(matrix)是数组的特例,总是2维的
数组的普适属性:维度
| # 数组可以是任意维数 # 下面的案例是:2行、3列、4组,越右边的参数,反而是越大的组别 # 数值默认先纵向,再横向,一个组别填充完后再填充下一个组别 (myArray <- array(1:24, c(2,3,4))) |
, , 1
[,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6
, , 2
[,1] [,2] [,3] [1,] 7 9 11 [2,] 8 10 12
, , 3
[,1] [,2] [,3] [1,] 13 15 17 [2,] 14 16 18
, , 4
[,1] [,2] [,3] [1,] 19 21 23 [2,] 20 22 24
以下的数组维度不同,代表各不相同的数组对象
| # 1行、1列、5组 array(1:5, c(1,1,5)) |
, , 1 [,1] [1,] 1
, , 2 [,1] [1,] 2
, , 3 [,1] [1,] 3
, , 4 [,1] [1,] 4
, , 5 [,1] [1,] 5
| # 1行、5列、1组 array(1:5, c(1,5,1)) |
, , 1
[,1] [,2] [,3] [,4] [,5] [1,] 1 2 3 4 5
| # 5行、1列、1组 array(1:5, c(5,1,1)) |
, , 1
[,1] [1,] 1 [2,] 2 [3,] 3 [4,] 4 [5,] 5
直接修改数组或矩阵的维数
| # 调整数组的维数 dim(myArray) <- c(4,6) myArray |
[,1] [,2] [,3] [,4] [,5] [,6] [1,] 1 5 9 13 17 21 [2,] 2 6 10 14 18 22 [3,] 3 7 11 15 19 23 [4,] 4 8 12 16 20 24
| # 调整矩阵的维数 dim(myMatrix) <- c(3,2) myMatrix |
[,1] [,2] [1,] 1 4 [2,] 2 5 [3,] 3 6
数组的普适属性:元素个数
| # 返回数组的元素个数 length(myArray) |
[1] 24
| # 返回矩阵的元素个数 length(myMatrix) |
[1] 6
| # 数组的行数 nrow(myArray) |
[1] 4
| # 数组的列数 ncol(myArray) |
[1] 6
| # 矩阵的行数 nrow(myMatrix) |
[1] 3
| # 矩阵的列数 ncol(myMatrix) |
[1] 2
数组的普适属性:维度名称
注意:数组元素通过行列索引访问,是没有单个元素的名称(name)属性的
| # 数组的维度名称 dimnames(myArray) <- list(c('R1','R2','R3','R4'), c('C1','C2','C3','C4','C5','C6')) myArray |
C1 C2 C3 C4 C5 C6 R1 1 5 9 13 17 21 R2 2 6 10 14 18 22 R3 3 7 11 15 19 23 R4 4 8 12 16 20 24
| # 查看名称属性 names(myArray) |
NULL
c()将数组打平为一维向量,按列优先填充
| # 组合两个矩阵 c(myMatrix,myMatrix) |
[1] 1 2 3 4 5 6 1 2 3 4 5 6
| # 单个数组的效果相同 c(myArray) |
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
合并矩阵的行和列
| # 合并列 cbind(myMatrix,rep(9,3)) |
[,1] [,2] [,3] [1,] 1 4 9 [2,] 2 5 9 [3,] 3 6 9
| # 合并行 rbind(myMatrix, rep(9,2)) |
[,1] [,2] [1,] 1 4 [2,] 2 5 [3,] 3 6 [4,] 9 9
矩阵转置
| # 矩阵的行列转置,也即沿主对角线翻转 t(myMatrix) |
[,1] [,2] [,3] [1,] 1 2 3 [2,] 4 5 6
判断是否为数组或矩阵
| # 判断是否数组 is.array(myArray) |
[1] TRUE
| # 矩阵也是数组 is.array(myMatrix) |
[1] TRUE
查看数组和矩阵的维数
| # 数组的维数 dim(myArray) |
[1] 4 6
| # 矩阵的维数 dim(myMatrix) |
[1] 3 2
向量转换为数组,遗憾的是,不像 array/matrix 函数,as.array/as.matrix 函数是没有维度参数的
| # dim参数不起作用 as.array(1:12, dim=c(3,2,2)) |
[1] 1 2 3 4 5 6 7 8 9 10 11 12
| # nrow和ncol参数不起作用 as.matrix(1:12, nrow=4, ncol=3) |
[,1] [1,] 1 [2,] 2 [3,] 3 [4,] 4 [5,] 5 [6,] 6 [7,] 7 [8,] 8 [9,] 9 [10,] 10 [11,] 11 [12,] 12
只有一行/一列的矩阵、一维数组,也是一维数据结构,但行为模式与向量不同
| # 一维向量 str(1:3) |
int [1:3] 1 2 3
| # 单行矩阵(一行三列) str(matrix(1:3, nrow=1)) |
int [1, 1:3] 1 2 3
| # 一列矩阵(三行一列) str(matrix(1:3, ncol=1)) |
int [1:3, 1] 1 2 3
| # 一维数组 str(array(1:3, dim=3)) |
int [1:3(1d)] 1 2 3
列表矩阵和列表数组
dim()可以设置任何向量(包括列表)的维度,使其转变为矩阵或数组(按列优先填充)
列表矩阵的元素可以是任意类型,不要求全部元素相同类型,因为 dim()只是给对象添加了 dim 属性,并未改变列表的性质
这是一种复杂的数据结构,在需要将对象(比如某些模型)按网格结构存储时有用
| # 列表矩阵 myList3 <- list(1:3, 'a', T, 1.0) dim(myList3) <- c(2,2) myList3 |
[,1] [,2] [1,] integer,3 TRUE [2,] “a” 1
| # 对象类型 typeof(myList3) |
[1] “list”
| # 是否矩阵 is.matrix(myList3) |
[1] TRUE
上面案件中,设置myList3维度之后,其变成:
| 1:3 | TRUE |
| “a” | 1 |
输出矩阵或数组时,R会调用对象的print方法打印对象摘要:
- 长度大于1的对象,会显示其类型和长度(比如上例的“integer,3”)
- 长度等于1的对象,直接显示其值
2.4 数据框
R中存储数据使用最多的就是数据框,二维结构,每一列由相同长度的向量组成,每列的数据类型可不相同,这使其既有矩阵的性质,又有列表的性质
数据框具有如下常用属性:
| colnames() | 列名 |
| rownames() | 行名 |
| names() | 同 colnames(),返回列名 |
| ncol() | 列数 |
| nrow() | 行数 |
| length() | 同 ncol(),返回列数 |
2.4.1 格建数据框
| # 创建数据框 # 如果需要将字符列转换为因子,可设置参数stringsAsFactors为T(默认F) (myFrame <- data.frame(x=11:13, y=c('a','b','c'))) |
x y 1 11 a 2 12 b 3 13 c
| # 列名,同colnames() names(myFrame) |
[1] “x” “y”
| # 列名 colnames(myFrame) |
[1] “x” “y”
| # 返回行名 rownames(myFrame) |
[1] “1” “2” “3”
| # 列数,同ncol() length(myFrame) |
[1] 2
| # 列数 ncol(myFrame) |
[1] 2
| # 行数 nrow(myFrame) |
[1] 3
2.4.2 类型判断
data.frame是S3类,R语言中的S3类本质上是:对象+class属性,class属性标识对象属于哪个类
泛型函数会根据该class属性调用对应的方法(可以用 methods()查看泛型函数支持的方法),比如:
- print(1:5)调用的是print.default或print.integer方法
- print(myFrame)调用的是print.data.frame方法
类的方法命名格式是:泛型函数名.类名,比如:
- print.data.frame:用于打印数据框。上例中的 print(myFrame),R会检查data.frame 类,然后调用 print.data.frame
- summary.default:默认的summary方法
- plot.lm:用于线性模型对象的绘图方法
S3类的方法派发机制,比如调用泛型函数 f(obj)时,R 按以下顺序查找方法:
data.frame本质上是一个列表(每列都是一个向量,列表是对象),再附加 class=”data.frame”属性,这使得 data.frame 拥有很多专有方法(而不是一个普通的列表),包括:
- print.data.frame:以表格形式打印数据框
- summary.data.frame:统计摘要数据框的每一列
- plot.data.frame:绘制散点图矩阵
S3类是一种简单、灵活、基于泛型函数和 class属性的面向对象编程,R中绝大部分基础包都属于S3类。R 语言还有 S4、R6 等支持更严格、更复杂的面向对象系统。
| # 数据框本质上是一个列表 typeof(myFrame) |
[1] “list”
| # 判断对象是否为数据框,要查看它的类 class(myFrame) |
[1] “data.frame”
| # 也可以使用is.xx()测试函数 is.data.frame(myFrame) |
[1] TRUE
2.4.3 合并数据框
合并行列与矩阵相同,使用 rbind()和 cbind()
| # 合并行:列的名字和数量必须一致 # 如果列名不同,则无法合并 rbind(myFrame, data.frame(y='d',x=14)) |
x y 1 11 a 2 12 b 3 13 c 4 14 d
| # 合并列:行数必须一致,行的名称可忽略 cbind(myFrame, data.frame(z=c('甲','乙','丙'))) |
x y z 1 11 a 甲 2 12 b 乙 3 13 c 丙
注意:合并向量时,cbind()默认返回矩阵(而非数据框),除非其中有一个参数是数据框才会返回数据框
| # 合并向量时,返回的是矩阵 myBind1 <- cbind(1:5, 6:10) str(myBind1) |
int [1:5, 1:2] 1 2 3 4 5 6 7 8 9 10
| # 测试是否矩阵 is.matrix(myBind1) |
[1] TRUE
| # 如果需要返回数据框,可以用数据框构造函数 myBind2 <- data.frame(cbind(1:5, 6:10)) str(myBind2) |
‘data.frame’: 5 obs. of 2 variables: $ X1: int 1 2 3 4 5 $ X2: int 6 7 8 9 10
| # 或者直接创建为数据框 myBind3 <- data.frame(a=1:5, b=6:10) str(myBind3) |
‘data.frame’: 5 obs. of 2 variables: $ a: int 1 2 3 4 5 $ b: int 6 7 8 9 10
2.4.4 特殊列
数据框是一个向量列表,数据框的列本身也可以是一个列表
| myFrame2 <- data.frame(x=1:3) myFrame2$y <- list(1:2,1:3,1:4) myFrame2 |
x y 1 1 1, 2 2 2 1, 2, 3 3 3 1, 2, 3, 4
但如果使用 data.frame()构造则会报错,因为它会试着将列表每个元素分别当作独立的列来构造数据框
| # 尝试将列表元素1:2、1:3、1:4作为独立的三列,行数不同,故而报错 try(data.frame(x=1:3, y=list(1:2,1:3,1:4))) |
Error in (function (…, row.names = NULL, check.rows = FALSE, check.names = TRUE, : arguments imply differing number of rows: 2, 3, 4
可以使用 I()将列表作为一个单元整体,并会将该列添加一个 AsIs 的类属性
| # I()会将列表作为一个整体,且为该列添加一个AsIs类别 myFrame3 <- data.frame(x=1:3, y=I(list(1:2,1:3,1:4))) str(myFrame3) |
‘data.frame’: 3 obs. of 2 variables: $ x: int 1 2 3 $ y:List of 3 ..$ : int 1 2 ..$ : int 1 2 3 ..$ : int 1 2 3 4 ..- attr(*, “class”)= chr “AsIs”
同理,数据框的列也可以是矩阵或数组
| # 数据框特殊列 myFrame4 <- data.frame(x=1:3, y=I(matrix(11:19,nrow=3))) str(myFrame4) |
‘data.frame’: 3 obs. of 2 variables: $ x: int 1 2 3 $ y: ‘AsIs’ int [1:3, 1:3] 11 12 13 14 15 16 17 18 19
| # 引用某一列 myFrame4[1,'y'] |
[,1] [,2] [,3] [1,] 11 14 17
特别注意:很多函数会假定数据框的每列都是原子向量,可能无法处理列表列的情况
本文是我阅读和学习《高级R语言编程指南》(作者Hadley Wickham,机械工业出版社出版)后的读书笔记,大部分内容与例子引用原书,并加上自己的一部分理解与补充。
推荐关注我的个人博客:R数据分析,不定期发布R语言经典书籍的读书笔记、数据分析案例,欢迎交流~


