-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?

Bappa Das
  • 5,817
  • 3
  • 18
  • 40
  • 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). – Bappa Das 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? – Bappa Das 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. – Bappa Das 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
  • 17
  • 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? – Bappa Das 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. – Bappa Das 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 – Bappa Das 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. – Bappa Das 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. – Bappa Das 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