[英]Is it possible to include the product of two smooth terms in a mgcv gam model
我使用 gam 對時間序列數據的季節性建模取得了巨大成功。 除了季節性變化之外,我的最新模型清楚地顯示了每周模式。 雖然每周模式本身在一年中非常穩定,但其幅度也隨季節變化。 所以理想情況下,我想將我的數據建模為:
y ~ f(day in year) + g(day in year) * h(day in week)
其中f
, g
和h
是mgcv
中的循環平滑函數
gam(
y ~ s(day_in_year, k=52, bs='cc')
+ s(day_in_year, k=52, bs='cc'):s(day_in_week, k=5, bs='cc')
, knots=list(
day_in_year=c(0, 356)
, day_in_week=c(0,7)
)
, data = data
)
不幸的是,這不起作用並拋出錯誤NA/NaN argument
。 我嘗試使用te(day_in_year, day_in_week, k=c(52, 5), bs='cc')
,它有效,但引入了太多的自由度,因為該模型過度擬合了假期,這些假期落在有限的可用工作日內年。
是否可以按照我嘗試的方式指定模型?
哇,一個很老的問題!
雖然每周模式本身在一年中非常穩定,但其幅度也隨季節變化。
使用張量積樣條基te
是交互的正確方式,盡管更合適的構造函數是ti
。 你說te
返回很多參數。 當然。 第一個邊距為k = 52
,第二個邊距為k = 5
,那么該張量項的系數為52 * 5 - 1
。 但這只是創建交互的方式。
請注意,在mgcv
GAM 公式中, :
或*
僅對參數項之間的交互有效。 平滑之間的交互由te
或ti
處理。
如果這不是您所希望的,那么您期望“產品”是什么? 兩個邊際設計矩陣的 Hadamard 乘積? 那么這有什么意義呢? 順便說一下,Hadamard 乘積需要兩個相同維度的矩陣。 但是,您的兩個邊距的列數不同。
如果你不明白為什么我一直在談論矩陣,那么你需要閱讀 Simon 在 2006 年的書。雖然 GAM 估計解釋現在已經過時了,GAM 的構造/設置(如設計矩陣和懲罰矩陣)在章節中解釋4 即使在十年之后也根本沒有改變。
好的,讓我再給你一個提示。 用於構建te
/ ti
設計矩陣的行式 Kronecker 產品並不是一項新發明。
平滑項s(x)
很像因子變量g
,就好像它們看起來是單個變量一樣,它們被構造為具有許多列的設計矩陣。 對於g
它是一個虛擬矩陣,而對於f(x)
它是一個基矩陣。 因此,兩個平滑函數之間的交互作用的構建方式與構建兩個因素之間的交互作用的方式相同。
如果您的因子g1
為 5 個水平,另一個因子g2
為 10 個水平,它們的邊際設計矩陣(對比后)有 4 列和 9 列,那么交互作用g1:g2
將有 36 列。 這樣的設計矩陣,就是g1
和g2
的設計矩陣的行式 Kronecker 乘積。
正如你所說,你只有幾年的數據,也許是 2 或 3 年? 在這種情況下,您的模型已通過對day_in_year
使用k = 52
過度參數化。 嘗試將其減少到例如k = 30
。
如果過度擬合仍然很明顯,這里有一些方法可以解決它。
方式 1:您使用 GCV 進行平滑度選擇。 嘗試method = "REML"
。 GCV 總是傾向於過度擬合數據。
方式 2:繼續使用 GCV,手動增加平滑參數以獲得更重的懲罰。 gam
gamma
參數在這里很有用。 例如,嘗試gamma = 1.5
。
結的位置,應該是day_in_year = c(0, 365)
嗎?
您的模型沒有多大意義,因為您聲明有:
這可以完全表示為平滑的張量積。 您在此處其他答案的評論中提到的模型
y ~ f(一年中的一天) + g(一年中的一天) * h(一周中的一天)
如果您的意思是*
作為主要效應 + 交互作用,那么它只是完整張量積的分解。 在這種情況下,您擁有的模型無法識別-您兩次獲得一年中的某一天的函數。 如果您的意思是:
的等價物,那么您的模型就沒有星期幾的主要影響,這似乎是不可取的。
我一直適合這種形式的模型(僅適用於一年中的每一天)。 我會通過以下方式解決這個問題:
gam(y ~ te(day_of_year, day_of_week, k = c(20, 6), bs = c("cc", "cc")),
data = foo, method = "REML", knots = knots)
您還可以調整結定義。 我傾向於使用以下內容:
knots <- list(day_of_year = c(0.5, 366.5),
day_of_week = c(0.5, 7.5)
這不會有太大區別,但您只是將邊界結點放置在離數據更近一點的地方。
如果要分解效果,可以使用ti()
平滑擬合模型
gam(y ~ ti(day_of_year, bs = "cc", k = 12) + ti(day_of_week, bs = "cc", k = 6) +
te(day_of_year, day_of_week, k = c(12, 6), bs = c("cc", "cc")),
data = foo, method = "REML", knots = knots)
您可以結合數據和gam.check()
調整k
的值以確定合適的值。
您還需要向模型添加一個術語來處理假期。 這將是參數項,如果這一天是假日,則應用調整 - 因此,創建一個因子holiday
並將其添加到模型+ holiday
。 你可以想到更復雜的模型; 如果一周有假期,則可能是一個因子索引,再加上day_of_week
組件的因子平滑,這樣您就可以在一周是正常周的情況下估計一個每周模式,如果該周包含假期,則估計第二個每周模式。
如果您向我們展示數據的示例/圖,我可以不那么普遍地展開或評論。
您安裝的te()
平滑不能很好地適應假期也就不足為奇了; 該模型假設周效應是平滑的,並且隨着一年中的一天效應的平滑變化。 假期不是從前一周或后一周的每周模式平穩偏離的。 假日效應並沒有通過平滑的關系很好地建模,需要其他一些東西來解釋這種效應。
我的問題現在已經有幾年了,我想添加最終對我最有效的解決方案。
首先,不可能使用 mgcv 擬合我的問題中描述的模型。
有一段時間我使用了一個兩階段的過程。
model1 = gam(
y ~ s(day_in_year, k=52, bs='cc')
+ s(day_in_year, k=52, bs='cc')
+ as.factor(day_in_week)
, knots=list(
day_in_year=c(0, 366)
, day_in_week=c(0,7)
)
, data = data
)
# get_weekday_offset gets the coefficients for each weekday and normalizes them to have mean 0
data$weekday_offset = get_weekday_offset(model1)[data$day_in_week]
model2 = gam(
y ~ s(day_in_year, k=52, bs='cc')
+ s(day_in_year, k=52, bs='cc')
+ s(day_in_year, k=52, bs='cc', by=weekday_offset)
+ as.factor(day_in_week)
, knots=list(
day_in_year=c(0, 366)
, day_in_week=c(0,7)
)
, data = data
)
第二個模型的形式為"y ~ f(年中的天)+g(年中的天)*h(周中的天)",但 h 是一個固定函數,不適合第二個模型中的數據,而是基於最適合model1
。 這樣做的明顯缺點是 mgcv 需要運行兩次,並且幾乎所有第一次運行的信息(工作日的系數除外)都被丟棄。
我最終放棄這個模型的原因是除了振幅的變化之外,在一年的過程中,形狀確實發生了一些變化,所以我最終確定了這個樣子。
gam(
y ~ s(day_in_year, k=52, bs='cc')
+ s(day_in_year, k=10, bs='cc', by=as.factor(day_in_week)
+ as.factor(day_in_week)
, knots=list(
day_in_year=c(0, 366)
, day_in_week=c(0,7)
)
, data = data
)
我刪除了工作日的s
,因為我使用每周模式的最大自由度來證明數據是合理的,並且我添加了工作日和一年中的某一天之間的交互效果( s(day_in_year, k=12, bs='cc', by=as.factor(day_in_week)
),這估計了每個工作日的單獨季節性曲線,但是因為它包含的自由度數遠低於季節性項s(day_in_year, k=52, bs='cc')
,我沒有遇到嘗試te
條款時遇到的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.