上一篇:在MacOS上安装以及使用Jupyter Notebook运行R语言

给媳妇@Keren Wang整理的一点学习笔记。希望可以帮你把这部分内容掌握透彻。

tidyverse和鹦鹉书

悉尼大学*OLET1601 Analysing and Plotting Data: R (Online)*的学习路线是先从tidyverse这个工具包开始。关于这个工具包,网上的中文资料几乎没有,但是细心的同学会发现OREILLY经典的《R for Data Science》猫头鹰鹦鹉书就是围绕着tidyverse展开的。事实上,这本书的作者、RStudio首席科学家Hadley Wickham就是tidyverse和ggplot2的作者。所以其实除了官方的英文文档以外,中英文版本的猫头鹰鹦鹉书也是非常理想的参考资料。英文版本的可以在线阅读,中文版本的我上传了一版pdf,有需要的自取,如有侵权请联系本人删除。

如果是在本地环境而非在线练习网站上运行tidyverse相关代码,我们需要在R语言中安装这个工具包:

1
install.packages("tidyverse")

需要重申的基本概念

(这一段主要写给第一节课没有好好复盘的@Keren Wang。)

  • 返回值(return):可以理解为一个函数执行后的输出。

  • 赋值语句:将某个值(数值、数据结构)赋给某个变量的过程。简单来说,以下语句都是典型的赋值语句。

1
2
3
4
a <- 1
b = 'hello world'
c = c(1, 2, 3)
d <- paste(a, b, c, 'yes')

​ 在执行完赋值表达式后,赋值符号(<-或者=)左边的变量就被赋予了代表右边的值(数值、数据结构、表达式执 行 结果)的含义。

  • 向量(Vector):向量从数据结构上看就是一个线性表(列表),可以看成一个数组。R 语言中向量作为一种类型存在可以让向量的操作变得更加容易。

  • c() :是一个创造向量的函数。我们新建两个向量a, b并输出向量加法的结果作为示例。

1
2
3
a = c(3, 4)
b = c(5, 0)
a + b

​ 运行代码,得到下图的结果,即输出[3, 4] + [5, 0]的运算结果:向量[8, 4]

  • 加或者不加print()的区别:在上图所示的例子中,我们可以看到即使没有print(a + b)也输出了结果。如果加上,那输出是这样:

    看起来好像只是多了个行序号而已。那么加不加print()有什么区别呢?事实上,只有在终端、jupyter等工作空间中逐行运行程序的时候,直接把某个值作为语句执行才会输出结果;而在运行一段脚本文件(xxx.R)的时候,没有使用print()函数的值语句是无法打印出来的。

  • 拼接字符串:R语言中使用paste()函数对字符串进行拼接,该函数接收数个值作为参数,拼接时会自动在值之间添加空格,返回值为一个字符串。

载入包、读取和展示数据

载入tidyverse

我们使用下列两条语句之一载入tidyverse工具包:

1
2
3
4
# option 1:
library(tidyverse)
# option 2:
require(tidyverse)

在R语言中,library()require()都可以载入工具包,二者之间的区别是后者具有bool类型的返回值:在一个函数中,如果一个包不存在,执行到library()将会停止执行,require()则会返回false后继续执行。下图就是获取了require(tidyverse)的返回值并输出结果,可以看到,因为载入了正确的包,require()输出了true

而如果载入了不存在或者未安装的包,则会返回false

读取数据

使用read_delim()可以读取任意分隔符的文件。在本章我们只需要了解该函数的两个参数即可:read_delim("文件名", delim = "分隔符类型")。下面两行代码分别代表读取使用逗号分隔的"OECDpop.csv"文件和读取使用空格分隔的“AustraliaDemographics.dat”文件(点击下载上述文件),返回一个tibble()类型的数据表(dataframe),并赋给名为pop_data的变量。

1
2
3
4
# 读取使用逗号分隔的"OECDpop.csv"文件
pop_data = read_delim('OECDpop.csv',delim = ',')
# 读取使用空格分隔的“AustraliaDemographics.dat”文件
pop_data <- read_delim("AustraliaDemographics.dat", delim = " ")

展示数据

  • 对于tibble类型的数据表,tail()head()函数分别可以返回数据表的最后和最前6行。
1
2
3
4
# 返回最后6行
tail(pop_data)
# 返回最前6行
head(pop_data)
  • 对于tibble类型的数据表,summary()函数可以输出数据表每一列的名称、数据类型、数值型数据的各种统计值和非数值型数据的长度。

选取某一部分数据(行与列的组合)

  • 假如我想在pop_data这个表中选取“Total”这一列,可以使用类似数组下标的方式。同时,如果下标有两项,第一项代表的就是行号。
1
2
3
4
# 取出Total这一列
a <- pop_data["Total"]
# 取出Total这一列的第五行
b <- pop_data[5, "Total"]
  • 如果我想选择某几列的数据,需要先把这几列的标题建立成一个向量,然后再对这个向量取下标。同时,如果下标有两项,第一项代表的是行号,且行号也可以是一个向量(代表取不止一行)
1
2
3
4
5
6
7
8
# 取出这三列
a <- pop_data[c("Male", "Female", "Total")]
# 取出这三列的第五行
b <- pop_data[5, c("Male", "Female", "Total")]
# 取出这三列的第5、6、7行
c <- pop_data[c(5, 6, 7), c("Male", "Female", "Total")]
# 取出Total这一列的第5、6、7行
d <- pop_data[c(5, 6, 7), "Total"]
  • 总而言之,就是单个值和向量的自由组合。但是注意⚠️:即使想取所有列,把列标题留空,也必须在行号后面保留逗号。如下所示:
1
2
3
4
# 取出所有列的第5、6、7行的错误做法
a <- pop_data[c(5, 6, 7)]
# 取出所有列的第5、6、7行的正确做法
a <- pop_data[c(5, 6, 7), ]

使用tidyverse简单分析数据

最大/最小值计算

假如我想计算某一列的最大值或者最小值,可以使用summarize()函数。该函数接收两个参数:summarize(数据表名, which.max(列标题)或者which.min(列标题)),输出的是tibble DataFrame格式当中的一个单元格,需要通过下文的强制类型转换为正常的数值才能进行运算。

1
2
# 计算pop_data表中Female这一列的最大值
idx <- summarize(pop_data, which.max(Female))

注意:

  • summarize()函数是一种通过某些计算输出新的DataFrame的函数,它接收DataFrame和需要生成的内容的规则作为参数,输出一个DataFrame格式的数据(可以是行号数据或者是一列数据)。

  • 后面我们会了解到,summarize()函数中的参数如果是which/which.xxx函数的话,输出的就是某一行的index;而如果是条件表达式xx == "xxx"的话,输出的就是一列完整的数据。下一章中我们会使用这个特性建立频率表。

类型转换

在数据表中即使精确定位了某一行、某一列的数据,输出的数据类型也仍然是tibble类型,如上文的idx变量。为了对这些数据进行运算或者操作,tidyverse定义了强制类型转换函数如as.numeric()as.character()

1
2
# 将idx转为数字后作为行标,取出这一行的State这一列,即得到了女性最多的州
fem_max_state <- pop_data[as.numeric(idx),"State"]

管道(pipeline)

管道是一种组合多个操作的方式。简单来说,如果你想对a进行b操作后计算得到c值,你大可以一步步保存中间变量,再做下一步操作;但是也可以通过管道的方式,使用%>%连接前后操作。我们以对pop_data按照State分组后,使用一个summarize函数分别计算出每个State的男性人口最大值、平均值和标准差,并保存到一个叫做sum的数据表当中为例,介绍管道的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义保存结果的变量名sum
sum <- pop_data %>%
# 使用group_by进行分组后
group_by(`State`) %>%
# 对每一组进行summarize
summarize(
# 计算男性人口最大值
max_M = max(Male),
# 计算男性人口平均值
average_M = mean(Male),
# 计算男性人口标准差
st_dev_M = sd(Male)
)

运行sum输出一下,表格长这样:

增加或修改列

使用mutate()函数增加或者修改列,函数必须接收数据表的名称和使用数据表中某些列进行的运算操作作为参数。mutate()总是将新列添加在数据集的最后。举例来说,使用管道新建列计算女性百分比的操作是这样写的:

1
2
3
# 如果不想覆盖原来的数据表,可以新建变量写成 new_pop_data <- pop_data %>%
pop_data <- pop_data %>%
mutate(women_perc = Female/Total*100)

如果不想使用管道,也可以写成:

1
2
3
4
5
pop_data <- mutate(
# 如果不想覆盖原来的数据表,可以新建变量写成 new_pop_data,
pop_data,
women_perc = Female/Total*100
)

运行head(pop_data)输出一下,两种方法都输出了这个:

可以看到,我们新建的women_perc已经添加在了最后。

过滤(filter)

filter()是tidyverse非常重要的一种方法。一般来说,这个函数接受的第一个参数是数据表的名称,后面数个参数是我们需要设定的过滤器,格式为“属性 == 某个值”。理解起来非常直白,下面展示几个例子:

1
2
3
4
5
6
# 取出子集:NSW州的人口数据表
df_nsw_T <- filter(pop_data, State == "NSW")
# 取出子集:ACT州的人口数据表中的Male这一列
df_act_male <- filter(pop_data, State == "NSW")["Male"]
# 取出子集:VIC州的人口数据表中2014年数据的的Male和Female这两列
df_vic_14_mf <- filter(pop_data, State == "VIC", Year == 2014)[c("Male", "Female")]

保留小数

使用round()函数控制保留位数(四舍五入)。

1
2
# 对浮点数3.1415926取小数点后3位,输出:3.142
pie <- round(3.1415926, 3)

参考资料

[1] Tidyverse Official Homepage https://www.tidyverse.org/packages/

[2] R for Data Science https://r4ds.had.co.nz/

下一篇:R语言-分析与绘图(2):使用rvest库抓取和分析网页信息