【问题标题】:summarize groups into intervals using dplyr使用 dplyr 将组汇总为间隔
【发布时间】:2026-01-17 13:55:01
【问题描述】:

H, 我有一个这样的数据框:

d <- data.frame(v1=seq(0,9.9,0.1),
            v2=rnorm(100),
            v3=rnorm(100))

> head(d)
   v1          v2         v3
1 0.0 -0.01431916 -0.5005415
2 0.1 -1.01575590  1.5307473
3 0.2  1.00081065 -0.1730830
4 0.3 -1.20697918  0.5105118
5 0.4 -2.16698578 -1.0120544
6 0.5  0.33886508  0.4797016

我现在想要一个新的数据框,它总结了区间 0-0.99、1-1.99、2-2.99、3-3.99 中的所有值,例如平均值

喜欢这个

start end mean.v2 mean.v3
    0   1     0.2     0.1
    1   2     0.5     0.4

等等

谢谢

更新我应该补充一点,在我的真实数据集中,每个间隔中的观察具有不同的长度,它们并不总是从零开始或在 10 结束

【问题讨论】:

  • 您可以使用cut。也许d %&gt;% group_by(v1 = cut(v1, breaks= c(-Inf,0, 0.99, 1.99, 2.99, Inf))) %&gt;% summarise_each(funs(mean))
  • @akrun 我在问题中添加了一些进一步的信息。应该有一种方法,我不必手动设置间隔

标签: r dplyr


【解决方案1】:

这是@akrun 建议的使用cut() 的一种方法:

d %>% mutate( ints = cut(v1 ,breaks = 11)) %>% 
   group_by(ints) %>% 
   summarise( mean.v2 = mean(v2) , mean.v3 = mean(v3) )

【讨论】:

  • 谢谢,但我添加了一些我忘记的额外约束。每个区间的观察结果可能不同,它应该适用于具有不同开始和结束值的不同数据集
  • 我的解决方案不知道每个区间内的观察次数及其范围,您只需设置所需的休息次数。也许我误解了你;在这种情况下,您应该提供一个最小的示例和预期的输出。
  • 所以中断类似于 max(ceiling(d$v1))+1 吗?有没有办法让区间包含括号从 (..] 到 [..) ?
  • cut的帮助下:“当breaks被指定为单个数字时,数据的范围被划分为等长的breaks块,然后向外移动0.1范围的百分比 [...]"
  • 要“获取从 (..] 到 [..) 的区间包含括号”,cut() 的帮助告诉您它有一个参数 right
【解决方案2】:

基于@David H 的回答,有 2 个选项可供选择:

  1. 使用中断向量生成带有cut() 的区间
  2. 使用floor() 而不是cut() 生成间隔

创建数据

set.seed(33)
d <- data.frame(v1=seq(0,9.9,0.1),
            v2=rnorm(100),
            v3=rnorm(100))

使用中断向量生成带有cut() 的区间

对于这个简单的示例,您可以使用breaks &lt;- 0:10,但更一般地说,让我们取d$v1 的最小值和最大值。

breaks <- floor(min(d$v1)):ceiling(max(d$v1))
breaks 
# [1]  0  1  2  3  4  5  6  7  8  9 10

总结区间 0-0.99, 1-1.99, 2-2.99, 3-3.99,....

d %>% 
    mutate(interval = cut(v1,
                          breaks, 
                          include.lowest = TRUE, 
                          right = FALSE)) %>%
    group_by(interval) %>% 
    summarise( mean.v2 = mean(v2) , mean.v3 = mean(v3))

# Source: local data frame [10 x 3]
# 
#    interval     mean.v2     mean.v3
#      (fctr)       (dbl)       (dbl)
# 1     [0,1) -0.13040624 -0.20781247
# 2     [1,2)  0.26505794  0.51990167
# 3     [2,3)  0.13451628  1.12066174
# 4     [3,4)  0.23451272 -0.14773437
# 5     [4,5)  0.34326922  0.28567969
# 6     [5,6) -0.77059944 -0.16629580
# 7     [6,7) -0.17617190  0.03320797
# 8     [7,8)  0.86550135 -0.24664350
# 9     [8,9) -0.06652047 -0.27798769
# 10   [9,10] -0.10424865  0.24060163

使用floor() 而不是cut() 生成间隔

通过从每个间隔的末尾减去一个小数 1e-9 来作弊。

d %>% 
    mutate(start = floor(v1), end = start + 1 - 1e-9 ) %>%
    group_by(start, end) %>% 
    summarise_each(funs(mean))

# Source: local data frame [10 x 4]
# Groups: start [?]
# 
#    start   end     mean.v2     mean.v3
#    (dbl) (dbl)       (dbl)       (dbl)
# 1      0     1 -0.13040624 -0.20781247
# 2      1     2  0.26505794  0.51990167
# 3      2     3  0.13451628  1.12066174
# 4      3     4  0.23451272 -0.14773437
# 5      4     5  0.34326922  0.28567969
# 6      5     6 -0.77059944 -0.16629580
# 7      6     7 -0.17617190  0.03320797
# 8      7     8  0.86550135 -0.24664350
# 9      8     9 -0.06652047 -0.27798769
# 10     9    10 -0.10424865  0.24060163

【讨论】:

    【解决方案3】:

    使用 floor() 和 ceiling() 函数。以及 ifelse() 在间隔为 1 - 1 或 2 - 2 的情况下。

    d<-data.frame(v1=seq(0,9.9,0.1),
                  v2=rnorm(100),
                  v3=rnorm(100))          
    
    library(dplyr)
    
    d%>%
            mutate(start=floor(v1),
                   end=ifelse(ceiling(v1)==start,start+1,ceiling(v1)))%>%
            group_by(start,end)%>%
            summarise(mean.v2=mean(v2),
                      mean.v3=mean(v3))
    
    Source: local data frame [10 x 4]
    Groups: start [?]
    
       start   end      mean.v2     mean.v3
       (dbl) (dbl)        (dbl)       (dbl)
    1      0     1  0.135180183 -0.36083298
    2      1     2 -0.245567899  0.26827020
    3      2     3 -0.051136441  0.14211666
    4      3     4  0.252451303  0.38530797
    5      4     5  0.007209073  0.30137345
    6      5     6 -0.307008690  0.07662942
    7      6     7  0.103271270  0.14734865
    8      7     8  0.016753997 -0.02559756
    9      8     9 -0.199958098 -0.21821830
    10     9    10  0.532339512 -0.46509108
    

    相同,但包括一个名为间隔的列,而不是两个(开始和结束):

    d%>%
            mutate(start=floor(v1),
                   end=ifelse(ceiling(v1)==start,start+1,ceiling(v1)),
                   interval=paste(start,"-",end))%>%
            select(-start,-end)%>%
            group_by(interval)%>%
            summarise(mean.v2=mean(v2),
                      mean.v3=mean(v3))
    
    Source: local data frame [10 x 3]
    
       interval      mean.v2     mean.v3
          (chr)        (dbl)       (dbl)
    1     0 - 1  0.135180183 -0.36083298
    2     1 - 2 -0.245567899  0.26827020
    3     2 - 3 -0.051136441  0.14211666
    4     3 - 4  0.252451303  0.38530797
    5     4 - 5  0.007209073  0.30137345
    6     5 - 6 -0.307008690  0.07662942
    7     6 - 7  0.103271270  0.14734865
    8     7 - 8  0.016753997 -0.02559756
    9     8 - 9 -0.199958098 -0.21821830
    10   9 - 10  0.532339512 -0.46509108
    

    【讨论】:

    • OP 希望第一个间隔为 0-0.99。此代码从第一个间隔中排除 1,因为 floor(1) == ceiling(1)。这很好,但不如使用带有 right 参数的 cut() 函数透明。
    • 你是对的,这就是我使用 ifelse() 函数的原因。当您得到 1 - 1 时,代码解释为 2 - 1。但您也可以更改代码,使其为 0 - 1(在 ifelse 函数中)。代码非常通用。