百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

涛哥文章系列(21):利用管道连接多重操作

bigegpt 2024-08-15 19:54 2 浏览

从这篇文章开始起,我觉得还是给我的文章系列编个号吧。因为如果没有编号,大家阅读可能会遗漏、跳过,我提及前面文章中的部分内容时,也没有太好的标签。比如这一篇文章和上一篇《dplyr语法基础(为管道操作做准备)》是联系的比较紧密的。

为了探索dplyr的基本数据操作,我们还是使用上篇文章用到的nycflights13::flights。这个数据框包含了2013年从纽约出发的所有336,776个航班。数据来自美国运输统计局,可以用?flights查看帮助文件。

library(nycflights13)
library(tidyverse)
flights

将多重操作与管道相结合想象一下,我们想要探索每个位置的距离和平均延迟之间的关系。根据您对dplyr的了解,您可以编写如下代码:

by_dest <- group_by(flights, dest)
delay <- summarize(by_dest,
                   count = n(),
                   dist = mean(distance, na.rm = TRUE),
                   delay = mean(arr_delay, na.rm = TRUE)
)
delay <- filter(delay, count > 20, dest != "HNL")
# It looks like delays increase with distance up to ~750 miles
# and then decrease. Maybe as flights get longer there's more
# ability to make up delays in the air?
ggplot(data = delay, mapping = aes(x = dist, y = delay)) +
  geom_point(aes(size = count), alpha = 1/3) +
  geom_smooth(se = FALSE)

准备此数据有三个步骤:

1.按目的地分组航班。

2.汇总计算距离、平均延误和航班数。

3.过滤掉噪声点和火奴鲁鲁机场,火奴鲁鲁机场的距离几乎是第二个最近机场的两倍。

这段代码编写起来有点令人沮丧,因为我们必须为每个中间数据帧指定一个名称,尽管我们并不关心它。给事物命名很难,所以这会减慢我们的分析速度。

还有另一种方法来解决相同问题,使用管道%>%

delays <- flights %>%
  group_by(dest) %>%
  summarize(
    count = n(),
    dist = mean(distance, na.rm = TRUE),
    delay = mean(arr_delay, na.rm = TRUE)
  ) %>%
  filter(count > 20, dest != "HNL")

这侧重于转换,而不是正在转换的内容,这使得代码更易于阅读。您可以将其理解为一系列命令性语句:分组,然后汇总,然后过滤。阅读代码时%>%发音的一个好方法是“then”。在幕后,x%>%f(Y)变成f(x,y),x%>%f(y)%>%g(z)变成g(f(x,y),z),依此类推。您可以使用管道以一种可以从左到右、从上到下阅读的方式重写多个操作。从现在开始我们将经常使用管道,因为它极大地提高了代码的可读性。使用管道是属于tidyverse的关键标准之一。唯一的例外是ggplot2:它是在发明管道之前写好的。不幸的是,ggplot2的下一代ggvis(它确实使用管道)还没有准备好进入黄金时间。

缺少值

您可能对我们之前使用的na.rm参数感到疑惑。如果我们不设置会发生什么?

flights %>%
  group_by(year, month, day) %>%
  summarize(mean = mean(dep_delay))

我们得到了很多遗漏的值!这是因为聚合函数遵循通常的缺失值规则:如果输入中有任何缺失值,则输出将是缺失值。幸运的是,所有聚合函数都有一个na.rm参数,它可以在计算之前删除未命中的值:

flights %>%
  group_by(year, month, day) %>%
  summarize(mean = mean(dep_delay, na.rm = TRUE))

在这种情况下,如果缺少的值表示取消的航班,我们也可以通过首先删除取消的航班来解决这个问题。我们将保存此数据集,以便在接下来的几个示例中重用它:

not_cancelled <- flights %>%
  filter(!is.na(dep_delay), !is.na(arr_delay))
not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(mean = mean(dep_delay))

计数

每当您进行任何聚合时,最好包括一个计数(n())或一个非缺失值的计数(sum(!is.na(x)))。这样你就可以检查你不是基于非常少量的数据得出结论。例如,让我们看一下平均延迟最高的飞机(由其尾部编号标识):

delays <- not_cancelled %>%
  group_by(tailnum) %>%
  summarize(
    delay = mean(arr_delay)
  )
ggplot(data = delays, mapping = aes(x = delay)) +
  geom_freqpoly(binwidth = 10)

哇,有些飞机平均延误5小时(300分钟)!这个故事实际上更加细致入微。如果我们画一张航班数量与平均延误的散点图,我们可以得到更多的洞察力:

delays <- not_cancelled %>%
  group_by(tailnum) %>%
  summarize(
    delay = mean(arr_delay, na.rm = TRUE),
    n = n()
  )
ggplot(data = delays, mapping = aes(x = n, y = delay)) +
  geom_point(alpha = 1/10)

不足为奇的是,当航班很少时,平均延误的变化要大得多。这张图的形状很有特色:每当你绘制平均值(或其他汇总)与组大小的关系图时,你会看到变化随着样本量的增加而减少。在查看这类曲线图时,筛选出观察次数最少的组通常很有用,这样您就可以在最小的组中看到更多的模式和更少的极端变化。下面的代码就是这样做的,并向您展示了一个将ggplot2集成到dplyr流中的便捷模式。必须从%>%切换到+有点痛苦,但是一旦掌握了诀窍,就相当方便了:

delays %>%
  filter(n > 25) %>%
  ggplot(mapping = aes(x = n, y = delay)) +
  geom_point(alpha = 1/10)

这种类型的图案还有另一种常见的变体。让我们来看看棒球击球手的平均表现与他们击球次数之间的关系。在这里,我使用来自Lahman包的数据来计算每个大联盟棒球运动员的击球平均数(击打次数/尝试次数)。当我绘制击球手的技术(用击球平均数ba衡量)和击球机会(用at bat衡量,ab)时,你会看到两种模式:

  • 如上所述,随着数据点的增加,我们的总变化量会减少。
  • 技术(ba)与击球机会(Ab)呈正相关。这是因为球队控制着谁可以上场,显然他们会挑选出他们最好的球员:
# Convert to a tibble so it prints nicely
batting <- as_tibble(Lahman::Batting)
batters <- batting %>%
  group_by(playerID) %>%
  summarize(
    ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
    ab = sum(AB, na.rm = TRUE)
  )
batters %>%
  filter(ab > 100) %>%
  ggplot(mapping = aes(x = ab, y = ba)) +
    geom_point() +
    geom_smooth(se = FALSE)
#> `geom_smooth()ùsing method = 'gam'

这对排名也有重要影响。如果你天真地按照desc(ba)排序,那么击球率最好的人显然是幸运的,而不是熟练的:

batters %>%
  arrange(desc(ba))

有用的汇总函数

仅使用均值、计数和求和可以让您走很长一段路,但是R提供了许多其他有用的汇总函数:

位置度量

我们已经使用了mean(x),但是medium(x)也很有用。平均值是总和除以长度;中位数是x的50%在其上方,50%在其下方的值。

有时将聚合与逻辑子集结合起来很有用。

not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(
    # average delay:
    avg_delay1 = mean(arr_delay),
    # average positive delay:
    avg_delay2 = mean(arr_delay[arr_delay > 0])
  )

分布的测量sd(x), IQR(x), mad(x)

均方差,或标准差,简称sd,是分布的标准度量。四分位数范围IQR()和中位数绝对偏差mad(x)是稳健的等价物,如果您有异常值,它们可能会更有用:

# Why is distance to some destinations more variable
# than to others?
not_cancelled %>%
  group_by(dest) %>%
  summarize(distance_sd = sd(distance)) %>%
  arrange(desc(distance_sd))

秩的测量 min(x), quantile(x, 0.25), max(x)

分位数是中位数的推广。例如,quantile(x, 0.25)将找到大于25%的值、小于其余75%的值x:

# When do the first and last flights leave each day?
not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(
    first = min(dep_time),
    last = max(dep_time)
  )

位置的测量 first(x), nth(x, 2), last(x)

它们的工作方式类似于x[1]、x[2]和x[length(x)],但是如果该位置不存在(即,您试图从只有两个元素的组中获取第三个元素),则允许您设置默认值。例如,我们可以找到每天的第一个和最后一个出发的航班:

not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(
    first_dep = first(dep_time),
    last_dep = last(dep_time)
  )

这些函数是对秩过滤的补充。filter为您提供所有变量,每个观察值都在单独的行中:

not_cancelled %>%
  group_by(year, month, day) %>%
  mutate(r = min_rank(desc(dep_time))) %>%
  filter(r %in% range(r))

计数

您已经看到n(),它不接受任何参数,并返回当前组的大小。要计算未丢失的值的数量,请使用sum(!is.na(x))。要计算不同(唯一)值的数量,请使用n_distinct(x)

# Which destinations have the most carriers?
not_cancelled %>%
  group_by(dest) %>%
  summarize(carriers = n_distinct(carrier)) %>%
  arrange(desc(carriers))

计数非常有用,如果您只想要一个计数,dplyr会提供一个简单的帮助器:

not_cancelled %>%
  count(dest)

您可以选择提供权重变量。例如,您可以使用此命令“计算”(求和)飞机飞行的总里程数:

not_cancelled %>%
  count(tailnum, wt = distance)

逻辑值的计数和比例sum(x > 10), mean(y == 0)

与数值函数一起使用时,TRUE转换为1,FALSE转换为0。这使得sum()和means()非常有用:sum(x)给出x中的TRUE数,而mean(x)给出比例:

# How many flights left before 5am? (these usually
# indicate delayed flights from the previous day)
not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(n_early = sum(dep_time < 500))
# What proportion of flights are delayed by more
# than an hour?
not_cancelled %>%
  group_by(year, month, day) %>%
  summarize(hour_perc = mean(arr_delay > 60))

按多个变量分组

当您按多个变量分组时,每个摘要都会剥离分组的一个级别。这使得逐步汇总数据集变得很容易:

daily <- group_by(flights, year, month, day)
(per_day <- summarize(daily, flights = n()))
(per_month <- summarize(per_day, flights = sum(flights)))
(per_year  <- summarize(per_month, flights = sum(flights)))

在逐级汇总汇总时要小心:总和和计数都可以,但需要考虑加权方法和方差,而不可能完全针对基于排名的统计量(如中位数)这样做。换句话说,groupwise 总和是总和,但groupwise 中位数的中位数不是总体中位数。

取消分组

如果需要删除分组,并返回到对取消分组的数据的操作,请使用ungroup()

daily %>%
  ungroup() %>%             # no longer grouped by date
  summarize(flights = n())  # all flights

分组的mutate(和filter)分组与summarize()配合使用最有用,但您也可以使用mutate()和filter()执行方便的操作:

  • 找出每个组中最差的成员:
not_cancelled %>%
  group_by(year, month, day) %>%
  filter(rank(desc(arr_delay)) < 10)
  • 查找所有大于阈值的组:
popular_dests <- flights %>%
  group_by(dest) %>%
  filter(n() > 365)
popular_dests
  • 标准化以按组计算指标:
popular_dests %>%
  filter(arr_delay > 0) %>%
  mutate(prop_delay = arr_delay / sum(arr_delay)) %>%
  select(year:day, dest, arr_delay, prop_delay)

相关推荐

了解Linux目录,那你就了解了一半的Linux系统

大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...

Linux命令,这些操作要注意!(linux命令?)

刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...

Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!

在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...

linux必学的60个命令(linux必学的20个命令)

以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....

提高工作效率的--Linux常用命令,能够决解95%以上的问题

点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...

15 个实用 Linux 命令(linux命令用法及举例)

Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...

Linux 常用命令集合(linux常用命令全集)

系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...

Linux的常用命令就是记不住,怎么办?

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux常用文件操作命令(linux常用文件操作命令有哪些)

ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...

Linux 常用命令(linux常用命令)

日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E&#...

简单粗暴收藏版:Linux常用命令大汇总

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...

「Linux」linux常用基本命令(linux常用基本命令和用法)

Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...

Linux的常用命令就是记不住,怎么办?于是推出了这套教程

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux的30个常用命令汇总,运维大神必掌握技能!

以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...

Linux/Unix 系统中非常常用的命令

Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...