如何在R语言基础中使用group by功能

12

我想使用 基础R(不使用任何特定软件包)表达以下SQL查询:

select month, day, count(*) as count, avg(dep_delay) as avg_delay
from flights
group by month, day
having count > 1000

它选择繁忙日(超过1000次航班的日期)的平均延误时间和每天航班数。数据集是nycflights13,其中包含2013年从纽约出发的航班信息。

请注意,我可以很容易地使用dplyr编写这个:

flights %>%
  group_by(month, day) %>%
  summarise(count = n(), avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  filter(count > 1000)

4
老实说,我建议您只使用 sqldf。您已经花费时间编写查询了。 - Tim Biegeleisen
sqldf并非基础操作,因此难以导出代码,不适用于软件包,了解基础操作对一般情况很有好处。 - moodymudskipper
我认为这样的代码应该可以运行:subset(aggregate(dep_delay ~ month + day, flights, function(x) data.frame(count=length(x), avg_delay=mean(x,na.rm=TRUE))), count>1000) - moodymudskipper
4个回答

12

既然之前提到了 by 的优雅之处(向 @Parfait 致敬),这里提供一个使用 by 的解决方案:

res <- by(flights, list(flights$month, flights$day), function(x)
    if (nrow(x) > 1000) {
        c(
            month = unique(x$month),
            day = unique(x$day),
            count = nrow(x),
            avg_delay = mean(x$dep_delay, na.rm = TRUE))
        })

# Store in data.frame and order by month, day
df <- do.call(rbind, res);
df <- df[order(df[, 1], df[, 2]) ,];
#     month day count avg_delay
#[1,]     7   8  1004 37.296646
#[2,]     7   9  1001 30.711499
#[3,]     7  10  1004 52.860702
#[4,]     7  11  1006 23.609392
#[5,]     7  12  1002 25.096154
#[6,]     7  17  1001 13.670707
#[7,]     7  18  1003 20.626789
#[8,]     7  25  1003 19.674134
#[9,]     7  31  1001  6.280843
#[10,]     8   7  1001  8.680402
#[11,]     8   8  1001 43.349947
#[12,]     8  12  1001  8.308157
#[13,]    11  27  1014 16.697651
#[14,]    12   2  1004  9.021978

by是一个很棒的基础R函数。我的唯一抱怨是,它返回的类为by的对象相当不完善(并且并不是那么必要)。我发现定义一个as.data.frame.by的方法,可以像你在这里展示的那样很有用。 - AdamO
我同意你的看法,@AdamO;但公平地说(对于 by:-) ,by 的返回对象只是一个带有一些额外属性的 list - Maurits Evers

4

如评论所述,您可以使用subsetaggregate的组合。更改天和月的顺序以接收与您的dplyr方法相同的顺序。使用na.action = NULL来计算包括NA在内的行数。

library(nycflights13) 
#> Warning: Paket 'nycflights13' wurde unter R Version 3.4.4 erstellt
subset(aggregate(dep_delay ~ day + month, flights, 
       function(x) cbind(count=length(x), avg_delay=mean(x, na.rm = TRUE)),
       na.action = NULL), 
       dep_delay[,1] > 1000)
#>     day month dep_delay.1 dep_delay.2
#> 189   8     7 1004.000000   37.296646
#> 190   9     7 1001.000000   30.711499
#> 191  10     7 1004.000000   52.860702
#> 192  11     7 1006.000000   23.609392
#> 193  12     7 1002.000000   25.096154
#> 198  17     7 1001.000000   13.670707
#> 199  18     7 1003.000000   20.626789
#> 206  25     7 1003.000000   19.674134
#> 212  31     7 1001.000000    6.280843
#> 219   7     8 1001.000000    8.680402
#> 220   8     8 1001.000000   43.349947
#> 224  12     8 1001.000000    8.308157
#> 331  27    11 1014.000000   16.697651
#> 336   2    12 1004.000000    9.021978

这段文本是由 reprex软件包 (版本0.2.0)于2018年04月05日创建的。


0
这不是特别优雅的解决方案,但这将使用Base R实现您想要的功能。
flights_split <- split(flights, f = list(flights$month, flights$day))

result <- lapply(flights_split, function(x) {
  if(nrow(x) > 1000) {
    data.frame(month = unique(x$month), day = unique(x$day), avg_delay = mean(x$dep_delay, na.rm = T), count = nrow(x))
  } else {
    NULL
  }
}
)

do.call(rbind, result)

#        month day mean_delay    n
#  12.2     12   2   9.021978 1004
#  8.7       8   7   8.680402 1001
#  7.8       7   8  37.296646 1004
#  8.8       8   8  43.349947 1001
#  7.9       7   9  30.711499 1001
#  7.10      7  10  52.860702 1004
#  7.11      7  11  23.609392 1006
#  7.12      7  12  25.096154 1002
#  8.12      8  12   8.308157 1001
#  7.17      7  17  13.670707 1001
#  7.18      7  18  20.626789 1003
#  7.25      7  25  19.674134 1003
#  11.27    11  27  16.697651 1014
#  7.31      7  31   6.280843 1001

0

这是我的解决方案:

grp <- expand.grid(mth = unique(flights$month), d = unique(flights$day))
out <- mapply(function(mth, d){
    sub_data <- subset(flights, month == mth & day == d)
    df <- data.frame(
        month = mth,
        day = d,
        count = nrow(sub_data), 
        avg_delay = mean(sub_data$dep_delay, na.rm = TRUE)
    )
    df[df$count > 1000]
}, grp$mth, grp$d)
res <- do.call(rbind, out)

这个比dplyr解决方案慢得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接