【问题标题】:Converting for loop to function and applying to list of dataframes将 for 循环转换为函数并应用于数据帧列表
【发布时间】:2022-01-06 18:49:31
【问题描述】:

我之前创建了一个 for 循环来识别水位下降的时间段。这适用于较小的连续时间序列数据。

library(tidyverse)
library(lubridate)

level_data <- c(10:4, 20:9, 16:5, rep(0, 3))           
times_stamp <- seq(ymd_hms('2015-07-22 12:15:00'), ymd_hms('2015-07-22 20:30:00'), by = '15 mins')
precip_data <- c(rep(0, 10), 1:4, rep(0, 10), 1:5, rep(0, 5))

maxP.neg <- .1

# Create objects for holding the start and end dates. These lists should end up 
# the same length, so that each start date has a corresponding end date.

startDates <- c()                                     
endDates <- c()

recede <- 0                                           # This is a switch to keep track of whether a recession period is in progress

for (i in 2:length(level_data)) {                     # i.e. start at the second data point
 diffQ <- level_data[i] - level_data[i - 1]          # Calculate difference between current and previous timestamp
 
 if (diffQ < 0 &&                                    # If difference is negative (i.e. receding) AND
     recede == 0 &&                                  # a recession period has not already begun (recede == 0) AND
     precip_data <= maxP.neg) {                      # min. dry period criteria is met ...
   startDates <- append(startDates, times_stamp[i])  # Record the start time of the recession period
   recede <- 1                                       # Change recede to 1 to indicate a recession period has begun
   
 } else if (diffQ >= 0 &&                            # If the difference becomes positive 
            recede == 1) {                           # and a recession period was in progress...
   endDates <- append(endDates, times_stamp[i - 1])  # Record the previous timestamp as the end date of the recession     
   recede <- 0                                       # Set recede back to 0 to show the recession period has ended                             
   
 } else {                                            # Otherwise just continue to the next data point.
   next                                     
 }
}

但是,我想对有时间间隔的较大数据集执行相同的分析。我想将数据拆分为数据框列表并使用lapply 和自定义函数。

这是我想出的,但我没有得到与 for 循环方法相同的输出?

diff_Q <- diff(level_data)

date_time1 <-
  seq(ymd_hms('2015-07-22 12:15:00'),
      ymd_hms('2015-07-22 20:15:00'),
      by = '15 mins')
date_time2 <-
  seq(ymd_hms('2015-07-25 08:00:00'),
      ymd_hms('2015-07-25 16:00:00'),
      by = '15 mins')

cum_precip <- c(rep(0, 10), 1:4, rep(0, 10), 1:5, rep(0, 4))

df1 <-
  data.frame(date_time1, diff_Q, cum_precip) %>% rename(date_time = date_time1)
df2 <-
  data.frame(date_time2, diff_Q, cum_precip) %>%  rename(date_time = date_time2)

recede_ls <- list(df1, df2)

startDates2 <- c()
endDates2 <- c()

RA.function <- function(x) {
  recede <- 0
  
  if (diff_Q < 0 &&
      recede == 0 &&
      cum_precip <= maxP.neg) {
    startDates2 <- x$date_time
    recede <- 1
  } else if (diffQ >= 0 &&
             recede == 1) {
    endDates2 <- x$date_time[-1]
    recede <-
      0
    
  } else {
    next
  }
}

lapply(recede_ls, RA.function)

感谢您的帮助!

【问题讨论】:

    标签: r for-loop if-statement time-series lapply


    【解决方案1】:
    library(tidyverse)
    library(lubridate)
    
    level_data <- c(10:4, 20:9, 16:5, rep(0, 3))           
    times_stamp <- seq(ymd_hms('2015-07-22 12:15:00'), ymd_hms('2015-07-22 20:30:00'), by = '15 mins')
    precip_data <- c(rep(0, 10), 1:4, rep(0, 10), 1:5, rep(0, 5))
    
    df <- data.frame(date_time = times_stamp,
                     level_data = level_data, 
                     cum_precip = precip_data)
    
    # Precalculate `diffQ`
    df$diffQ <- NA
    for(i in 2:nrow(df)){
      df[i,"diffQ"] <- df[i,"level_data"] - df[i-1,"level_data"]
    }
    
    df
    #>              date_time level_data cum_precip diffQ
    #> 1  2015-07-22 12:15:00         10          0    NA
    #> 2  2015-07-22 12:30:00          9          0    -1
    #> 3  2015-07-22 12:45:00          8          0    -1
    #> 4  2015-07-22 13:00:00          7          0    -1
    #> 5  2015-07-22 13:15:00          6          0    -1
    #> 6  2015-07-22 13:30:00          5          0    -1
    #> 7  2015-07-22 13:45:00          4          0    -1
    #> 8  2015-07-22 14:00:00         20          0    16
    #> 9  2015-07-22 14:15:00         19          0    -1
    #> 10 2015-07-22 14:30:00         18          0    -1
    #> 11 2015-07-22 14:45:00         17          1    -1
    #> 12 2015-07-22 15:00:00         16          2    -1
    #> 13 2015-07-22 15:15:00         15          3    -1
    #> 14 2015-07-22 15:30:00         14          4    -1
    #> 15 2015-07-22 15:45:00         13          0    -1
    #> 16 2015-07-22 16:00:00         12          0    -1
    #> 17 2015-07-22 16:15:00         11          0    -1
    #> 18 2015-07-22 16:30:00         10          0    -1
    #> 19 2015-07-22 16:45:00          9          0    -1
    #> 20 2015-07-22 17:00:00         16          0     7
    #> 21 2015-07-22 17:15:00         15          0    -1
    #> 22 2015-07-22 17:30:00         14          0    -1
    #> 23 2015-07-22 17:45:00         13          0    -1
    #> 24 2015-07-22 18:00:00         12          0    -1
    #> 25 2015-07-22 18:15:00         11          1    -1
    #> 26 2015-07-22 18:30:00         10          2    -1
    #> 27 2015-07-22 18:45:00          9          3    -1
    #> 28 2015-07-22 19:00:00          8          4    -1
    #> 29 2015-07-22 19:15:00          7          5    -1
    #> 30 2015-07-22 19:30:00          6          0    -1
    #> 31 2015-07-22 19:45:00          5          0    -1
    #> 32 2015-07-22 20:00:00          0          0    -5
    #> 33 2015-07-22 20:15:00          0          0     0
    #> 34 2015-07-22 20:30:00          0          0     0
    
    dfs <- list(df, df)
    
    RA.function <- function(x) {
      
      startDates2 <- c()
      endDates2 <- c()
      recede <- 0
      maxP.neg <- 0.1
      
      for(i in 2:nrow(x)){
        
        if (x[i,"diffQ"] < 0 && recede == 0 && x[i,"cum_precip"] <= maxP.neg) {
          startDates2 <- c(startDates2, paste(x[i, "date_time"]))
          recede <- 1
        } else if (x[i,"diffQ"] >= 0 && recede == 1) {
          endDates2 <- c(endDates2, paste(x[i-1,"date_time"]))
          recede <- 0
        }
      }
      return(cbind(startDates2, endDates2))
    }
    
    # Apply your function to a list of dataframes
    results <- lapply(dfs, RA.function)
    
    # Results that correspond with the first dataframe in the input list (`dfs[[1]]`)
    results[[1]]
    #>      startDates2           endDates2            
    #> [1,] "2015-07-22 12:30:00" "2015-07-22 13:45:00"
    #> [2,] "2015-07-22 14:15:00" "2015-07-22 16:45:00"
    #> [3,] "2015-07-22 17:15:00" "2015-07-22 20:00:00"
    
    # Results that correspond with the second dataframe in the input list (`dfs[[2]]`)
    results[[2]]
    #>      startDates2           endDates2            
    #> [1,] "2015-07-22 12:30:00" "2015-07-22 13:45:00"
    #> [2,] "2015-07-22 14:15:00" "2015-07-22 16:45:00"
    #> [3,] "2015-07-22 17:15:00" "2015-07-22 20:00:00"
    Created on 2022-01-07 by the reprex package (v2.0.1)
    

    【讨论】:

    • 不客气!对于错误:确保变量名正确。我怀疑您需要使用 diff_QdiffQ (两者都出现在您的问题中;选择一个名称并始终更新)。如果您仍有问题,请发布数据框。要访问结果数据:知道 lapply 返回数据帧列表。 results &lt;- lapply(dfs, RA.function) 其中results[[1]] 是第一个数据帧,results[[2]] 是第二个数据帧。
    • 感谢您的帮助@Skaqqs。我有一个长度不同且已包含所需数据的 DF 列表 (align = right)。 DF 列是date_timediffQcum_precip。我已尝试更改您的方法,因为我不再需要计算 diffQ,因为它已经在 DF 中。这是我使用的语法:for(i in 1:nrow(df)){ if (x[i,"diffQ"] &lt; 0 &amp;&amp; recede == 0 &amp;&amp; x[i,"cum_precip"] &lt;= maxP.neg) { 但我得到error missing value where TRUE/FALSE needed 另外如何访问 startDates2 和 endDates2 以进行进一步分析?谢谢
    • 嗨@mrob27,你的代码看起来不错,但也许你忘了更新if 语句?我更新了我的答案以适应预先计算的 diffQ 值,并向您展示如何获取与每个输入数据帧相对应的 startdatesenddates。如果有任何不清楚的地方,请告诉我。
    • 嗨@Skaqqs,我认为问题是'for(i in 1:nrow(df))',它现在似乎正在工作。有一条警告消息In cbind(startDates2, endDates2) : number of rows of result is not a multiple of vector length (arg 2)。这表明startdatesenddates 的长度不同?我希望每个startdate 都有一个对应的enddate。再次感谢!
    • 很好地抓住了那里的错字。是的,我认为您对新错误的看法是正确的。如果不查看您现在正在使用的输入数据,就很难判断;它适用于您问题中的输入数据。可以分享更多数据吗?
    猜你喜欢
    • 1970-01-01
    • 2017-07-21
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 2018-10-29
    相关资源
    最近更新 更多