4
set.seed(123)
dat <- data.frame(day = 1:365, rain = runif(min = 0, max = 5,365),tmean = runif(min = 15, max = 33, 365) )
dat <- dat %>% mutate(mean.daily.rain = mean(rain),mean.daily.tmean = mean(tmean)) %>% 
mutate(rain.acc = rain - mean.daily.rain,tmean.acc = tmean - mean.daily.tmean)

If I want to find which day of the year the cumsum value of rain.acc or tmean.acc was the minimum I can do this:

dat %>% summarise(which.min(cumsum(rain.acc)))
329

dat %>% summarise(which.min(cumsum(tmean.acc)))
159

However, I want to impose a condition that I only want to look at the doy >= 213 and <= 365 i.e. how do I extract the day of year between 213 and 365 with the lowest value of cumsum(rain.acc) and cumsum(tmean.acc). Note that cumsum has to be calculated over the entire year.

Frank
  • 66,179
  • 8
  • 96
  • 180
89_Simple
  • 3,393
  • 3
  • 39
  • 94

4 Answers4

3

Apply a filter to possible values using ifelse()

fun = function(x, i, min, max)
    which.min(cumsum(x) * ifelse(i >= min & i <= max, 1, NA))

with

> fun(dat$tmean.acc, dat$day, 213, 365)
[1] 248

or

> dat %>% summarize(
    rain.min = fun(rain.acc, day, 213, 365),
    tmean.min = fun(tmean.acc, day, 213, 365)
  )
  rain.min tmean.min
1      329       248

or

> filter(dat, row_number() == fun(tmean.acc, day, 213, 365))
  day     rain    tmean mean.daily.rain mean.daily.tmean rain.acc tmean.acc
1 248 4.846782 15.39589          2.4938         24.03155 2.352982 -8.635665
Martin Morgan
  • 45,935
  • 7
  • 84
  • 112
2

Note: You have to add 212 to get the correct day of the year.

using base R

with(dat, which.min(cumsum(rain - mean(rain))[day>=213 & day<=365]) ) + 212  # 329
with(dat, which.min(cumsum(tmean - mean(tmean))[day>=213 & day<=365]) ) + 212  # 248

using data.table package

library('data.table')
setDT(dat)

# calculate cumsum over the entire year
dat[ , rain.acc := cumsum(rain - mean(rain)) ]
dat[ , tmean.acc := cumsum(tmean - mean(tmean)) ]

# For entire data    
dat[ dat[ , which.min( rain.acc) ], ]
#    day     rain    tmean  rain.acc tmean.acc
# 1: 329 1.691956 17.52186 -5.548483  13.31113
dat[ dat[ , which.min( tmean.acc) ], ]
#    day    rain    tmean  rain.acc tmean.acc
# 1: 159 2.22384 15.67266 0.1829257 -79.17573

# For data within a specified range    
dat[ dat[ day >=213 & day <= 365, which.min( rain.acc) + 213 - 1 ], ]
#    day     rain    tmean  rain.acc tmean.acc
# 1: 329 1.691956 17.52186 -5.548483  13.31113
dat[ dat[ day >=213 & day <= 365, which.min( tmean.acc) + 213 - 1 ], ]
#    day     rain    tmean rain.acc tmean.acc
# 1: 248 4.846782 15.39589 7.623054  -37.2419
Sathish
  • 12,453
  • 3
  • 41
  • 59
1

Can you just subset after taking cumsum but before which.min?

dat %>% summarise(which.min(cumsum(rain.acc)[day>=213&day<=365]))
steveLangsford
  • 646
  • 5
  • 9
  • This is also fine. However, one additional step is the one shown by @satish which is adding 212 to get the actual day of the year. Thank you for your time. – 89_Simple Mar 22 '18 at 11:00
1

One option is to use filter for 1st subset rows and then matching condition with row_number() to find exact row as:

  library(dplyr)

  dat %>%
    filter(day >= 213 & day <= 365) %>%
    filter(row_number() == which.min(cumsum(rain.acc)))
  # day     rain    tmean mean.daily.rain mean.daily.tmean   rain.acc tmean.acc
  # 1 329 1.691956 17.52186          2.4938         24.03155 -0.8018434 -6.509688

  dat %>%
    filter(day >= 213 & day <= 365) %>%
    filter(row_number() == which.min(cumsum(tmean.acc)))
  # day     rain    tmean mean.daily.rain mean.daily.tmean rain.acc tmean.acc
  # 1 248 4.846782 15.39589          2.4938         24.03155 2.352982 -8.635665
MKR
  • 19,739
  • 4
  • 23
  • 33
  • 1
    This is also fine. However, I accepted Martin's answer because it allows me to achieve in a single dataframe. Thank you for your time – 89_Simple Mar 22 '18 at 11:03
  • 1
    @Crop89 I'm happy you got desired helped. I think you can achieve both values in single dataframe command per my answer too. I had kept it simpler to start with. I'll update my answer later. – MKR Mar 22 '18 at 11:31