r - 在 R 中计算碳随时间的半衰期

标签 r nls self-starting

首先,我要说的是,我不确定这是否更属于 StackOverfloww 还是 CrossValidated。最后,我认为这更多的是编码/执行的问题,而不是基本的统计概念,所以我选择把它放在这里。如果它属于其他地方,请告诉我。

我正在尝试计算几种有机 Material 中碳的半衰期。我将这些 Material 孵化了几周,并测量了一段时间内排放的二氧化碳。然后,我将累积排放的二氧化碳毫升数转换为毫克 C,这使我能够计算每个采样间隔每个 sample 中剩余的 C。您会注意到,随着容易矿化的碳被消耗,碳会出现初始下降(不同 Material 的下降程度不同),之后碳的损失就会受到限制。

enter image description here

然后,我尝试使用 SSAsymp 函数(以 0 作为渐近线)计算每个样本的半衰期。我在下面包含了代码和一些示例数据:

dat<-structure(list(Item = c("litter", "woodlt10", "litter", "woodlt10", 
"chargt10", "woodlt10", "litter", "chargt10", "chargt10"), `0` = c(161.4599767, 
178.78608, 154.3154933, 179.5406033, 177.9216, 185.262, 150.8786667, 
195.4312667, 227.50085), `1` = c(161.0021445, 178.3139851, 153.6009328, 
179.2539234, 177.8203349, 185.262, 150.7417449, 195.358527, 227.3496655
), `2.5` = c(158.8259128, 177.5134301, 152.5134086, 178.6545425, 
177.7889754, 184.3638163, 149.216371, 195.358527, 227.3496655
), `4.5` = c(156.5077532, 175.921231, 151.4148628, 177.7793692, 
177.4767007, 183.2183622, 147.201998, 195.0909267, 227.0262222
), `6.5` = c(154.7131141, 174.9474735, 150.4432374, 177.1403608, 
177.2406706, 182.4578207, 146.234637, 194.8740861, 226.7688705
), `9.5` = c(153.2392748, 174.0175268, 149.3042064, 176.5575212, 
176.8846807, 181.7943539, 145.5862023, 194.6301544, 226.4292793
), `13` = c(152.0007445, 173.2103072, 148.4350239, 176.0309575, 
176.5002673, 181.1742383, 145.0268347, 194.4425645, 226.3546808
), `16.5` = c(150.9846197, 172.6132263, 147.6816509, 175.5924338, 
176.3494115, 180.7311843, 144.555467, 194.3811803, 226.2060901
), `21` = c(150.2721712, 172.192254, 147.2036125, 175.3900685, 
176.341071, 180.4498045, 144.2670636, 194.355281, 226.1714114
), `25.5` = c(149.6342556, 171.7482415, 146.6502626, 175.1314172, 
176.2993861, 180.0476477, 143.9400763, 194.2879702, 226.1714114
), `30.5` = c(149.0119875, 171.2716008, 146.1358666, 174.8327655, 
176.1848876, 179.7659473, 143.5427987, 194.2297192, 226.0823399
), `36.5` = c(148.5402568, 170.8086499, 145.6660173, 174.5592093, 
176.0286056, 179.5362906, 143.2190717, 194.1430492, 225.949889
), `43` = c(148.0427195, 170.2820678, 145.1835833, 174.1679759, 
175.8830912, 179.218831, 142.8504933, 194.0126381, 225.76894), 
`49.5` = c(147.7386827, 170.0050513, 144.8519388, 173.8786241, 
175.7664341, 178.9888063, 142.5957979, 193.9764544, 225.6975125
), `56.5` = c(147.4501476, 169.7254062, 144.5900736, 173.6467626, 
175.6701446, 178.805284, 142.3922732, 193.9764544, 225.6401166
), `64.5` = c(147.0743873, 169.3696494, 144.2525808, 173.2666537, 
175.5422531, 178.5399775, 142.1173998, 193.8920486, 225.5513622
), `73` = c(146.7558811, 169.0445058, 143.940297, 172.871404, 
175.4054422, 178.2874291, 141.7951601, 193.7639492, 225.3946395
), `81` = c(146.6028383, 168.9047583, 143.8443744, 172.6848769, 
175.4054422, 178.1929838, 141.664276, 193.7639492, 225.3946395
), `88.5` = c(146.3920556, 168.7163201, 143.7024872, 172.488525, 
175.3520018, 178.0604067, 141.4846825, 193.7430944, 225.3551643
), `99.5` = c(146.1854778, 168.5426061, 143.5068639, 172.3002049, 
175.2961331, 177.9321711, 141.290387, 193.7237412, 225.2926565
)), row.names = c(1L, 2L, 4L, 6L, 7L, 11L, 12L, 16L, 38L), class = "data.frame")

dat.a<-data.frame(t(dat))
dat.a$Days<-rownames(dat.a)
colnames(dat.a)<-dat.a[1,]
colnames(dat.a)<-paste("X",seq(1:ncol(dat.a)),sep="")
dat.a<-dat.a[-1,]
library(dplyr)
dat.a<-mutate_all(dat.a, function(x) as.numeric(as.character(x)))
storage <- list()
for(i in names(dat.a)){
tryCatch({
    storage[[i]] <- log(2)/exp(coefficients(nls(dat.a[,i] ~ SSasymp(X10, 0.0001, R0, lrc),                 
data=dat.a))[3])
}, error=function(e){cat("ERROR :",conditionMessage(e), "\n")})} 
library(dplyr)
halflives<-melt(storage)
samplelist<-data.frame(matrix(NA, nrow = 10, ncol = 1))
samplelist$L1<-colnames(dat.a)
halflives<-merge(samplelist,halflives,by="L1",all=TRUE)
library(readr)
halflives$ord<-parse_number(halflives$L1)
halflives <- halflives[order(halflives$ord),]
colnames(halflives)<-c("L1","junk","halflife","ord")
halflives<-halflives[-10,]
halflives$material<-dat$Item

aggregate(x = halflives$halflife,                 
          by = list(halflives$material),              
          FUN = mean)

但是,我不断收到错误:

ERROR : singular gradient matrix at initial parameter estimates

我认为这是因为我将渐近线设置为零?或者因为响应值随着时间的推移变化不够大?任何人都可以根据我现有的代码或计算半衰期的其他方式确定解决方案吗?

非常感谢您仔细检查我的粗糙代码并花时间帮助我!

最佳答案

以天为单位的半衰期可以计算为 log2(y) 与天数的斜率倒数的负数。

dat2 <- t(dat[-1])
days <- as.numeric(rownames(dat2))
colnames(dat2) <- paste0("X", 1:ncol(dat2))
fm <- lm(log2(dat2) ~ days)
-1/coef(fm)[2, ] # half lives
##        X1        X2        X3        X4        X5        X6        X7        X8        X9 
##  816.2918 1306.5804 1063.3762 1854.8503 4882.6257 1914.4168 1275.1235 8489.2436 7799.1691 

为了存在恒定的半衰期,log2(y) 与天数的关系图应该是一条直线;然而,如下图所示,在问题中显示的数据范围内,情况似乎并非如此,因为早期和后期似乎有不同的半衰期。您可能希望将这些天分为两个或更多段,并分别计算每个段的半衰期。我们将天数范围分为两部分。我们将首先使用 10 天作为分界线,但我们将使用其结果来运行 nls 来拟合两条线——一条在截止日期之前,另一条在不假设任何特定截止日期之后。两条线的方程为 log2(y) = b1 + m1 * days 和 log2(y) = b2 + m2 * days。因为线条上方的区域是凸的,所以我们可以将这两者中的最大值作为在任何点使用的值。通过对 T0 求解方程 b1 + m1 * T0 = b2 + m2 * T0 即可获得截止点。

# starting values
fm1 <- lm(log2(dat2) ~ days, subset = days < 10)
co1 <- coef(fm1)
fm2 <- lm(log2(dat2) ~ days, subset = days >= 10)
co2 <- coef(fm2)

# calculate list of nls objects
fmList <- lapply(1:ncol(dat2), function(i) {
  st <- list(b1 = co1[1,i], m1 = co1[2,i], b2 = co2[1,i], m2 = co2[2,i])
  nls(log2(dat2)[, i] ~ pmax(b1 + m1 * days, b2 + m2 * days), start = st)
})

# plot
fits <- sapply(fmList, fitted)
matplot(days, log2(dat2), col = 1, cex = 0.5)
matplot(days, fits, type = "l", col = 1, lty = 1, add = TRUE)

# calculate statistics (half lives, etc.)
stats <- sapply(fmList, function(fm) {
  co <- as.list(coef(fm))
  with(co, c(co, half1 = -1/m1, half2 = -1/m2, T0 = (b1 - b2) / (m2 - m1)))
})
stats

给予:

      [,1]          [,2]          [,3]          [,4]          [,5]         
b1    7.334025      7.48148       7.267299      7.487643      7.475502     
m1    -0.008487284  -0.004336574  -0.005028027  -0.002671511  -0.0009155759
b2    7.242063      7.435454      7.210472      7.461353      7.46409      
m2    -0.0005902792 -0.0004453948 -0.0005338854 -0.0003596622 -0.0001180285
half1 117.8233      230.5968      198.8852      374.3201      1092.209     
half2 1694.113      2245.199      1873.061      2780.387      8472.529     
T0    11.6452       11.8284       12.64488      11.37197      14.30921     
      [,6]          [,7]          [,8]          [,9]         
b1    7.534028      7.236452      7.610853      7.829888     
m1    -0.003162845  -0.006031012  -0.0006520529 -0.0007230756
b2    7.500386      7.180011      7.60358       7.82288      
m2    -0.0002895582 -0.0004227974 -6.616548e-05 -8.131695e-05
half1 316.1711      165.8096      1533.618      1382.981     
half2 3453.538      2365.199      15113.62      12297.56     
T0    11.70858      10.06396      12.41357      10.92091  

检查

只是为了仔细检查假设我们有

x <- c(0, 3, 6)
y <- c(8, 4, 2)

其半衰期显然为 3。那么

-1/coef(lm(log2(y) ~ x))[[2]]
## [1] 3

screenshot

已添加

关于评论,假设双指数表示您的意思是由 pracma::mexpfit 拟合的模型

library(pracma)

# fmList has one element per dat2 column with fit parameters, etc.
fmList <- lapply(1:ncol(dat2), function(i) {
  mexpfit(days, dat2[, i], -seq(2))
})

# plots
fits <- sapply(fmList, with,
  a0 + a[1] * exp(b[1] * days) + a[2] * exp(b[2] * days)
)
matplot(days, dat2, col = 1, cex = 0.5)
matplot(days, fits, type = "l", col = 1, lty = 1, add = TRUE)

关于r - 在 R 中计算碳随时间的半衰期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69260988/

相关文章:

r - 在 R 中使用 nls2::nls2 时无法抑制警告或消息

r - 如何从 R 中的 nls 获取绘图?

r - 删除矩阵中的行

r - 在 R 中可视化从一组对象到另一组对象的流程

r - R Dataframe 中的交叉表

r - 使用 ggmap 为每个位置行生成 map

oracle - JDBC 瘦驱动程序的 NLS_LANG 设置?