根据过去 48 小时内发生的情况更改值?

Changing values based on a condition happening in the past 48 hours?

我有一个数据集,我需要将过去 48 小时内为 0 但具有非零值的任何值更改为特定字符串。我猜我可能需要在执行此操作之前将第一列以外的所有列从 dbl 转换为 chr?

   Time                 colA             colB             colC  colD
   <dttm>              <dbl>            <dbl>            <dbl> <dbl>
 1 2021-11-21 10:00:00     8                0                9   176
 2 2021-11-11 11:00:00    21                0               22   416
 3 2021-11-21 11:00:00    19                0               20   373
 4 2021-11-11 12:00:00    40               13               28   566
 5 2021-11-21 12:00:00    26                0               27   527
 6 2021-11-11 13:00:00    50               20               32   651
 7 2021-11-11 10:00:00    11                0               12   216
 8 2021-11-21 13:00:00    30                0               31   617
 9 2021-11-11 14:00:00    51                0               32   675
10 2021-11-21 14:00:00    31                0               32   644

抱歉,数据尚未按时间排序,正在努力解决这个问题。 例如,这是我 喜欢 得到的输出:

   Time                 colA             colB             colC  colD
   <dttm>              <dbl>            <dbl>            <dbl> <dbl>
 1 2021-11-21 10:00:00     8                0                9   176
 2 2021-11-11 11:00:00    21                0               22   416
 3 2021-11-21 11:00:00    19                0               20   373
 4 2021-11-11 12:00:00    40               13               28   566
 5 2021-11-21 12:00:00    26                0               27   527
 6 2021-11-11 13:00:00    50               20               32   651
 7 2021-11-11 10:00:00    11                0               12   216
 8 2021-11-21 13:00:00    30                0               31   617
 9 2021-11-11 14:00:00    51          STRING1               32   675
10 2021-11-21 14:00:00    31                0               32   644

由于 colB 在 2021 年 11 月 11 日的值为 0 14:00:00,但在此之前的 48 小时内至少有 1 个先前值!= 0,因此它更改为“STRING1”

抱歉,如果这令人困惑,我正在尝试将我通常在 Excel 中手动执行的操作自动化。提前致谢

这是一个 tidyverse 解决方案。我将首先创建一些示例数据(请注意,我确实将其他列设置为字符):

data = tribble(
  ~ time, ~ colA, ~ colB,
  "2021-11-21 12:00:00", 1, 0,
  "2021-11-22 00:00:00", 0, 1,
  "2021-11-24 12:00:00", 0, 0,
  "2021-11-25 12:00:00", 1, 1,
  "2021-11-26 12:00:00", 0, 0,
) %>%
  mutate(
    time = ymd_hms(time), 
    across(-time, as.character)
  )

# A tibble: 5 x 3
  time                colA  colB 
  <dttm>              <chr> <chr>
1 2021-11-21 12:00:00 1     0    
2 2021-11-22 00:00:00 0     1    
3 2021-11-24 12:00:00 0     0    
4 2021-11-25 12:00:00 1     1    
5 2021-11-26 12:00:00 0     0    

这个问题的挑战在于,每次我们都需要知道要查找哪些其他行以确定每列的新值。为此,我将使用 purrr:pmap()dplyr 中可用的 .data 对象。我将首先演示如何“回顾”过去 48 小时内某个时间的行:

data %>%
  mutate(
    across(
      .cols = -time, 
      function(col) {
        pmap_chr(list(time), function(t) {
          eligible = .data$time >= t - hours(48) & .data$time < t
          paste(col[eligible], collapse = ",")
        })
      },
      .names = "{.col}_previous"
    )
  )

输出:

# A tibble: 5 x 5
  time                colA  colB  colA_previous colB_previous
  <dttm>              <chr> <chr> <chr>         <chr>        
1 2021-11-21 12:00:00 1     0     ""            ""           
2 2021-11-22 00:00:00 0     1     "1"           "0"          
3 2021-11-24 12:00:00 0     0     ""            ""           
4 2021-11-25 12:00:00 1     1     "0"           "0"          
5 2021-11-26 12:00:00 0     0     "0,1"         "0,1"    

如您所见,这看起来很有希望。我们准备以此为核心思想开发解决方案。

data %>%
  mutate(
    across(
      .cols = -time, 
      function(col) {
        modify_ind = pmap_lgl(list(time), function(t) {
          eligible = .data$time >= t - hours(48) & .data$time < t
          any(col[eligible] != "0")
        })
        ifelse(modify_ind & col == "0", "STRING1", col)
      }
    )
  )

输出:

# A tibble: 5 x 3
  time                colA    colB   
  <dttm>              <chr>   <chr>  
1 2021-11-21 12:00:00 1       0      
2 2021-11-22 00:00:00 STRING1 1      
3 2021-11-24 12:00:00 0       0      
4 2021-11-25 12:00:00 1       1      
5 2021-11-26 12:00:00 STRING1 STRING1

一些注意事项:

  • 在我的解决方案中,我创建了一个 modify_ind,它建立在前面显示的想法之上:访问在过去 48 小时内按时间过滤的列,然后检查是否有任何非零值。然后,每当其 modify_indTRUE 并且该列的值为 "0".
  • 时,我都会修改该列
  • 如果您想创建新列而不是完全替换原始列,请将 .names 参数添加到 across(),如前所示。
  • 如果您只想修改数据集中的几列,而不是使用 across(.cols = -time),请使用类似 across(.cols = c("colA", "colB")).
  • 的内容