【问题标题】:Avoiding the use of for loop for cumsum避免对 cumsum 使用 for 循环
【发布时间】:2018-05-26 03:05:44
【问题描述】:

首先生成一些样本数据:

 doy <- rep(1:365,times=2)
 year <- rep(2000:2001,each=365)
 set.seed(1)
 value <-runif(min=0,max=10,365*2)
 doy.range <- c(40,50,60,80)
 thres <- 200

 df <- data.frame(cbind(doy,year,value))

我想做的是:

对于df$year == 2000,从doy.range == 40开始,开始添加 df$value,当df$value的累计和>=thres时,计算df$doy

这是我的长 for loop 来实现这一点:

# create a matrix to store results

 mat <- matrix(, nrow = length(doy.range)*length(unique(year)),ncol=3)
 mat[,1] <- rep(unique(year),each=4)
 mat[,2] <- rep(doy.range,times=2)

for(i in unique(df$year)){

     dat <- df[df$year== i,]

       for(j in doy.range){

         dat1 <- dat[dat$doy >= j,]
         dat1$cum.sum <-cumsum(dat1$value) 
         day.thres <- dat1[dat1$cum.sum >= thres,"doy"][1] # gives me the doy of the year where cumsum of df$value becomes >= thres
        mat[mat[,2] == j & mat[,1] == i,3] <- day.thres
  }
}

cumsum$value 超过thres 时,这个循环给了我矩阵第三列中的doy

但是,我真的很想避免循环。有什么办法可以用更少的代码做到这一点?

【问题讨论】:

    标签: r for-loop apply cumsum split-apply-combine


    【解决方案1】:

    如果我理解正确,您可以使用dplyr。假设阈值为 200:

    library(dplyr)
    df %>% group_by(year) %>% 
      filter(doy >= 40) %>% 
      mutate(CumSum = cumsum(value)) %>% 
      filter(CumSum >= 200) %>% 
      top_n(n = -1, wt = CumSum)
    

    产生

    # A tibble: 2 x 4
    # Groups:   year [2]
        doy  year    value   CumSum
      <dbl> <dbl>    <dbl>    <dbl>
    1    78  2000 3.899895 201.4864
    2    75  2001 9.205178 204.3171
    

    我猜所使用的动词是不言自明的。如果没有,请告诉我。

    为不同的doy创建一个函数并使用lapply

    f <- function(doy.range) {
      df %>% group_by(year) %>% 
        filter(doy >= doy.range) %>% 
        mutate(CumSum = cumsum(value)) %>% 
        filter(CumSum >= 200) %>% 
        top_n(n = -1, wt = CumSum)
    }
    
    lapply(doy.range, f)
    
    [[1]]
    # A tibble: 2 x 4
    # Groups:   year [2]
        doy  year    value   CumSum
      <dbl> <dbl>    <dbl>    <dbl>
    1    78  2000 3.899895 201.4864
    2    75  2001 9.205178 204.3171
    
    [[2]]
    # A tibble: 2 x 4
    # Groups:   year [2]
        doy  year    value   CumSum
      <dbl> <dbl>    <dbl>    <dbl>
    1    89  2000 2.454885 200.2998
    2    91  2001 6.578281 200.6544
    
    [[3]]
    # A tibble: 2 x 4
    # Groups:   year [2]
        doy  year    value   CumSum
      <dbl> <dbl>    <dbl>    <dbl>
    1    98  2000 4.100841 200.5048
    2   102  2001 7.158333 200.3770
    
    [[4]]
    # A tibble: 2 x 4
    # Groups:   year [2]
        doy  year    value   CumSum
      <dbl> <dbl>    <dbl>    <dbl>
    1   120  2000 6.401010 204.9951
    2   120  2001 5.884192 200.8252
    

    【讨论】:

      【解决方案2】:

      这个想法是创建一个基于给定(起始)doy 和阈值的函数,为您提供相关信息。然后将此函数应用于起始doy和阈值的不同组合,并获取包含所有相关信息的数据集:

      # create example data
      doy <- rep(1:365,times=2)
      year <- rep(2000:2001,each=365)
      set.seed(1)
      value <-runif(min=0,max=10,365*2)
      
      df <- data.frame(doy,year,value)
      
      
      library(dplyr)
      library(purrr)
      
      # function (inputs: dr for doy range and t for threshold)
      f = function(dr, t) {
      
        df %>% 
          filter(doy >= dr) %>%                    # keep rows with values aboven a given doy
          group_by(year) %>%                       # for each year
          mutate(CumSumValue = cumsum(value)) %>%  # get the cumulative sum of value
          filter(CumSumValue >= t) %>%             # keep rows equal or above a given threshold
          slice(1) %>%                             # keep the first row
          ungroup() %>%                            # forget the grouping
          select(-value) %>%                       # remove unnecessary variable
          mutate(doy_input=dr, thres_input=t) %>%  # add the input info as columns
          select(doy_input, thres_input, year, doy, CumSumValue)  # re arrange columns 
      
      }
      
      # input doy and threshold
      doy.range <- c(40,50,60,80)
      thres <- 200
      
      # map those vectors to the function
      map2_df(doy.range, thres, f)
      
      # # A tibble: 8 x 5
      #   doy_input thres_input  year   doy CumSumValue
      #       <dbl>       <dbl> <int> <int>       <dbl>
      # 1        40         200  2000    78    201.4864
      # 2        40         200  2001    75    204.3171
      # 3        50         200  2000    89    200.2998
      # 4        50         200  2001    91    200.6544
      # 5        60         200  2000    98    200.5048
      # 6        60         200  2001   102    200.3770
      # 7        80         200  2000   120    204.9951
      # 8        80         200  2001   120    200.8252
      

      【讨论】:

        猜你喜欢
        • 2023-03-27
        • 1970-01-01
        • 2015-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-25
        • 2020-03-10
        • 1970-01-01
        相关资源
        最近更新 更多