美文网首页数据-R语言-图表-决策-Linux-Python
R数据科学(五)探索性数据分析

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

作者: 子鹿学生信 | 来源:发表于2018-11-17 10:15 被阅读1次

    明确概念:探索性数据分析(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数据科学(五)探索性数据分析

        本文链接:https://www.haomeiwen.com/subject/arsbfqtx.html