R数据科学(五)探索性数据分析

明确概念:探索性数据分析(exploratory data analysis, EDA),一般过程为:
(1) 对数据提出问题。
(2) 对数据进行可视化、转换和建模,进而找出问题的答案。
(3) 使用上一个步骤的结果来精炼问题,并提出新问题。

5.3.1 对分布进行可视化表示

确定变量是分类变量还是连续变量,要想检查分类变量的分布,可以使用条形图:

library(tidyverse)
head(diamonds)
ggplot(diamonds,aes(x=cut))+ geom_bar() 

条形的高度表示每个 x 值中观测的数量,可以使用 dplyr::count() 手动计算出这些值:

diamonds%>% count(cut)

要想检查连续变量的分布,可以使用直方图:

ggplot(data = diamonds) +
geom_histogram(mapping = aes(x = carat), binwidth = 0.5)

可以通过 dplyr::count() 和 ggplot2::cut_width() 函数的组合来手动计算结果. binwidth 参数来设定直方图中的间隔的宽度,该参数是用 x 轴变量的单位来度量的。

diamonds %>%
count(cut_width(carat, 0.5))

smaller <- diamonds %>% filter(carat < 3)

ggplot(diamonds,aes(carat)) + geom_histogram(bindwidth=0.1)

在同一张图上叠加多个直方图, 用geom_freqploy()代替geom_histogram(),用折线表示。

ggplot(smaller,aes(carat,color=cut)) + geom_freqpoly(binwidth=0.1)

相似值聚集形成的簇表示数据中存在子组。

5.3.3 异常值

ggplot(diamonds) + geom_histogram(aes(y),bindwidth=0.5)
# 可以看出y轴有一大片空白,因为有异常值出现
# 放大y轴看一下
ggplot(diamonds) + 
  geom_histogram(aes(y),binwidth = 0.5) +
  coord_cartesian(ylim = c(0,50))

# 用dplyr中的filter将异常值找出来
unusual <- diamonds %>% filter(y<3 | y>20) %>% arrange(y)
unusual

coord_cartesian() 函数中有一个用于放大 x 轴的 xlim() 参数。 ggplot2 中也有功能稍有区
别的 xlim() 和 ylim() 函数:它们会忽略溢出坐标轴范围的那些数据。
如果带有异常值和不带异常值的数据分别进行分析,结果差别较大的话要找出异常值的原因,如果差别不大,可以用NA代替。

5.3.4 练习
(1)研究 x、 y 和 z 变量在 diamonds 数据集中的分布。你能发现什么?思考一下,对于一条
钻石数据,如何确定表示长、宽和高的变量?

ggplot(diamonds) + geom_density(aes(z))
head(diamonds)
diamonds %>%
  mutate(id = row_number()) %>%
  select(x, y, z, id) %>%
  gather(variable, value, -id)  %>%
  ggplot(aes(x = value)) +
  geom_density() +
  geom_rug() +
  facet_grid(variable ~ .)


(2)研究 price 的分布,你能发现不寻常或令人惊奇的事情吗?(提示:仔细考虑一下
binwidth 参数,并确定试验了足够多的取值。)

head(diamonds)
diamonds  %>% ggplot() + geom_histogram(aes(price),binwidth = 10)
 ggplot(filter(diamonds,price<2500)) + geom_histogram(aes(price),binwidth = 10)
 
 ggplot(filter(diamonds), aes(x = price)) +
  geom_histogram(binwidth = 100, center = 0)
 
 diamonds %>%
  mutate(ending = price %% 10) %>%
  ggplot(aes(x = ending)) +
  geom_histogram(binwidth = 1, center = 0) +
  geom_bar()
 
 diamonds %>%
  mutate(ending = price %% 100) %>%
  ggplot(aes(x = ending)) +
  geom_histogram(binwidth = 1) +
  geom_bar()
 
 diamonds %>% mutate(ending=price%%1000) %>% filter(ending>500, ending<1000) %>%
   ggplot(aes(x=ending)) +
   geom_histogram(binwidth = 1) + geom_bar()

(3) 0.99 克拉的钻石有多少? 1 克拉的钻石有多少?造成这种区别的原因是什么?

head(diamonds)
diamonds %>% filter(carat >= 0.99, carat <= 1) %>% count(carat)
# 发现0.99的比较少,是因为0.01的差别,1克拉卖的比较贵吗?可以看下其他类型的数量
diamonds %>% filter(carat >= 0.9, carat <= 1.1) %>% count(carat) %>% print(n=30)

(4)比较并对比 coord_cartesina() 和 xlim()/ylim() 在放大直方图时的功能。如果不设置
binwidth 参数,会发生什么情况?如果将直方图放大到只显示一半的条形,那么又会发
生什么情况?

ggplot(diamonds) + geom_histogram(aes(price)) + coord_cartesian(xlim = c(100,5000),ylim = c(0,3000))
# 下面这种方法将不在xlim和ylim范围的值都去掉了,而coord_cartesian方法是计算后取值,并没有将范围外的点去掉。
ggplot(diamonds) + geom_histogram(aes(price)) + xlim(100,5000)+ylim(0,3000)

5.4 缺失值

数据中有异常值,可以将异常值去掉:

dim(diamonds)
diamonds2 <- diamonds %>% filter(between(y,3,20))
dim(diamonds2)

一般不建议去掉,建议使用缺失值来代替异常值。

diamonds2 <- diamonds %>% mutate(y=ifelse(y<3 | y>20, NA, y))

ifelse函数参数1放入逻辑判断,如果为T,结果就是第二个参数的值,如果为F,就是第三个参数的值。
ggplot2会忽略缺失值:

ggplot(diamonds2, aes(x,y)) + geom_point()
# na.rm=TRUE 可以去掉缺失值
# is.na将含NA的行进行区分
nycflights13::flights %>%
mutate(
cancelled = is.na(dep_time),
sched_hour = sched_dep_time %/% 100,
sched_min = sched_dep_time %% 100,
sched_dep_time = sched_hour + sched_min / 60
) %>%
ggplot(mapping = aes(sched_dep_time)) +
geom_freqpoly(
mapping = aes(color = cancelled),
binwidth = 1/4
)

练习
(1) 直方图如何处理缺失值?条形图如何处理缺失值?为什么会有这种区别?

diamonds2 <- diamonds %>%
  mutate(y = ifelse(y < 3 | y > 20, NA, y))

ggplot(diamonds2, aes(x = y)) +
  geom_histogram()
# 直方图对x进行计数,自动去掉缺失值
# 柱状图将NA也统计为一个变量进行计数
diamonds %>%
  mutate(cut = if_else(runif(n()) < 0.1, NA_character_, as.character(cut))) %>%
  ggplot() +
  geom_bar(mapping = aes(x = cut))

(2) na.rm = TRUE 在 mean() 和 sum() 函数中的作用是什么?
移除缺失值再进行统计

5.5 相关变动

5.5.1 分类变量与连续变量

# geom_freqpoly对于变异较小的变量不容易看出分别
# 这里频率多边形图显示的是数量
ggplot(diamonds,aes(price)) + geom_freqpoly(aes(color=cut),binwidth=500)

# 可以将y轴转变为密度
ggplot(diamonds,aes(price,..density..)) + geom_freqpoly(aes(color=cut),binwidth=500)

按分类变量的分组显示连续变量分布的另一种方式是使用箱线图

ggplot(diamonds,aes(x=cut,y=price)) + geom_boxplot()

ggplot(mpg,aes(class,hwy)) + geom_boxplot()
# 为了更容易发现趋势,可以基于 hwy 值的中位数对 class 进行重新排序
ggplot(mpg,aes(x=reorder(class,hwy,FUN=median),hwy)) + geom_boxplot()

# coord_flip将图形旋转 90 度
ggplot(mpg,aes(x=reorder(class,hwy,FUN=median),hwy)) + geom_boxplot() + coord_flip()

练习
(1) 前面对比了已取消航班和未取消航班的出发时间,使用学习到的知识对这个对比的可视
化结果进行改善。

# 比较取消和未取消的航班出发时间,简化为分类变量与连续变量的关系,用boxplot
head(nycflights13::flights)
nycflights13::flights %>% mutate( canceled = is.na(dep_time), 
                                  sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + sched_min / 60
  ) %>%
  ggplot() +
    geom_boxplot(mapping = aes(y = sched_dep_time, x = canceled))

(2) 在钻石数据集中,哪个变量对于预测钻石的价格最重要?这个变量与切割质量的关系是
怎样的?为什么这两个变量的关系组合会导致质量更差的钻石价格更高呢?

# 很明显carat与price的价格最相关了,这两个都是连续变量,可以用点图表示
ggplot(diamonds,aes(x=carat,y=price)) + geom_point()

# 也可以将x分割为一个个单元进行统计
ggplot(diamonds,aes(carat,price)) + geom_boxplot(aes(group=cut_width(carat,0.1)))

# 查看颜色与价格的关系
ggplot(diamonds,aes(color,price)) + geom_boxplot()

ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = clarity, y = price))

(3) 安装 ggstance 包,并创建一个横向箱线图。这种方法与使用 coord_flip() 函数有何区别?

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
  coord_flip()

library("ggstance")
# 可以看出x和y轴进行了调换
ggplot(data = mpg) +
  geom_boxploth(mapping = aes(y = reorder(class, hwy, FUN = median), x = hwy))

(4) 箱线图存在的问题是,在小数据集时代开发而成,对于现在的大数据集会显示出数量极
其庞大的异常值。解决这个问题的一种方法是使用字母价值图。安装 lvplot 包,并尝试
使用 geom_lv() 函数来显示价格基于切割质量的分布。你能发现什么问题?如何解释这
种图形?

library("lvplot")
ggplot(diamonds, aes(x = cut, y = price)) +
  geom_lv()

ggplot(diamonds, aes(x = cut, y = price)) +
  geom_boxplot()

(5) 比较并对比 geom_violin()、分面的 geom_histogram() 和着色的 geom_freqploy()。每种方法的优缺点是什么?

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)

ggplot(diamonds,aes(price)) + geom_histogram() + facet_wrap(~ cut, ncol = 1, scales = "free_y")

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_violin() +
  coord_flip()

(6) 对于小数据集,如果要观察连续变量和分类变量间的关系,有时使用 geom_jitter() 函数是特别有用的。 ggbeeswarm 包提供了和 geom_jitter()相似的一些方法。列出这些方法
并简单描述每种方法的作用。

library("ggbeeswarm")
ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(x = reorder(class, hwy, FUN = median),
                                 y = hwy))

ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(x = reorder(class, hwy, FUN = median),
                                 y = hwy),
                   method = "tukey")

ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(x = reorder(class, hwy, FUN = median),
                                 y = hwy),
                   method = "tukeyDense")

ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(x = reorder(class, hwy, FUN = median),
                                 y = hwy),
                   method = "frowney")

ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(x = reorder(class, hwy, FUN = median),
                                 y = hwy),
                   method = "smiley")

5.5.2 两个分类变量

两个分类变量的关系肯定要先计数,可以用geom_count()函数
d3heatmap 或 heatmaply 包可以生成交互式图

ggplot(diamonds) + geom_count(aes(cut,color))
str(diamonds)
# dplyr也可以用来计数
diamonds %>% count(color,cut)
#geom_tile函数填充热图
diamonds %>% count(color,cut) %>% ggplot(aes(color,cut)) +
  geom_tile(aes(fill=n))

练习
(1) 如何调整 count 数据,使其能更清楚地表示出切割质量在颜色间的分布,或者颜色在切
割质量间的分布?

# 另外一种表示方法就是求比例
library(viridis)
diamonds %>% count(color,cut) %>% group_by(color) %>% mutate(prop=n/sum(n)) %>% ggplot(aes(color,cut)) + geom_tile(aes(fill=prop)) + scale_fill_viridis(limits=c(0,1))

diamonds %>%
  count(color, cut) %>%
  group_by(cut) %>%
  mutate(prop = n / sum(n)) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(mapping = aes(fill = prop)) +
  scale_fill_viridis(limits = c(0, 1))

(2) 使用 geom_tile() 函数结合 dplyr来探索平均航班延误数量是如何随着目的地和月份的
变化而变化的。为什么这张图难以阅读?如何改进?

nycflights13::flights    %>%
  group_by(month, dest) %>%
  summarise(dep_delay = mean(dep_delay, na.rm = TRUE)) %>%
  ggplot(aes(x = factor(month), y = dest, fill = dep_delay)) +
  geom_tile() +
  labs(x = "Month", y = "Destination", fill = "Departure Delay")
# 可以看出信息很杂乱,可以对目的地排序,移除缺失值,改变颜色等进行改进
nycflights13::flights    %>% group_by(month,dest) %>% summarise(dep_delay=mean(dep_delay,na.rm=T)) %>% 
  group_by(dest) %>% filter(n() == 12) %>% ungroup() %>% mutate(dest=reorder(dest,dep_delay)) %>% 
  ggplot(aes(x=factor(month),y=dest,fill=dep_delay)) +
  geom_tile() + scale_fill_viridis() + labs(x = "Month", y = "Destination", fill = "Departure Delay")

(3) 为什么在以上示例中使用 aes(x = color, y = cut) 要比 aes(x = cut, y = color) 更好?

# 一般会将大数量的分类变量放在y轴或者名字比较长的放y轴
diamonds %>%
  count(color, cut) %>%  
  ggplot(mapping = aes(y = color, x = cut)) +
    geom_tile(mapping = aes(fill = n))

diamonds %>%
  count(color, cut) %>%  
  ggplot(mapping = aes(x = color, y = cut)) +
    geom_tile(mapping = aes(fill = n))

5.5.3 两个连续变量

连续变量之间的关系一般用散点图来表示。geom_point()

ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price))
# 
ggplot(data = diamonds) +
geom_point(
mapping = aes(x = carat, y = price),
alpha = 1 / 100
)

对于大数据集,为了避免重合,可以用geom_bin2d() 和 geom_hex()函数将坐标平面分为二维分箱,并使用一种填充颜色表示落入
每个分箱的数据点。

ggplot(diamonds,aes(carat,price)) + geom_bin2d()

另一种方式是对一个连续变量进行分箱,因此这个连续变量的作用就相当于分类变量。
cut_width(x, width) 函数将 x 变量分成宽度为 width 的分箱。参数 varwidth = TRUE 让箱线图的宽度与观测数量成正比。
cut_number() 函数近似地显示每个分箱中的数据点的数量

ggplot(diamonds,aes(carat,price)) +geom_boxplot(aes(group=cut_width(carat,0.1))) 

ggplot(diamonds,aes(carat,price)) +geom_boxplot(aes(group=cut_number(carat,20))) 

练习
(1) 除了使用箱线图对条件分布进行摘要统计,你还可以使用频率多边形图。使用 cut_
width() 函数或 cut_number() 函数时需要考虑什么问题?这对 carat 和 price 的二维分
布的可视化表示有什么影响?

ggplot(diamonds,aes(color=cut_width(carat,1,boundary = 0),x=price)) + 
  geom_freqpoly() +ylab('Carat')

(2) 按照 price 分类对 carat 的分布进行可视化表示。

ggplot(diamonds,aes(cut_number(price,10),y=carat)) + geom_boxplot() +coord_flip() 

(3) 比较特别大的钻石和比较小的钻石的价格分布。结果符合预期吗?还是出乎意料?

(4) 组合使用你学习到的两种技术,对 cut、 carat 和 price 的组合分布进行可视化表示。

(5) 二维图形可以显示一维图形中看不到的离群点。例如,以下图形中的有些点具有异常的
x 值和 y 值组合,这使得这些点成为了离群点,即使这些点的 x 值和 y 值在单独检验时
似乎是正常的。
ggplot(data = diamonds) +
geom_point(mapping = aes(x = x, y = y)) +
coord_cartesian(xlim = c(4, 11), ylim = c(4, 11))

5.6 模式和模型

数据中的模式提供了关系线索,用于探索两个变量的相关性。
模型是用于从数据中抽取模式的一种工具。
残差(预测值和实际值之间的差别)

library(modelr)
mod <- lm(log(price) ~ log(carat),data=diamonds)

diamonds2 <- diamonds %>% add_residuals(mod) %>% mutate(resid=exp(resid))

ggplot(diamonds2) + geom_point(aes(carat,resid))

阅读推荐:
生信技能树公益视频合辑:学习顺序是linux,r,软件安装,geo,小技巧,ngs组学!
B站链接:https://m.bilibili.com/space/338686099
YouTube链接:https://m.youtube.com/channel/UC67sImqK7V8tSWHMG8azIVA/playlists
生信工程师入门最佳指南:https://mp.weixin.qq.com/s/vaX4ttaLIa19MefD86WfUA

你可能感兴趣的:(R数据科学(五)探索性数据分析)