【问题标题】:Applying yearwise segmented regression in R在 R 中应用逐年分段回归
【发布时间】:2020-06-25 11:28:46
【问题描述】:

我有每日降雨量数据,我已使用以下代码将其转换为年度累积值

library(seas)
library(data.table)
library(ggplot2)

#Loading data
data(mscdata)
dat <- (mksub(mscdata, id=1108447))
dat$julian.date <- as.numeric(format(dat$date, "%j"))
DT <- data.table(dat)
DT[, Cum.Sum := cumsum(rain), by=list(year)]

df <- cbind.data.frame(day=dat$julian.date,cumulative=DT$Cum.Sum)

然后我想逐年应用分段回归以获得逐年断点。我可以像一年一样做到这一点

library("segmented")
x <- subset(dat,year=="1984")$julian.date
y <- subset(DT,year=="1984")$Cum.Sum
fit.lm<-lm(y~x)
segmented(fit.lm, seg.Z = ~ x, npsi=3)

我使用npsi = 3 有3 个断点。现在如何最小化地应用它逐年分段回归并获得估计的断点?

【问题讨论】:

    标签: r regression tidyverse


    【解决方案1】:

    这是一个带有自定义函数的简短脚本,以便您可以运行不同的年度回归。

    ## using tidyverse processes instead of mixing and matching with other data manipulation packages 
    library(tidyverse); library(segmented); library(seas)
    
    ## get mscdata from "seas" packages
    data(mscdata)
    dat <- (mksub(mscdata, id=1108447))
    
    ## generate cumulative sum of rain by year
    d2 <- dat %>% group_by(year) %>% mutate(rain_cs = cumsum(rain)) %>% ungroup
    
    ## write a custom function
    
    segmentedlm <- function(data, year){
      subset.df <- data %>% filter(year == year)
      fit.lm <- lm(rain_cs ~ julian.date, subset.df)
      segmented(fit.lm, seg.Z = ~ julian.date, npsi=3)
    }
    
    # run the customised function for 1975 data
    segmentedlm(d2, "1975") %>% plot(., main="1975")
    

    segmentedlm(d2, "1984") %>% plot(., main = "1984")
    

    将多年的分段线性模型的摘要输出到文本文件中:

    sink("output.txt")
    lapply(c("1975", "1984"), function(x) segmentedlm(d2, x))
    sink()
    

    您可以将 lapply 的参数更改为输入所有年份。

    【讨论】:

    • 绘图没问题,但是否可以一次运行所有年份并在 .csv 文件中设置断点。
    • 你想在 csv 文件上输出什么样的输出?
    • 如果你只运行 segmentedlm(d2, "1975") 这个,那么我想写这个 psi1.julian.date psi2.julian.date psi3.julian.date 81.5 152.0 285.7 一个在另一个下面的所有年份,行名是年份。
    • 修改了上面的答案,以便您可以将控制台输出写入文本文件
    • 另一种方法是使用broom::tidy 将每个分段线性模型的控制台输出到一个表中。但是将它们全部组合到一个 csv 中会非常令人困惑。
    【解决方案2】:

    您可以将lm 对象存储在一个列表中,并为每个year 应用segmented

    library(tidyverse)
    
    data <- DT %>%
             group_by(year) %>%
             summarise(fit.lm = list(lm(Cum.Sum~julian.date)), 
                       julian.date1 = list(julian.date)) %>%
             mutate(out = map2(fit.lm, julian.date1, function(x, julian.date) 
                           data.frame(segmented::segmented(x, 
                                      seg.Z = ~julian.date, npsi=3)$psi))) %>%
             unnest_wider(out) %>%
             unnest(cols = c(Initial, Est., St.Err)) %>%
             dplyr::select(-fit.lm, -julian.date1)
    
    # A tibble: 90 x 4
    #    year Initial  Est. St.Err
    #   <int>   <dbl> <dbl>  <dbl>
    # 1  1975    84.8  68.3  1.44 
    # 2  1975   168.  167.   9.31 
    # 3  1975   282.  281.   0.917
    # 4  1976    84.8  68.3  1.44 
    # 5  1976   168.  167.   9.33 
    # 6  1976   282.  281.   0.913
    # 7  1977    84.8  68.3  1.44 
    # 8  1977   168.  167.   9.32 
    # 9  1977   282.  281.   0.913
    #10  1978    84.8  68.3  1.44 
    # … with 80 more rows
    

    【讨论】:

    • 如何让列表输出到数据框?只是我想将断点(psi)作为输出,并将年份作为行名。你可以使用segmented(x, seg.Z = ~julian.date, npsi=3)$psi
    • segmented(fit.lm, seg.Z = ~ x, npsi=3)$psi 返回一个 3 X 3 矩阵,因此将有 9 个值。那么你想要有 9 列吗?
    • 第一列是初始断点,第二列是断点的最终估计值,第三列是标准误。如果我们可以有第二列,它将达到目的。输出中的所有内容都将是最好的,具有各自的名称。
    • @BappaDas 但每年都会有。 3 行对吗?
    • 是的……我明白了。我不确定它是如何工作的。如果您将输出直到summarise 并将其存储在data 中,我们可以看到有不同的模型data$fit.lm[[1]]data$fit.lm[[2]],但是如果您将它们放在segmented 中,它们会生成相同的输出。 segmented::segmented(data$fit.lm[[1]], npsi=3)$psi , segmented::segmented(data$fit.lm[[2]], npsi=3)$psi
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-21
    • 2020-08-03
    • 1970-01-01
    相关资源
    最近更新 更多