5.6 信息汇总summarise()
最后一个是summarise()
。它将数据框折叠为一行:
summarise(flights, delay = mean(dep_delay, na.rm = TRUE))
#> # A tibble: 1 x 1
#> delay
#> <dbl>
#> 1 12.6
(na.rm = TRUE
意味着什么?)
一般是将Summarise()
与group_by()
一起使用,否则它并不是特别有用。这将分析范围从完整数据集更改为单个组。然后,当您在分组数据帧上使用dplyr
时,它们将自动“按组”分配。例如,如果我们对日期分组,将得到每个日期的平均延迟:
by_day <- group_by(flights, year, month, day)
summarise(by_day, delay = mean(dep_delay, na.rm = TRUE))
#> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
#> # A tibble: 365 x 4
#> # Groups: year, month [12]
#> year month day delay
#> <int> <int> <int> <dbl>
#> 1 2013 1 1 11.5
#> 2 2013 1 2 13.9
#> 3 2013 1 3 11.0
#> 4 2013 1 4 8.95
#> 5 2013 1 5 5.73
#> 6 2013 1 6 7.15
#> # … with 359 more rows
group_by()和summarise()一起使用提供最常用的工具之一:分组摘要。但在我们进一步讨论这个问题之前,我们需要了解管道。
5.6.1 用管道连接多个操作
想象一下,我们要探索每个位置的距离和平均延迟之间的关系。通过对 dplyr 的了解,可以编写如下代码:
by_dest <- group_by(flights, dest)
delay <- summarise(by_dest,
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
)
#> `summarise()` ungrouping output (override with `.groups` argument)
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)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
准备这些数据需要三个步骤:
按目的地对航班分组。
统计并计算距离、平均延误和航班数量。
过滤以去除噪声点和檀香山机场,该机场的距离几乎是下一个最近机场的两倍。
这段代码写起来有点繁琐,因为我们必须为每个中间数据帧命名。对每个变量都要命名,因此这会减慢我们的分析速度。
以下方法通过管道可以解决相同的问题 %>%
:
delays <- flights %>%
group_by(dest) %>%
summarise(
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
) %>%
filter(count > 20, dest != "HNL")
#> `summarise()` ungrouping output (override with `.groups` argument)
实际上,x %>% f(y)
变成f(x, y)
,x %>% f(y) %>% g(z)
变成g(f(x, y), z)
等等。您可以使用管道以从左到右、从上到下阅读的方式重写多个操作。从现在开始我们将经常使用管道,因为它大大提高了代码的可读性,我们将在管道中更详细地回到它。
5.6.2 缺失值
我们上面使用的参数na.rm
。如果我们不设置它会发生什么?
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
#> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
#> # A tibble: 365 x 4
#> # Groups: year, month [12]
#> year month day mean
#> <int> <int> <int> <dbl>
#> 1 2013 1 1 NA
#> 2 2013 1 2 NA
#> 3 2013 1 3 NA
#> 4 2013 1 4 NA
#> 5 2013 1 5 NA
#> 6 2013 1 6 NA
#> # … with 359 more rows
我们将会得到了很多缺失值!这是因为聚合函数遵循缺失值的通用规则:如果输入中有任何缺失值,则输出将是缺失值。然而所有聚合函数都有一个na.rm
参数,我们可以在计算之前删除缺失值:
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay, na.rm = TRUE))
#> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
#> # A tibble: 365 x 4
#> # Groups: year, month [12]
#> year month day mean
#> <int> <int> <int> <dbl>
#> 1 2013 1 1 11.5
#> 2 2013 1 2 13.9
#> 3 2013 1 3 11.0
#> 4 2013 1 4 8.95
#> 5 2013 1 5 5.73
#> 6 2013 1 6 7.15
#> # … with 359 more rows
在此处,缺失值代表取消的航班,我们还可以先删除取消的航班来解决该问题。我们将保存此数据集,以便在接下来的几个示例中重复使用它。
not_cancelled <- flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay))
not_cancelled %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
#> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
#> # A tibble: 365 x 4
#> # Groups: year, month [12]
#> year month day mean
#> <int> <int> <int> <dbl>
#> 1 2013 1 1 11.4
#> 2 2013 1 2 13.7
#> 3 2013 1 3 10.9
#> 4 2013 1 4 8.97
#> 5 2013 1 5 5.73
#> 6 2013 1 6 7.15
#> # … with 359 more rows
5.6.3 计数
无论何时进行任何聚合,包含一个count (n()
)或一个非缺失值的计数(sum(!is.na(x)
)都是很好选择。这样你就可以确定你不是基于非常少量的数据得出结论。例如,让我们看看平均延误时间最高的飞机(通过机尾号来确定):
delays <- not_cancelled %>%
group_by(tailnum) %>%
summarise(
delay = mean(arr_delay)
)
#> `summarise()` ungrouping output (override with `.groups` argument)
ggplot(data = delays, mapping = aes(x = delay)) +
geom_freqpoly(binwidth = 10)
可以看到,有些飞机平均延误了 5 小时(300 分钟)!
如果我们绘制航班数量与平均延误的散点图,我们可以获得更多信息:
delays <- not_cancelled %>%
group_by(tailnum) %>%
summarise(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
#> `summarise()` ungrouping output (override with `.groups` argument)
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)
当我将击球手的技巧(以击球平均值ba
衡量)与击球机会数(以击球次数ab
衡量)作图时,您会看到两种模式:
如上所述,随着我们获得更多数据点,我们的聚合变化会减少。
击球技巧 (
ba
) 和击球次数(ab
)之间存在正相关关系。这是因为球队控制谁可以上场,而且显然他们会挑选最好的球员。
# Convert to a tibble so it prints nicely
batting <- as_tibble(Lahman::Batting)
batters <- batting %>%
group_by(playerID) %>%
summarise(
ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
ab = sum(AB, na.rm = TRUE)
)
#> `summarise()` ungrouping output (override with `.groups` argument)
batters %>%
filter(ab > 100) %>%
ggplot(mapping = aes(x = ab, y = ba)) +
geom_point() +
geom_smooth(se = FALSE)
#> `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
这对排名也有重要影响。如果你直接排序desc(ba)
,具有最佳击球率的人显然是幸运的,而不是技术娴熟的:
batters %>%
arrange(desc(ba))
#> # A tibble: 19,689 x 3
#> playerID ba ab
#> <chr> <dbl> <int>
#> 1 abramge01 1 1
#> 2 alanirj01 1 1
#> 3 alberan01 1 1
#> 4 banisje01 1 1
#> 5 bartocl01 1 1
#> 6 bassdo01 1 1
#> # … with 19,683 more rows
5.6.4 其它统计函数
只使用means, counts和 sum 可以解决很多问题,但R提供了许多其他有用的统计函数:
-
位置测量:我们使用过mean(x),但median(x)也很有用。平均值是总和除以长度;中位数是一个值,
x
中 50%高于中位数,50% 低于中位数。有时将汇总与逻辑子集相结合很有用。
not_cancelled %>% group_by(year, month, day) %>% summarise( avg_delay1 = mean(arr_delay), avg_delay2 = mean(arr_delay[arr_delay > 0]) # the average positive delay ) #> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument) #> # A tibble: 365 x 5 #> # Groups: year, month [12] #> year month day avg_delay1 avg_delay2 #> <int> <int> <int> <dbl> <dbl> #> 1 2013 1 1 12.7 32.5 #> 2 2013 1 2 12.7 32.0 #> 3 2013 1 3 5.73 27.7 #> 4 2013 1 4 -1.93 28.3 #> 5 2013 1 5 -1.53 22.6 #> 6 2013 1 6 4.24 24.4 #> # … with 359 more rows
-
数据分布:sd(x), IQR(x), mad(x)。均方根偏差或标准偏差sd(x)是数据分布的标准度量。四分位数IQR(x)和中值绝对偏差mad(x)`是有用的选项,如果您有异常值,它们可能更有用。
# Why is distance to some destinations more variable than to others? not_cancelled %>% group_by(dest) %>% summarise(distance_sd = sd(distance)) %>% arrange(desc(distance_sd)) #> `summarise()` ungrouping output (override with `.groups` argument) #> # A tibble: 104 x 2 #> dest distance_sd #> <chr> <dbl> #> 1 EGE 10.5 #> 2 SAN 10.4 #> 3 SFO 10.2 #> 4 HNL 10.0 #> 5 SEA 9.98 #> 6 LAS 9.91 #> # … with 98 more rows
-
等级度量:min(x), quantile(x, 0.25), max(x)。分位数和中位数定义相似。例如,quantile(x, 0.25) 会发现一个值
x
大于 25% 的值,而小于其余 75% 的值。# When do the first and last flights leave each day? not_cancelled %>% group_by(year, month, day) %>% summarise( first = min(dep_time), last = max(dep_time) ) #> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument) #> # A tibble: 365 x 5 #> # Groups: year, month [12] #> year month day first last #> <int> <int> <int> <int> <int> #> 1 2013 1 1 517 2356 #> 2 2013 1 2 42 2354 #> 3 2013 1 3 32 2349 #> 4 2013 1 4 25 2358 #> 5 2013 1 5 14 2357 #> 6 2013 1 6 16 2355 #> # … with 359 more rows
-
位置测量:
first(x)
,nth(x, 2)
,last(x)
。这些类似于x[1]
,x[2]
,x[length(x)]
但如果该位置不存在,则让您设置默认值(即您试图从只有两个元素的组中获取第三个元素)。例如,我们可以找到每天的第一次和最后一次出发:not_cancelled %>% group_by(year, month, day) %>% summarise( first_dep = first(dep_time), last_dep = last(dep_time) ) #> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument) #> # A tibble: 365 x 5 #> # Groups: year, month [12] #> year month day first_dep last_dep #> <int> <int> <int> <int> <int> #> 1 2013 1 1 517 2356 #> 2 2013 1 2 42 2354 #> 3 2013 1 3 32 2349 #> 4 2013 1 4 25 2358 #> 5 2013 1 5 14 2357 #> 6 2013 1 6 16 2355 #> # … with 359 more rows
这些函数是对排序过滤后的补充。过滤可以为您提供所有变量,每个观察值都在单独的行中:
not_cancelled %>% group_by(year, month, day) %>% mutate(r = min_rank(desc(dep_time))) %>% filter(r %in% range(r)) #> # A tibble: 770 x 20 #> # Groups: year, month, day [365] #> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time #> <int> <int> <int> <int> <int> <dbl> <int> <int> #> 1 2013 1 1 517 515 2 830 819 #> 2 2013 1 1 2356 2359 -3 425 437 #> 3 2013 1 2 42 2359 43 518 442 #> 4 2013 1 2 2354 2359 -5 413 437 #> 5 2013 1 3 32 2359 33 504 442 #> 6 2013 1 3 2349 2359 -10 434 445 #> # … with 764 more rows, and 12 more variables: arr_delay <dbl>, carrier <chr>, #> # flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, #> # distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>, r <int>
-
Counts:你已经使用了
n()
,它不接受任何参数,并返回当前组的大小。要计算非缺失值的数量,请使用sum(!is.na(x))
。要计算不同(唯一)值的数量,请使用n_distinct(x)
.# Which destinations have the most carriers? not_cancelled %>% group_by(dest) %>% summarise(carriers = n_distinct(carrier)) %>% arrange(desc(carriers)) #> `summarise()` ungrouping output (override with `.groups` argument) #> # A tibble: 104 x 2 #> dest carriers #> <chr> <int> #> 1 ATL 7 #> 2 BOS 7 #> 3 CLT 7 #> 4 ORD 7 #> 5 TPA 7 #> 6 AUS 6 #> # … with 98 more rows
计数非常有用,如果你只想要计数,dplyr 提供了一个函数
count()
:not_cancelled %>% count(dest) #> # A tibble: 104 x 2 #> dest n #> <chr> <int> #> 1 ABQ 254 #> 2 ACK 264 #> 3 ALB 418 #> 4 ANC 8 #> 5 ATL 16837 #> 6 AUS 2411 #> # … with 98 more rows
您可以提供权重变量。例如,您可以使用它来“count”(sum)飞机飞行的总英里数:
not_cancelled %>% count(tailnum, wt = distance) #> # A tibble: 4,037 x 2 #> tailnum n #> <chr> <dbl> #> 1 D942DN 3418 #> 2 N0EGMQ 239143 #> 3 N10156 109664 #> 4 N102UW 25722 #> 5 N103US 24619 #> 6 N104UW 24616 #> # … with 4,031 more rows
-
逻辑值的计数和比例:sum(x > 10), mean(y == 0)。 当与数字函数使用时,
TRUE
被转换成1和FALSE
转换成0。这使得sum()和mean()非常有用的: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) %>% summarise(n_early = sum(dep_time < 500)) #> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument) #> # A tibble: 365 x 4 #> # Groups: year, month [12] #> year month day n_early #> <int> <int> <int> <int> #> 1 2013 1 1 0 #> 2 2013 1 2 3 #> 3 2013 1 3 4 #> 4 2013 1 4 3 #> 5 2013 1 5 3 #> 6 2013 1 6 2 #> # … with 359 more rows # What proportion of flights are delayed by more than an hour? not_cancelled %>% group_by(year, month, day) %>% summarise(hour_prop = mean(arr_delay > 60)) #> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument) #> # A tibble: 365 x 4 #> # Groups: year, month [12] #> year month day hour_prop #> <int> <int> <int> <dbl> #> 1 2013 1 1 0.0722 #> 2 2013 1 2 0.0851 #> 3 2013 1 3 0.0567 #> 4 2013 1 4 0.0396 #> 5 2013 1 5 0.0349 #> 6 2013 1 6 0.0470 #> # … with 359 more rows
5.6.5 多变量分组
当您按多个变量分组时,每个摘要都会剥离分组的一个级别。这使得逐步汇总数据集变得容易:
daily <- group_by(flights, year, month, day)
(per_day <- summarise(daily, flights = n()))
#> `summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
#> # A tibble: 365 x 4
#> # Groups: year, month [12]
#> year month day flights
#> <int> <int> <int> <int>
#> 1 2013 1 1 842
#> 2 2013 1 2 943
#> 3 2013 1 3 914
#> 4 2013 1 4 915
#> 5 2013 1 5 720
#> 6 2013 1 6 832
#> # … with 359 more rows
(per_month <- summarise(per_day, flights = sum(flights)))
#> `summarise()` regrouping output by 'year' (override with `.groups` argument)
#> # A tibble: 12 x 3
#> # Groups: year [1]
#> year month flights
#> <int> <int> <int>
#> 1 2013 1 27004
#> 2 2013 2 24951
#> 3 2013 3 28834
#> 4 2013 4 28330
#> 5 2013 5 28796
#> 6 2013 6 28243
#> # … with 6 more rows
(per_year <- summarise(per_month, flights = sum(flights)))
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 1 x 2
#> year flights
#> <int> <int>
#> 1 2013 336776
逐步汇总汇总时要小心:sum和count是可以的,但需要考虑加权均值和方差,而对于基于等级的统计数据(如中位数),不可能完全做到这一点。换句话说,分组总和的总和是总和,但分组中位数的中位数不是总中位数。
5.6.6 取消分组
如果需要删除分组,并返回对未分组的数据,请使用ungroup()
.
daily %>%
ungroup() %>% # no longer grouped by date
summarise(flights = n()) # all flights
#> # A tibble: 1 x 1
#> flights
#> <int>
#> 1 336776
5.7 改变分组(和过滤器)
在与summarise()
结合使用时分组最有用,但也可以使用mutate()
和filter()
进行操作:
-
找出每组中最差的成员:
flights_sml %>% group_by(year, month, day) %>% filter(rank(desc(arr_delay)) < 10) #> # A tibble: 3,306 x 7 #> # Groups: year, month, day [365] #> year month day dep_delay arr_delay distance air_time #> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> #> 1 2013 1 1 853 851 184 41 #> 2 2013 1 1 290 338 1134 213 #> 3 2013 1 1 260 263 266 46 #> 4 2013 1 1 157 174 213 60 #> 5 2013 1 1 216 222 708 121 #> 6 2013 1 1 255 250 589 115 #> # … with 3,300 more rows
-
查找所有大于阈值的组:
popular_dests <- flights %>% group_by(dest) %>% filter(n() > 365) popular_dests #> # A tibble: 332,577 x 19 #> # Groups: dest [77] #> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time #> <int> <int> <int> <int> <int> <dbl> <int> <int> #> 1 2013 1 1 517 515 2 830 819 #> 2 2013 1 1 533 529 4 850 830 #> 3 2013 1 1 542 540 2 923 850 #> 4 2013 1 1 544 545 -1 1004 1022 #> 5 2013 1 1 554 600 -6 812 837 #> 6 2013 1 1 554 558 -4 740 728 #> # … with 332,571 more rows, and 11 more variables: arr_delay <dbl>, #> # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>, #> # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
-
标准化以计算每组指标:
popular_dests %>% filter(arr_delay > 0) %>% mutate(prop_delay = arr_delay / sum(arr_delay)) %>% select(year:day, dest, arr_delay, prop_delay) #> # A tibble: 131,106 x 6 #> # Groups: dest [77] #> year month day dest arr_delay prop_delay #> <int> <int> <int> <chr> <dbl> <dbl> #> 1 2013 1 1 IAH 11 0.000111 #> 2 2013 1 1 IAH 20 0.000201 #> 3 2013 1 1 MIA 33 0.000235 #> 4 2013 1 1 ORD 12 0.0000424 #> 5 2013 1 1 FLL 19 0.0000938 #> 6 2013 1 1 ORD 8 0.0000283 #> # … with 131,100 more rows