【问题标题】:R: using apply family instead of for-loopsR:使用应用系列而不是 for 循环
【发布时间】:2018-01-10 01:52:05
【问题描述】:

先放一些样本数据

yr1 <- sample(0:1, 365, replace = T)
yr2 <- sample(0:1, 365, replace = T)
yr3 <- sample(0:1, 365, replace = T)
yr4 <- sample(0:1, 365, replace = T)

value <- c(yr1, yr2, yr3, yr4)

yr <- rep(2000:2003, each = 365)
doy <- rep(1:365, times = 4)

foo <- as.data.frame(cbind(value, yr, doy))

foo 包含 3 列。第 1 列具有任意值,即 1 或 0。第 2 列包含年份,第 3 列包含一年中的某一天(365 天)

我有两个向量,分别是儒略日的开始日和结束日

start <- c(258, 258,258,258)
mid <- c(279, 281,285,288)
end <- c(286, 295,300,320)

range.val <- as.data.frame(cbind(start, mid, end))
range.val$yr<- c(2000, 2001, 2002, 2003)

range.val 给了我朱利安天数,在这些天数之间我必须对 foo 中每年的值求和。

例如,对于 2000 年,我需要从 258 天到 279 天,然后从 279 到 286 对 foo$value 求和。同样​​, 对于 2001 年,总和 foo$value 从 258 到 281,然后从 281 到 295。

我还需要计算每年这些指数之间最长连续出现 1 的长度。

我这样做了:

for(yr in 2000:2003){

    range.sub <- range.val[range.val$yr == yr,]
    foo.sub <- foo[foo$yr == yr,]

    sum.1 <- sum(foo.sub[range.sub$start:range.sub$mid,"value"])
    sum.2 <- sum(foo.sub[range.sub$mid:range.sub$end,"value"])

    length.1 <- rle(foo.sub[range.sub$start:range.sub$mid,"value"]) 
    max.spell.length <-  max(sort(length.1$lengths, , decreasing = TRUE))

    length.1 <- rle(foo.sub[range.sub$mid:range.sub$start,"value"]) 
    max.spell.length1 <-  max(sort(length.1$lengths, , decreasing = TRUE))
}

在我不断努力尽量减少使用 for 循环的过程中,我想知道是否可以使用其他一些函数来缩短上述代码。

【问题讨论】:

    标签: r for-loop apply


    【解决方案1】:

    这是使用 dplyr 的解决方案。

    创建一个联合数据框并指明每个年份组合是否在范围 1(开始到中间)、范围 2(中间到结束)或两者都没有。

    library(dplyr)
    
    df <- left_join(foo, range.val, by = "yr")
    df <- df %>%
      mutate(in.range1 = doy >= start & doy <= mid,
             in.range2 = doy >= mid & doy <= end)
    # Note: I'm not sure if the ranges are supposed to be inclusive on both ends, but you
    # should be able to change that easily
    

    对于每年范围 X 中的总值,过滤范围并按年份汇总:

    df.sum.1 <- df %>%
      filter(in.range1) %>% #change to in.range2 for mid-end
      group_by(yr) %>%
      summarise(value = sum(value))
    
    > df.sum.1
    # A tibble: 4 x 2
         yr value
      <dbl> <int>
    1  2000    12
    2  2001    12
    3  2002    10
    4  2003    10
    

    对于最长的 1,过滤范围并在每年的值上执行 rle。请注意,我们应该首先过滤 value == 1,否则如果有更长的 0,您可能会得到它:

    df.spell.length1 <- df %>%
      filter(in.range1) %>% #change to in.range2 for mid-end
      group_by(yr) %>%
      arrange(doy) %>%
      do(data.frame(unclass(rle(.$value)))) %>%
      filter(values == 1) %>%
      filter(lengths == max(lengths)) %>%
      unique()
    
    > df.spell.length1
    # A tibble: 4 x 3
    # Groups: yr [4]
         yr lengths values
      <dbl>   <int>  <int>
    1  2000       7      1
    2  2001       3      1
    3  2002       3      1
    4  2003       3      1
    

    (为了重现性,样本数据是用set.seed(123)生成的。)

    【讨论】:

    • 谢谢。就像评论一样,先这样做detach(package:plyr),否则它会给你一些有趣的结果
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 2011-10-06
    相关资源
    最近更新 更多