首页 > > > 【R语言】数据结构Ⅱ—列表,数据框,因子
列表(list),可以包含不同类型的对象,甚至可以包括其他列表。列表的灵活性使得它非常有用。
例如,用R 拟合一个线性回归模型,其返回结果就是一个列表,其中包含了线性回归的详细结果,如线性回归系数(数值向量)、残差(数值向量)、QR 分解(包含一个矩阵和其他对象的列表)等。因为这些结果全都被打包到一个列表中,就可以很方便地提取所需信息,而无需每次调用不同的函数。
1. 创建列表
可以用函数list()创建一个列表。不同类型的对象可以被装进同一个列表中。例如,创建了一个列表,包含3个成分:一个单元素的数值向量、一个两元素的逻辑向量和一个长度为3的字符向量:
> l0 <- list(1, c(TRUE, FALSE), c("a", "b", "c"))
> l0
[[1]]
[1] 1
[[2]]
[1] TRUE FALSE
[[3]]
[1] "a" "b" "c"也在创建列表时,为列表的每个成分指定名称:
> l1 <- list(x = 1, y = c(TRUE, FALSE), z = c("a", "b", "c"))
> l1
$x
[1] 1
$y
[1] TRUE FALSE
$z
[1] "a" "b" "c"也可以创建列表后再对列表成分命名或修改命名:
> names(l1) <- c("A","B","C")
> names(l1) <- NULL #移除列表成分的名字2. 从列表中提取元素
提取列表中的元素,最常用的方法是用$,通过成分名称来提取该成分下的元素(内容):
> l1$x
NULL
> l1$y
NULL
> l1$z
NULL
> l1$m #访问一个不存在的成分m, 将会返回NULL
NULL也可以用 [[ n ]] 来提取列表第n个成分的内容,n也可以换成成分的名字:
> l1[[2]]
[1] TRUE FALSE
> l1[["y"]]
NULL使用 [[ ]] 提取列表中某个成分的内容更加灵活,因为可以赋值改变想要提取其内容的成分名字:
> member <- "z" #想要取出的成分名字
> l1[[member]]
NULL3. 构建列表子集
经常也需要从列表中提取多个成分的内容,由这些成分组成的列表构成了原列表的一个子集。
就像构建向量和矩阵的子集一样,构建一个列表子集是用 [ ],可以取出列表中的一些成分,然后放到一个新的列表中。
[ ] 中可以用字符向量表示成分名称,用数值向量表示成分位置,或用逻辑向量指定是否选择,来取出列表成分。
> l1["x"] #同l1[1]
[[1]]
NULL
> l1[c("x", "y")] #同l1[c(1, 2)]
[[1]]
NULL
[[2]]
NULL
> l1[c(TRUE, FALSE, TRUE)]
[[1]]
[1] 1
[[2]]
[1] "a" "b" "c"注:用 [ ] 提取若干成分时,返回列表的子集,还是一个列表;用[[ ]]提取单个成分的元素,返回的是对应成分的元素。总之,[ ] 提取对象的子集,类型仍是该对象;[[ ]] 提取对象的内容(下一级元素)。
4. 对列表的成分赋值
> l1 <- list(x = 1, y = c(TRUE, FALSE), z = c("a", "b", "c"))
> l1$x <- #将列表的成分x赋值为0若给一个不存在的成分赋值,列表会自动地在对应名称或位置下增加一个新成分:
> l1$m <- 4
> l1
$x
[1]
$y
[1] TRUE FALSE
$z
[1] "a" "b" "c"
$m
[1] 4也可以同时给多个列表成分赋值:
> l1[c("y", "z")] <- list(y = "new value for y", z = c(1, 2))
> l1
$x
[1]
$y
[1] "new value for y"
$z
[1] 1 2
$m
[1] 4若要移除列表中的某些成分,只需赋值为 NULL:
> l1[c("z", "m")] <- NULL
> l1
$x
[1]
$y
[1] "new value for y"5. 列表函数
用as.list( )函数可将向量转换成列表:
> l3 <- as.list(c(a = 1, b = 2, c = 3))
> l3
$a
[1] 1
$b
[1] 2
$c
[1] 3
用函数unlist(),可将一个列表强制转换成一个向量。该函数基本上对所有列表成分进行转换,并把它们存储在一个类型兼容的向量中:
> unlist(l3)
a b c
1 2 3 若对一个混合了数值和文本的列表进行去列表化(unlist),则每个成分都会被转换为其所能转换成的最近类型(closest type):
> l4 <- list(a = 1, b = 2, c = "hello")
> unlist(l4)
a b c
"1" "2" "hello" 这里的l4$a 和l4$b都是数值,可以被转换为字符向量;但是,l4$c是字符向量,无法被转换为数值。因此,能够兼容这些元素的最近类型就是字符向量。
函数summary() 可以查看列表的汇总信息:
> summary(l0)
Length Class Mode
[1,] 1 -none- numeric
[2,] 2 -none- logical
[3,] 3 -none- characterR语言中做统计分析的样本数据,都是按数据框类型操作的。
数据框是指有若干行和列的数据集,它与矩阵类似,但并不要求所有列都是相同的类型;本质上讲,数据框就是一个列表,它的每个成分都是一个向量,并且长度相同,以表格的形式展现。总之,数据框是由列向量组成、有着矩阵形式的列表。
数据框与最常见的数据表是一致的:每一列代表一个变量属性,每一行代表一条样本数据:

1. 创建数据框
可以用data.frame()函数,对每一列提供相应类型的列向量来创建一个数据框。
> persons <- data.frame(
+ Name = c("Ken", "Ashley", "Jennifer"),
+ Gender = c("Male", "Female", "Female"),
+ Age = c(24, 25, 23),
+ Major = c("Finance", "Statistics", "Computer Science"))
> persons
Name Gender Age Major
1 Ken Male 24 Finance
2 Ashley Female 25 Statistics
3 Jennifer Female 23 Computer Science注意,数据框的创建方式与列表完全一致。也可以对列表直接调用data.frame( )或者as.data.frame( )将其转换为数据框:
> l1 <- list(x = c(1, 2, 3), y = c("a", "b", "c"))
> data.frame(l1) # 同as.data.frame(l1)
x y
1 1 a
2 2 b
3 3 c同样的方式,也可以将矩阵转化为数据框:
> m1 <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, byrow = FALSE)
> data.frame(m1)
X1 X2 X3
1 1 4 7
2 2 5 8
3 3 6 9
> as.data.frame(m1)
V1 V2 V3
1 1 4 7
2 2 5 8
3 3 6 9注:若矩阵有行名或列名,则转换中会被保留下来。
数据框既是列表的特例,也是矩阵的推广,因此访问这两类对象的方式都适用于数据框。例如与矩阵类似,对数据框的行列重新命名:
> df1 <- data.frame(id = 1:5, x = c(, 2, 1, -1, -3), y = c(0.5, 0.2, 0.1, 0.5, 0.9))
> colnames(df1) <- c("id", "level", "score")
> rownames(df1) <- letters[1:5]
> df1
id level score
a 1 0.5
b 2 2 0.2
c 3 1 0.1
d 4 -1 0.5
e 5 -3 0.9另外,可用names()查看或修改数据框的列名。
2. 提取数据框的元素、子集
数据框是由列向量组成、有着矩阵形式的列表,所以可以用两种操作方式来访问数据框的元素和子集。
(1) 以列表方式提取数据框的元素、子集
若把数据框看作是由向量组成的列表,则可以沿用列表的操作方式来提取元素或构建子集。例如,可以用$按列名来提取某一列的值,或者用[[ ]]按照位置提取。
> df1$id #同df1[[1]]
[1] 1 2 3 4 5以列表形式构建子集完全适用于数据框,同时也会生成一个新的数据框。提取子集的操作符 [ ] 允许用数值向量表示列的位置,用字符向量表示列名,或用逻辑向量指定是否选择。
> df1[1]
id
a 1
b 2
c 3
d 4
e 5
> df1[1:2]
id level
a 1
b 2 2
c 3 1
d 4 -1
e 5 -3
> df1["level"]
level
a
b 2
c 1
d -1
e -3
> df1[c("id", "score")]
id score
a 1 0.5
b 2 0.2
c 3 0.1
d 4 0.5
e 5 0.9
> df1[c(TRUE, FALSE, TRUE)]
id score
a 1 0.5
b 2 0.2
c 3 0.1
d 4 0.5
e 5 0.9(2) 以矩阵方式提取数据框的元素、子集
以列表形式操作并不支持行选择。以矩阵形式操作更加灵活,若将数据框看作矩阵,其二维形式的存取器可以很容易地获取一个子集的元素,同时支持列选择和行选择。
换句话说,可以使用 [i, j] 指定行或列来提取数据框子集,[ , ]内可以是数值向量、字符向量或者逻辑向量。
选择指定的列:
> df1[, "level"]
[1] 2 1 -1 -3
> df1[, c("id", "level")]
id level
a 1
b 2 2
c 3 1
d 4 -1
e 5 -3
> df1[, 1:2]
id level
a 1
b 2 2
c 3 1
d 4 -1
e 5 -3选择指定的行:
> df1[1:4,]
id level score
a 1 0.5
b 2 2 0.2
c 3 1 0.1
d 4 -1 0.5
> df1[c("c", "e"),]
id level score
c 3 1 0.1
e 5 -3 0.9同时选择指定的行和列:
> df1[1:4, "id"]
[1] 1 2 3 4
> df1[1:3, c("id", "score")]
id score
a 1 0.5
b 2 0.2
c 3 0.1注意,以矩阵形式操作会自动简化结果,也就是说,若只提取一列,那么结果将不再是数据框形式,而只是一个列向量。若要保留数据框的形式,一种方法是以结合使用两种操作方式:
> df1[1:4,]["id"]
id
a 1
b 2
c 3
d 4这里,第1 组 [ , ] 以矩阵形式提取数据框的前4行和所有的列,第2 组 [ ] 再以列表形式提取列名为id 的这一列,结果即表现为数据框的形式。
另一种方法是通过设定参数drop = FALSE 避免简化结果:
> df1[1:4, "id", drop = FALSE] #结果同上
id
a 1
b 2
c 3
d 4用条件筛选数据:
例如,用score >= 0.5 筛选df1 的行,并选择id 和level 两列:
> df1$score >= 0.5
[1] TRUE FALSE FALSE TRUE TRUE
> df1[df1$score >= 0.5, c("id", "level")]
id level
a 1
d 4 -1
e 5 -3按行名属于集合{a, d, e}来筛选 df1的行,并选择id和score两列:
> rownames(df1) %in% c("a", "d", "e")
[1] TRUE FALSE FALSE TRUE TRUE
> df1[rownames(df1) %in% c("a", "d", "e"), c("id", "score")]
id score
a 1 0.5
d 4 0.5
e 5 0.9注:还可以用sqldf包中的sqldf()函数,借助sql语句索引。例如,
> library(sqldf)
> sqldf("select * from mtcars where carb=1 order by mpg", row.names=TRUE)3. 给数据框赋值
(1) 以列表方式给数据框赋值
> df1$score <- c(0.6, 0.3, 0.2, 0.4, 0.8)
> df1
id level score
a 1 0.6
b 2 2 0.3
c 3 1 0.2
d 4 -1 0.4
e 5 -3 0.8
> df1[["score"]] <- c(0.4, 0.5, 0.2, 0.8, 0.4)
> df1
id level score
a 1 0.4
b 2 2 0.5
c 3 1 0.2
d 4 -1 0.8
e 5 -3 0.4注:用[[ ]]只能对数据框的1列进行赋值。
用[ ]则既可以对数据框的1列,也可以对数据框的多列进行赋值:
> df1["score"] <- c(0.8, 0.5, 0.2, 0.4, 0.8)
> df1
id level score
a 1 0.8
b 2 2 0.5
c 3 1 0.2
d 4 -1 0.4
e 5 -3 0.8
> df1[c("level", "score")] <- list(level = c(1, 2, 1, , ), score = c(0.1, 0.2, 0.3, 0.4, 0.5))
> df1
id level score
a 1 1 0.1
b 2 2 0.2
c 3 1 0.3
d 4 0.4
e 5 0.5(2) 以矩阵方式给数据框赋值
以列表方式对数据框进行赋值时,也是只能访问列。若需要更加灵活地进行赋值操作,可以以矩阵方式进行。
> df1[1:3, "level"] <- c(-1, , 1)
> df1
id level score
a 1 -1 0.1
b 2 0.2
c 3 1 0.3
d 4 0.4
e 5 0.5
> df1[1:2, c("level", "score")] <- list(level = c(, ), score = c(0.9, 1.0))
> df1
id level score
a 1 0.9
b 2 1.0
c 3 1 0.3
d 4 0.4
e 5 0.54. 数据框函数
str()函数作用在R对象上,显示该对象的结构:
> str(persons)
'data.frame': 3 obs. of 4 variables:
$ Name : Factor w/ 3 levels "Ashley","Jennifer",..: 3 1 2
$ Gender: Factor w/ 2 levels "Female","Male": 2 1 1
$ Age : num 24 25 23
$ Major : Factor w/ 3 levels "Computer Science",..: 2 3 1summary()函数作用在数据框上,将生成一个汇总表来显示每一列的情况:
> summary(persons)
Name Gender Age Major
Ashley :1 Female:2 Min. :23.0 Computer Science:1
Jennifer:1 Male :1 1st Qu.:23.5 Finance :1
Ken :1 Median :24.0 Statistics :1
Mean :24.0
3rd Qu.:24.5
Max. :25.0 经常需要将多个数据框(或矩阵)按行或按列进行合并,使用函数rbind(),增加行(样本数据),要求宽度(列数)相同;使用函数cbind(),增加列(属性变量),要求高度(行数)相同。
例如,向数据框persons数据框中添加一个人的新记录:
> rbind(persons, data.frame(Name = "John", Gender = "Male", Age = 25, Major = "Statistics"))
Name Gender Age Major
1 Ken Male 24 Finance
2 Ashley Female 25 Statistics
3 Jennifer Female 23 Computer Science
4 John Male 25 Statistics))向persons数据框中添加两个新列表示每个人是否已注册和其手头的项目数量:
> cbind(persons, Registered = c(TRUE, TRUE, FALSE), Projects = c(3, 2, 3))
Name Gender Age Major Registered Projects
1 Ken Male 24 Finance TRUE 3
2 Ashley Female 25 Statistics TRUE 2
3 Jennifer Female 23 Computer Science FALSE 3注意,rbind()和cbind()不会修改原始数据,而是生成一个添加了行或列的新数据框。
另一个实用函数是expand.grid(),它会生成多个属性水平值所有组合(笛卡尔积)的数据框:
> expand.grid(type = c("A", "B"), class = c("M", "L", "XL"))
type class
1 A M
2 B M
3 A L
4 B L
5 A XL
6 B XL变量分为名义型(无顺序好坏之分的分类变量,如性别)、有序型(有顺序好坏之分的分类变量,如疗效)、连续型(通常的数值变量,可带小数位)。
名义型和有序型的类别变量,在R语言中称为因子,因子本质上是一个带有水平(level)属性的整数向量,其中“水平”是指事前确定可能取值的有限集合。
因子提供了一个简单且紧凑的形式来处理分类数据,因子用水平来表示所有可能的取值,例如,性别有两个水平:男、女。
1. 创建因子
(1) 用函数factor()创建因子,其基本格式为:
factor(x, levels, labels=..., exclude=..., ordered=..., nmax=...)其中,
x为创建因子的数据向量;
levels指定因子的水平数,默认为x中不重复的所有值;
labels设置各水平名称(前缀),与水平一一对应;
exclude指定有哪些水平是不需要的;
ordered设置是否对因子水平排序,默认为TRUE即有序因子,FALSE为无序因子;
nmax设定水平数的上限。
ff<-factor(substring("statistics",1:10,1:10),levels=letters)
ff
[1] s t a t i s t i c s
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
ff[,drop=TRUE] #去掉未包含在向量中的水平,同f.<-factor(ff)
[1] s t a t i s t i c s
Levels: a c i s t
factor(1:10,labels="let")
[1] let1 let2 let3 let4 let5 let6 let7 let8 let9 let10
Levels: let1 let2 let3 let4 let5 let6 let7 let8 let9 let10
factor(LETTERS[3:1],ordered = TRUE)
[1] C B A
Levels: A < B < C注:函数substring()用来提取字符串的子串,第2个参数是起始位置,第3个参数是终止位置;letters和LETTERS是R中专有变量,表示26个小写/大写字母组成的字符向量。
(2) 用函数gl()创建因子序列
用函数gl()生成不同水平的因子序列,基本格式为:
gl(n, k, length=n*k, labels=seq_len(n), ordered=FALSE)其中,
n表示因子水平数;
k表示每个水平的重复数;
length表示生成序列的长度;
labels为表示因子水平的n维向量;
ordered指定是否为有序因子,TRUE为有序因子,FALSE为无序因子。
#生成水平数为3,每个水平重复2次的因子序列
gl(3,2)
[1] 1 1 2 2 3 3
Levels: 1 2 3
#生成水平为“TRUE”和“FALSE”,每个水平重复3次的因子序列
gl(2,3,labels=c("TRUE","FALSE"))
[1] TRUE TRUE TRUE FALSE FALSE FALSE
Levels: TRUE FALSE
#生成水平数为2,序列长度为10的因子序列
gl(2,1,10)
[1] 1 2 1 2 1 2 1 2 1 2
Levels: 1 2
#生成水平数为3,每个水平重复2次的有序因子序列
gl(3,2,ordered = TRUE)
[1] 1 1 2 2 3 3
Levels: 1 < 2 < 32. 使用因子
R语言中,因子是以整数型向量存储的,每个因子水平对应一个整数型的数。对字符型向量创建的因子,可以指定因子水平顺序,否则默认会按照字母顺序,再对应到整数型向量。
考虑用一个变量存储字符串型的月份:
x1 <- c("Dec", "Apr", "Jan", "Mar")这有两个问题:
(1) 实际只需要12个月份值,对拼写错误也无能为力;
(2) 不会按照想要的方式排序:
> sort(x1)
[1] "Apr" "Dec" "Jan" "Mar"若改用因子型就能避免上述问题。创建因子型,首先要创建一个有效的“水平值”列表:
month_levels <- c(
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)现在来创建因子:
> month_levels <- c(
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ )
> y1 <- factor(x1, levels = month_levels)
> y1
[1] Dec Apr Jan Mar
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
> sort(y1)
[1] Jan Mar Apr Dec
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec并且任何不在水平集中的值都将转化为NA值:
> x2 <- c("Dec", "Apr", "Jam", "Mar")
> y2 <- factor(x2, levels = month_levels)
> y2
[1] Dec Apr <NA> Mar
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec有时候你可能更希望让水平的顺序与其在数据集中首次出现的次序相匹配,设置levels=unique(x)即可,或者用管道操作在其后增加fct_inorder():
> f1 <- factor(x1, levels = unique(x1))
> f1
[1] Dec Apr Jan Mar
Levels: Dec Apr Jan Mar用levels()函数可以返回因子变量所有的有效水平:
> levels(y1)
[1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"附 数据结构的判别与转换

主要参考文献:
[1] 任坤,R语言编程指南. 人民邮电出版社, 2017.
[2] R for Data Science. Hadley Wickham, Garrett Grolemund,O'Reilly, 2017.
原创作品,转载请注明。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3130120/ ,如需转载,请注明出处,否则将追究法律责任。