将年度时间点与R中之前365天的数据进行匹配



我正在尝试合并两个数据集。调查数据集由不同区域的生物多样性调查组成,每1-5年在某个月进行一次(该月在区域内是恒定的,但在区域之间不是恒定的(。温度数据集由每个调查区域的每日温度读数组成。

对于开始月份和时间范围不同的多个调查,我想将每个调查*年的组合与之前12个月的温度数据配对。换言之,我希望将1983年5月的调查与之前截至1983年4月30日的12个月(或365天——我不在乎是哪一天(的每日温度记录配对。同时,1983年8月在其他地方进行的另一项调查需要与截至1983年7月31日的365天温度数据相结合。

有(至少(两种方法可以做到这一点——一种是将调查数据与(更长的(温度数据结合起来,然后以某种方式细分或确定哪些日期在调查日期之前的12个月内。另一种是从调查数据开始,尝试将温度数据与每一行的矩阵列配对——我尝试使用CCD_ 1和CCD_;滞后;按区域分组时的正确值。

我能够创建一个标识符来连接数据集,这样温度数据中的每个日期都能及时与后续调查相匹配。然而,并非所有这些都在365天内(例如,在下面创建的数据集中,日期1983-06-03与参考年份aleutian_islands-5-1986匹配,因为调查仅每3-5年进行一次(。

以下是我想要的单个区域的一些行为示例(来自下面的示例数据集(,尽管我对实现相同功能但看起来不完全像这样的解决方案持开放态度:

对于这一行,我想要生成的新列中的值(ref_match(应该是NA;日期在CCD_ 6之前365天以上。

region           date        year month month_year ref_year                temperature     
<chr>            <date>     <dbl> <dbl> <chr>      <chr>                         <dbl>
1 aleutian_islands 1982-06-09  1982     6 6-1982     aleutian_islands-5-1983           0   

对于此行,ref_match应为aleutian_islands-5-2014,因为日期在ref_year的12个月内。

region           date        year month month_year ref_year                temperature
<chr>            <date>     <dbl> <dbl> <chr>      <chr>                         <dbl>
1 aleutian_islands 2013-07-22  2013     7 7-2013     aleutian_islands-5-2014       0.998

下面的脚本将生成一个数据集tsibble0,其中的列与上面片段中的列类似,我希望从中生成ref_match列。

# load packages
library(tidyverse)
library(lubridate)
set.seed=10
# make survey dfs
ai_dat <- data.frame("year" = c(1983, 1986, 1991, 1994, 1997), "region" = "aleutian_islands", "startmonth" = 5)
ebs_dat <- data.frame("year" = seq(1983, 1999, 1), "region" = "eastern_bering_sea", "startmonth" = 5)
# join and create what will become ref_year column
surv_dat <- rbind(ai_dat, ebs_dat) %>% 
mutate(month_year = paste0(startmonth,"-",year)) %>%
select(region, month_year) %>%
distinct() %>%
mutate(region_month_year = paste0(region,"-",month_year))
# expand out to all possible month*year combinations for joining with temperature
surv_dat_exploded <- expand.grid(month=seq(1, 12, 1), year=seq(1982, 2000, 1), region=c('aleutian_islands','eastern_bering_sea')) %>% # get a factorial combo of every possible month*year; have to start in 1982 even though we can't use surveys before 1983 because we need to match to temperature data from 1982
mutate(region_month_year = paste0(region,"-",month,"-",year)) %>% # create unique identifier
mutate(ref_year = ifelse(region_month_year %in% surv_dat$region_month_year, region_month_year, NA),
month_year = paste0(month,"-",year)) %>% 
select(region, month_year, ref_year) %>% 
distinct() %>% 
group_by(region) %>% 
fill(ref_year, .direction="up") %>%  # fill in each region with the survey to which env data from each month*year should correspond
ungroup() 
# make temperature dataset and join in survey ref_year column 
temp_dat <- data.frame(expand.grid(date=seq(ymd("1982-01-01"), ymd("1999-12-31"), "days"), region=c('aleutian_islands','eastern_bering_sea'))) %>% 
mutate(temperature = rnorm(nrow(.), 10, 5),  # fill in with fake data
year = year(date),
month = month(date),
month_year = paste0(month,"-",year)) %>% 
left_join(surv_dat_exploded, by=c('region','month_year')) %>% 
filter(!is.na(ref_year))# get rid of dates that are after any ref_year

听起来你想要一个非等联接。这很容易用data.table完成,而且速度非常快。下面是一个稍微修改MWE:的例子

library(data.table)
# make survey dfs
ai_dat = data.table(year = c(1983, 1986, 1991, 1994, 1997), 
region = "aleutian_islands", "startmonth" = 5)
ebs_dat = data.table(year = seq(1983, 1999, 1), 
region = "eastern_bering_sea", "startmonth" = 5)
# bind together and create date (and cutoffdate) vars
surv_dat = rbind(ai_dat, ebs_dat)
surv_dat[, startdate := as.IDate(paste(year, startmonth, '01', sep = '-'))
][, cutoffdate := startdate - 365L]
# make temperature df
temp_dat = CJ(date=seq(as.IDate("1982-01-01"), as.IDate("1999-12-31"), "days"), 
region=c('aleutian_islands','eastern_bering_sea'))
# add temperature var
temp_dat$temp = rnorm(nrow(temp_dat))
# create duplicate date variable (will make post-join processing easier)
temp_dat[, matchdate := date]
# Optional: Set keys for better join performance
setkey(surv_dat, region, startdate)
setkey(temp_dat, region, matchdate)
# Where the magic happens: Non-equi join
surv_dat = temp_dat[surv_dat, on = .(region == region, 
matchdate <= startdate, 
matchdate >= cutoffdate)]
# Optional: get rid of unneeded columns
surv_dat[, c('matchdate', 'matchdate.1') := NULL][]
#>             date             region       temp year startmonth
#>    1: 1982-05-01   aleutian_islands  0.3680810 1983          5
#>    2: 1982-05-02   aleutian_islands  0.8349334 1983          5
#>    3: 1982-05-03   aleutian_islands -1.3622227 1983          5
#>    4: 1982-05-04   aleutian_islands  1.4327587 1983          5
#>    5: 1982-05-05   aleutian_islands  0.5068226 1983          5
#>   ---                                                         
#> 8048: 1999-04-27 eastern_bering_sea -1.2924594 1999          5
#> 8049: 1999-04-28 eastern_bering_sea  0.7519078 1999          5
#> 8050: 1999-04-29 eastern_bering_sea -1.0185174 1999          5
#> 8051: 1999-04-30 eastern_bering_sea -1.4322252 1999          5
#> 8052: 1999-05-01 eastern_bering_sea -1.0412836 1999          5

创建于2021-05-20由reprex包(v2.0.0(

试试这个解决方案。

我基本上使用了您的参考列来生成ref_date,并估计观察和参考之间的天数差异。然后,我使用一个简单的ifelse来测试日期是否在365天的范围内,然后将它们复制到temp_valid列。


# load packages
library(tidyverse)
library(lubridate)
set.seed=10
# make survey dfs
ai_dat <- data.frame("year" = c(1983, 1986, 1991, 1994, 1997), "region" = "aleutian_islands", "startmonth" = 5)
ebs_dat <- data.frame("year" = seq(1983, 1999, 1), "region" = "eastern_bering_sea", "startmonth" = 5)
# join and create what will become ref_year column
surv_dat <-
rbind(ai_dat, ebs_dat) %>% 
mutate(year_month = paste0(year,"-",startmonth),
region_year_month = paste0(region,"-",year,"-",startmonth)) 

# expand out to all possible month*year combinations for joining with temperature
surv_dat_exploded <-
expand.grid(month=seq(01, 12, 1), year=seq(1982, 2000, 1), region=c('aleutian_islands','eastern_bering_sea')) %>% # get a factorial combo of every possible month*year; have to start in 1982 even though we can't use surveys before 1983 because we need to match to temperature data from 1982
mutate(year_month = paste0(year,"-",month)) %>%
mutate(region_year_month = paste0(region,"-",year,"-",month)) %>% 
mutate(ref_year = ifelse(region_year_month %in% surv_dat$region_year_month, region_year_month,NA)) %>%
group_by(region) %>% 
fill(ref_year, .direction="up") %>%  # fill in each region with the survey to which env data from each month*year should correspond
ungroup() 
# make temperature dataset and join in survey ref_year column 
temp_dat <- data.frame(expand.grid(date=seq(ymd("1982-01-01"), ymd("1999-12-31"), "days"), region=c('aleutian_islands','eastern_bering_sea'))) %>% 
mutate(temperature = rnorm(nrow(.), 10, 5),  # fill in with fake data
year = year(date),
month = month(date),
year_month = paste0(year,"-",month))
final_df <- 
left_join(temp_dat, surv_dat_exploded, by=c('region','year_month')) %>% 
#split ref_column in ref_year and ref_region
separate(ref_year, c("ref_region","ref_year"), "-", extra="merge") %>%
#convert ref_year into date
mutate_at("ref_year", as.Date, format= "%Y-%M") %>% 
#round it down to be in the first day of the month (not needed if the day matters)
mutate_at("ref_year", floor_date, "month" ) %>% 
#difference between observed and the reference
mutate(diff_days = date - ref_year) %>% 
# ifelse statement for capturing values of interest
mutate(temp_valid = ifelse(between(diff_days, -365, 0),temperature,NA))

最新更新