数据框是由列组成的列表
数据框是 R 的表格:每行是一条观测,每列是一个变量。它就是 R 里的电子表格,几乎所有真实数据都是这个形状:每个人、每次测量、每笔交易占一行,每个属性占一列。
从结构上看,数据框是一个由等长向量组成的列表,再加上行和列的行为。这一点能解释它的大部分规则:每列只有一种类型(因为列是向量),不同的列可以有不同的类型(因为数据框是列表),而且每列长度必须相同(这就是“框”的含义)。
把具名向量交给 data.frame() 就能创建一个数据框:
文本、数字和逻辑值并排放在一起,这是矩阵做不到的。实际上你很少会像这样手动输入数据,表格通常来自 read.csv 或数据库。但无论数据框从哪里来,下面的内容都完全适用。
快速了解一个数据框
没看过的数据集,千万别直接拿来计算。R 用来查看数据的工具不多,值得养成随手就用的习惯:
str()是拿到新数据后第一个要运行的:每列一行,显示类型和前几个值。如果某列“数字”因为混进一个异常值而被读成了 character,你在这里就能发现。nrow()/ncol()给出维度;names()列出所有列名。summary()给出每列的统计信息:数值列有最小值、中位数、平均值、最大值,逻辑列和因子列有计数。head(df)和tail(df)打印前六行和后六行,当真实数据集大到无法整个打印时,它们必不可少。下面你会在一个内置数据集上看到head()的用法。
取出一列的三种写法
访问列是最常用的操作,R 提供了三种写法:
people$age:日常写法,简短易读,大多数编辑器都能自动补全。people[["age"]]:结果完全相同,但列名可以来自变量(col <- "age"; people[[col]]),这是$做不到的。在函数内部优先用它。people[, "age"]:矩阵风格,逗号前是行(留空表示全部),逗号后是列。
三者都把这一列作为普通向量返回,可以直接用于 mean(people$age)。代码片段里的第四种写法是个陷阱:用单层方括号的 people["age"] 返回的是只有一列的数据框,而不是向量。这和列表中 [ 与 [[ 的区别是一回事,因为数据框本身就是列表。如果某个数学函数抱怨你的输入不是数值,先检查一下方括号。
行和单元格
用 [row, column] 写法可以取出表格的任意部分:
people[2, ] 是整个第二行(返回一个单行数据框;行里混合了多种类型,所以仍然保持数据框的形式)。people[2, "name"] 是单个单元格。用一个行号向量可以一次取出多行。
最后一行最重要:对某列写一个逻辑条件,就能选出符合条件的行,这里选出的是所有年龄超过 28 岁的人。这种用逻辑掩码筛选行的模式是 R 中所有数据筛选的基础,值得单独写一篇:完整讲解请看筛选与子集,包括 subset() 和多条件筛选。
添加和删除列
因为数据框是由列组成的列表,所以列的增删只需普通的赋值:
给新的列名赋值就会添加一列;向量长度必须等于 nrow()(或者长度为 1,会循环填满每一行)。第二次添加展示了地道的做法:用已有的列计算出新列,向量化运算一次作用于整列。赋值 NULL 会把一列彻底删除。
列中的值也可以用同样的方式覆盖:people$age <- people$age + 1 让每个人都长了一岁。
内置数据集,以及 tibble 简介
R 自带一些预先加载好的练习数据集,所以上面的内容你不用导入任何东西就能试。几乎每个教程都会用到的两个是 mtcars(32 辆汽车,11 个数值变量)和 iris(150 朵花,4 项测量值加一个表示品种的因子):
这里能看出 head() 的价值:六行就能让你了解数据的形状,又不会刷满屏幕。这些数据集是理想的练习场:当你不确定某个函数的行为时,先在 mtcars 上试一试,再用到自己的数据上。
一接触 tidyverse 你就会遇到一个术语:tibble,它是 tidyverse 版本的数据框。概念相同,行为更友好:打印时只显示前十行和各列类型,也不会对列名做部分匹配。用 tibble() 创建,或者由 readr/dplyr 的函数返回:
library(tibble)
tibble(name = c("Rosa", "Ken"), age = c(30, 41))
#> # A tibble: 2 x 2
#> name age
#> <chr> <dbl>
#> 1 Rosa 30
#> 2 Ken 41
本文的所有内容($、str()、添加列、逻辑行掩码)在 tibble 上都照样适用。tibble 本质上就是打磨过的数据框;学会了数据框,两者你就都会了。
要点总结
- 数据框是由等长列组成的列表:每列一种类型,整个表格可以混合多种类型,它就是 R 的电子表格。
- 先查看再计算:先用
str(),再用head()、summary()、nrow()/ncol()/names()。 df$col、df[["col"]]和df[, "col"]都把列作为向量返回;单层方括号的df["col"]返回数据框。df[rows, cols]可以切出任意部分;逗号前写逻辑掩码就能筛选行。- 用赋值添加列(常常由其他列计算得出);用
NULL删除列;用mtcars练手。
下一篇:缺失值,讲 NA 的含义、它为什么会在计算中扩散,以及该如何处理它。
常见问题
在 R 中怎么创建数据框?
把等长的具名向量传给 data.frame():df <- data.frame(name = c("Rosa", "Ken"), age = c(30, 41))。每个向量成为一列。实际工作中,大多数数据框都不是手动输入的,而是来自 read.csv() 或数据库,但结构完全一样。
怎么访问 R 数据框中的一列?
有三种写法:df$age(简洁易读)、df[["age"]](结果相同,列名存在变量里时也能用)、df[, "age"](矩阵风格)。三者都把这一列作为普通向量返回。而用单层方括号的 df["age"] 返回的是只有一列的数据框,这是一个常见的困惑来源。
str() 对数据框会显示什么?
每列一行:列名、类型和前几个值,顶部还有行数和列数。这是看清某列被读成数值还是文本的最快方法,所以拿到任何新加载的数据集,第一件事就应该运行 str()。
怎么给 R 数据框添加一列?
给一个还不存在的列名赋值:df$score <- c(88, 75, 93)。向量长度必须等于行数(或者长度为 1,会循环填满每一行)。要删除一列,就给它赋值 NULL:df$score <- NULL。