指定带数据框的循环时出现问题。 我的一般想法如下: 我有一个区域包含一定数量的光栅象限。几年来(例如1950年至2015年),这些光栅象限被不经常地访问过。
我有两个数据框: 1)包含rasterquadrants的ID的数据框(以及该象限首次访问年份的一列):
df1<- as.data.frame(cbind(c("12345","12346","12347","12348"),rep(NA,4)))
df1[,1]<- as.character(df1[,1])
df1[,2]<- as.numeric(df1[,2])
names(df1)<-c("Raster_Q","First_visit")
2)包含访问信息的数据框;这个是由第一个rasterquadrants订购,然后是第二年。该数据框具有访问rasterquadrant时的信息以及何时。
df2<- as.data.frame(cbind(c(rep("12345",5),rep("12346",7),rep("12347",3),rep(12348,9)),
c(1950,1952,1955,1967,1951,1968,1970,
1998,2001,2014,2015,2017,1965,1986,2000,1952,1955,1957,1965,2003,2014,2015,2016,2017)))
df2[,1]<- as.character(df2[,1])
df2[,2]<- as.numeric(as.character(df2[,2]))
names(df2)<-c("Raster_Q","Year")
我想知道整个区域的采样时间和频率&#39;。
Scheme of what I want to do; different colors indicate different areas/regions
我的理由: 我根据Quadrant和Year对df2中的完整数据进行了排序。然后我将df1中的rasterquadrant与df2中rasterquadrant的名称进行匹配,并添加df2中第一个年份值。
为此,我写了一个循环(见下文)
为了不复制象限,我创建了一个矢量&#34;访问了&#34;
visited<-c()
与df1匹配的df2的每个条目都将写入此向量,以便例如第二个条目rasterquadrant&#34; 12345&#34;在循环中忽略在df2中。
这是循环:
visited<- c()
for (i in 1:nrow(df2)){
index<- which(df1$"Raster_Q"==df2$"Raster_Q"[i])
if(length(index)==0) {next()} else{
if(df1$"Raster_Q"[index] %in% visited){next()} else{
df1$"First_visit"[index]<- df2$"Year"[i]
visited[index]<- df1$"Raster_Q"[index]
}
}
}
这给了我第一个完整的采样周期。
Raster_Q First_visit
1 12345 1950
2 12346 1968
3 12347 1965
4 12348 1952
但是,我想拥有所有完整的采样周期。
所以我这样做:
df1$"Second_visit"<-NA
我重置了访问过的向量并指定了以下循环:
visited <- c()
for (i in 1:nrow(df2)){
if(df2$Year[i]<=max(df1$"First_visit")){next()} else{
index<- which(df1$"Raster_Q"==df2$"Raster_Q"[i])
if(length(index)==0) {next()} else{
if(df1$"Raster_Q"[index] %in% visited){next()} else{
df1$"Second_visit"[index]<- df2$"Year"[i]
visited[index]<- df1$"Raster_Q"[index]
}
}
}
}
这与以前的循环基本相同,但是,只有确保,如果df2 $&#34;年&#34;在第一次访问中已经包含某个栅格象限,然后跳过它。
这给了我第二个完整的采样周期:
Raster_Q First_visit Second_visit
1 12345 1950 NA
2 12346 1968 1970
3 12347 1965 1986
4 12348 1952 2003
好的,到目前为止一切顺利。我可以手工完成这一切。但是我有大量的rasterquadrants和几个可以并且应该以这种方式进行筛选的区域。 因此,在一个循环中完成所有这一切将非常棒!但是,我意识到这会产生问题,因为循环会递归:
添加的列不会包含在循环的后续迭代中,因为不会为每个循环重新读取df1本身,因此,新采样周期的新库将不包含在以下内容中迭代:
visited<- c()
for (i in 1:nrow(df2)){
m<-ncol(df1)
index<- which(df1$"Raster_Q"==df2$"Raster_Q"[i])
if(length(index)==0) {next()} else{
if(df1$"Raster_Q"[index] %in% visited){next()} else{
df1[index,m]<- df2$"Year"[i]
visited[index]<- df1$"Raster_Q"[index]
#finish "first_visit"
df1[,m+1]<-NA
# add column for "second visit"
if(df2$Year[i]<=max(df1$"First_visit")){next()} else{
# make sure that the first visit year are not included
index<- which(df1$"Raster_Q"==df2$"Raster_Q"[i])
if(length(index)==0) {next()} else{
if(df1$"Raster_Q"[index] %in% visited){next()} else{
df1[index,m+1]<- df2$"Year"[i]
visited[index]<- df1$"Raster_Q"[index]
}
}
}
这不会奏效。另一个问题是,在此循环期间,矢量visited()没有被清空,因此基本上每个Raster_Q都已在第二个采样周期内被访问过。
我被困......任何想法?
答案 0 :(得分:3)
您可以使用dplyr
和tidyr
包在没有for循环的情况下执行此操作。首先,您使用df2
并使用dplyr::arrange
按栅格和年份排序。然后,您可以使用rank
函数内的dplyr::mutate
函数对访问过的年份进行排名。然后使用tidyr::spread
,您可以将它们全部放在自己的列中。这是代码:
df <- df2 %>%
arrange(Raster_Q, Year) %>%
group_by(Raster_Q) %>%
mutate(visit = rank(Year),
visit = paste0("visit_", as.character(visit))) %>%
tidyr::spread(key = visit, value = Year)
这是输出:
> df
# A tibble: 4 x 10
# Groups: Raster_Q [4]
Raster_Q visit_1 visit_2 visit_3 visit_4 visit_5 visit_6 visit_7 visit_8 visit_9
* <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 12345 1950 1951 1952 1955 1967 NA NA NA NA
2 12346 1968 1970 1998 2001 2014 2015 2017 NA NA
3 12347 1965 1986 2000 NA NA NA NA NA NA
4 12348 1952 1955 1957 1965 2003 2014 2015 2016 2017
编辑:所以我认为我现在对你的问题了解得更好。您希望删除对每个象限的所有重复访问,这些访问发生在每个相应的&#34; round&#34;的最大年份之前。访问。因此,为了实现这一点,我编写了一个简短的函数,它实质上是执行上面的代码所做的,但稍有改动。这是功能:
filter_by_round <- function(data, round) {
output <- data %>%
arrange(Raster_Q, Year) %>%
group_by(Raster_Q) %>%
mutate(visit = rank(Year, ties.method = "first")) %>%
ungroup() %>%
mutate(in_round = ifelse(Year <= max(.$Year[.$visit == round]) & visit > round,
TRUE, FALSE)) %>%
filter(!in_round) %>%
select(-c(in_round, visit))
return(output)
}
这个功能的作用是查看数据,如果给定的年份小于指定的&#34;访问轮次&#34;然后它被删除。要仅将其应用于第一轮,您可以这样做:
df2 %>%
filter_by_round(1) %>%
group_by(Raster_Q) %>%
mutate(visit = rank(Year, ties.method = "first")) %>%
ungroup() %>%
mutate(visit = paste0("visit_", as.character(visit))) %>%
tidyr::spread(key = visit, value = Year)
会给你这个:
# A tibble: 4 x 8
Raster_Q visit_1 visit_2 visit_3 visit_4 visit_5 visit_6 visit_7
* <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 12345 1950 NA NA NA NA NA NA
2 12346 1968 1970 1998 2001 2014 2015 2017
3 12347 1965 1986 2000 NA NA NA NA
4 12348 1952 2003 2014 2015 2016 2017 NA
然而,虽然它确实完成了你的for循环所具有的功能,但现在你遇到了同样问题。我已经想出了一种方法来成功地做到这一点,但它需要你知道有多少&#34;访问轮次&#34;你有或有一些反复试验。要实现此目的,您可以使用map并将更改分配给全局变量。
# I do this so we do not lose the original dataset
df <- df2
# I chose 1:5 after some trial and error showed there are 5 unique
# "visit rounds" in your toy dataset
# However, if you overshoot your number, it should still work,
# you will just get warnings about `max` not working correctly
# however, this may casue issues, so figuring out your exact number is
# recommended
purrr::map(1:5, function(x){
# this assigns the output of each iteration to the global variable df
df <<- df %>%
filter_by_round(x)
})
# now applying the original transformation to get the spread dataset
df %>%
group_by(Raster_Q) %>%
mutate(visit = rank(Year, ties.method = "first")) %>%
ungroup() %>%
mutate(visit = paste0("visit_", as.character(visit))) %>%
tidyr::spread(key = visit, value = Year)
这将为您提供以下输出:
# A tibble: 4 x 6
Raster_Q visit_1 visit_2 visit_3 visit_4 visit_5
* <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 12345 1950 NA NA NA NA
2 12346 1968 1970 2014 2015 2017
3 12347 1965 1986 NA NA NA
4 12348 1952 2003 2014 2015 2016
被授予,这可能不是最优雅的解决方案,但它确实有效。希望这能为您解决问题