循环遍历两个数据框并在循环内添加列

时间:2017-10-04 16:33:03

标签: r loops recursion dataframe

指定带数据框的循环时出现问题。 我的一般想法如下: 我有一个区域包含一定数量的光栅象限。几年来(例如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都已在第二个采样周期内被访问过。

我被困......任何想法?

1 个答案:

答案 0 :(得分:3)

您可以使用dplyrtidyr包在没有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

被授予,这可能不是最优雅的解决方案,但它确实有效。希望这能为您解决问题