-2

I want to calculate the following condition using dplyr

a) When the normal minimum temperature is equal to 10°C or more.

Cold Wave: Departure from normal is -5°C to -6°C.

Severe Cold Wave: Departure from normal is -7°C or less

b) When the normal minimum temperature is less than 10°C.

Cold Wave: Departure from normal is -4°C to -5°C.

Severe Cold Wave: Departure from normal is -6°C or less.

I am using the following code

library(tidyverse)

set.seed(123)
df <- data.frame("Date"= seq(from = as.Date("1970-1-1"), to = as.Date("2000-12-31"), by = "day"),
                 "Station1" = runif(length(seq.Date(as.Date("1970-1-1"), as.Date("2000-12-31"), "days")), 10, 30),
                 "Station2" = runif(length(seq.Date(as.Date("1970-1-1"), as.Date("2000-12-31"), "days")), 11, 29),
                 "Station3" = runif(length(seq.Date(as.Date("1970-1-1"), as.Date("2000-12-31"), "days")), 8, 28))

#Calculation of normal minimum temperature 
df_summarise_all <- df %>% 
  as_tibble() %>% # for easier viewing 
  mutate(day = format(as.Date(df$Date, format='%Y-%m-%d'), format='%m-%d')) %>% 
  group_by(day) %>%
  summarise_all(list(mean)) %>% 
  pivot_longer(cols = -c(Date, day), names_to = "variable", values_to = "value")

#Identification of days fulfilling the condition for cold wave
df_out <- df %>%
  as_tibble() %>% # for easier viewing 
  mutate(day = format(as.Date(df$Date, format='%Y-%m-%d'), format='%m-%d')) %>%
  tidyr::pivot_longer(cols = -c(Date, day), 
                      names_to = "Stations", values_to = "MinT") %>%
  left_join(df_summarise_all %>% rename(mean_MinT = value), 
            by = c('day' = 'day', 'Stations' = 'variable')) %>%
  mutate(is_coldwave = zoo::rollapplyr(MinT < (ifelse(mean_MinT < 10, mean_MinT - 4, mean_MinT - 5)), 
                                       1, all, fill = NA))

I could not implement the condition -5°C to -6°C and -4°C to -5°C. How can this be achieved using dplyr?

UseR10085
  • 7,120
  • 3
  • 24
  • 54
  • 1
    1. What is normal minimum temperature ? 2. It is easier to understand the problem if you provide a small subset of your data and show expected output for it. 3. It is not clear to me why you are using `zoo::rollapplyr` here. – Ronak Shah Oct 12 '21 at 06:31
  • 1. Normal minimum temperature is the daily average from multiple-year daily minimum temperature data. 2. When the condition is fulfilled it should return TRUE otherwise FALSE. 3. I have used it from one of your [previous answer](https://stackoverflow.com/a/61183244/6123824). – UseR10085 Oct 12 '21 at 06:40
  • Can you explain a little more what result you are expecting to get for a subset of the data? When I run the code, `minT` values that are smaller than `mean_MinT` by the required amount are labeled as `TRUE` and all others are `FALSE` which seems to be correct based on the description. – Tjn25 Oct 12 '21 at 06:56
  • @Tjn25 I am not able to implement the condition -5°C to -6°C and -4°C to -5°C. I could able to apply the condition as `mean_MinT - 4, mean_MinT - 5` but what about -6 and -5? – UseR10085 Oct 12 '21 at 07:03
  • My interpretation of the question provided is that departures of -6C or lower are severe, but if they're at least -4 (including -4.0 to -4.999) it's just a cold wave. So the nested ifelse just needs to compare to mean_MinT - 4. – Jon Spring Oct 12 '21 at 08:02
  • There may be two separate columns, one for cold wave another for severe cold wave. – UseR10085 Oct 12 '21 at 08:56

1 Answers1

1

As I understand it, you want a column that checks if the temperature deviates by 5 - 6 degrees (if the temp is greater than 10) and another column to check if it is differs by 7 degrees or more.

The current code you are using seems to identify the coldwave values correctly, although is including the severe_coldwave values as well.

You could add another check for the severe weather, similar to what is already coded, and set any is_coldwave values to FALSE if the weather is severe.

This will set values that deviate from 5-6.5 degrees to is_coldwave and more than 6.5 to is_severe_coldwave

df_out <- df %>%
  as_tibble() %>% # for easier viewing 
  mutate(day = format(as.Date(df$Date, format='%Y-%m-%d'), format='%m-%d')) %>%
  tidyr::pivot_longer(cols = -c(Date, day), 
                      names_to = "Stations", values_to = "MinT") %>%
  left_join(df_summarise_all %>% rename(mean_MinT = value), 
            by = c('day' = 'day', 'Stations' = 'variable')) %>%
  mutate(is_coldwave = MinT < ifelse(mean_MinT < 10, mean_MinT - 4, mean_MinT - 5))%>%
  mutate(is_severe_coldwave = MinT <= ifelse(mean_MinT < 10, mean_MinT - 5.5, mean_MinT - 6.5 ##values differing by 7 or more are is_severe_coldwave
                                              )) %>% 
  mutate(is_coldwave= ifelse(is_severe_coldwave == T, F, is_coldwave))  ##values labeled is_coldwave and is_severe_coldwave updated so that is_coldwave = F
Tjn25
  • 685
  • 5
  • 18
  • According to the definition, I think we should round the departure without decimals and when the departure is -6 for normal minimum temperature >= 10°C, it should be cold wave, not severe cold wave. But the output from your code recognises it as severe cold wave. Can you please see to it? – UseR10085 Oct 12 '21 at 09:41
  • I have updated the code to address that. – Tjn25 Oct 12 '21 at 10:16
  • Still, some problem is there in the output like how the values between 6-7 should be categorised? Your current output records the value as cold wave. Rounding the departure value to zero decimal place is required to satisfy the conditions, I think. – UseR10085 Oct 12 '21 at 10:57
  • @BappaDas I have updated it to round the departure values to zero. If this isn't working for a specific case, could you let me know what the case is (e.g. `minT` and `mean_MinT`) and what you would like it to be? – Tjn25 Oct 14 '21 at 06:41
  • If you save your output in excel and calculate the difference between `MinT` and `mean_MinT` and highlight the values <-6, you can see that in row number 44 the difference value is -7.34 and it should be Severe Cold Wave according to definition. But your output marks it as cold wave. Can you please check it? Similarly, if we round the difference, in many places the value is -7 and it has been marked as cold wave not as Severe Cold Wave. – UseR10085 Oct 14 '21 at 07:47
  • That is because it was changed in response to your previous comment about rounding values. It has been changed back now and I have stated what I expect the results to be in the description and commented the code. – Tjn25 Oct 14 '21 at 09:29
  • Again in the output, after rounding the difference between MinT and mean_MinT, the values of -7 has been classified as cold wave not as Severe Cold Wave (row number 33, 37). If we round the values < -6.5 it will become -7, so it should be marked as Severe Cold Wave and values between 6 to -6.5 should become 6 and it should be marked as cold wave. – UseR10085 Oct 14 '21 at 09:38
  • Sorry, not sure I understood the exact conditions. If you change the `mean_MinT - 7` to `mean_MinT - 6.5` (as I have in the answer) does that work? – Tjn25 Oct 14 '21 at 09:43
  • Yes, now it is working. `mean_MinT - 5.5` should be `mean_MinT - 5` only and you can remove `zoo::rollapplyr` and `, 1, all, fill = NA`. Those parts are not required as commented by @Ronak Shah. – UseR10085 Oct 14 '21 at 09:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/238128/discussion-between-tjn25-and-bappa-das). – Tjn25 Oct 14 '21 at 10:01