我正在用R中的学术论文(见结尾引用)实现统计方法。我认为有一种方法可以在不使用循环的情况下执行其中一个步骤,但我无法确定如何攻击它
此方法对具有三个变量的数据帧进行操作:x,n和p。它只能在所有i的p [i] <= p [i + 1]时运行。如果一对点违反了这一点,则通过将p [i]和p [i + 1]设置为等于其加权平均值来平滑它们 (N [1] * P [I] + N [I + 1] * P [I + 1])/(N [1] + N [I + 1]) 迭代此平滑直到p_i是非递减序列。
这种平滑的问题在于a)循环在R中是不良形式,并且b)如果在一行中存在多个点,则p_i> p_(i + 1)> = p_(i + 2),该方法可能无法终止或需要很长时间才能收敛。例如,如果发生这样的序列:
x n p
2 10 0.6
5 10 0.5
10 10 0.5
平滑将p的前两个值设置为0.55,然后将第二个值设置为0.525,然后将前两个设置为0.5325,依此类推并永远循环(或者如果我很幸运达到了重要的极限) a bajillion迭代)。通过识别相邻的递减数据点并将它们作为一组进行平均,应该有一种数学上等效但更有效的方法,但我不确定如何在R中进行处理。
如果您需要更多背景资料,相关论文为Martin A. Hamilton, Rosemarie C. Russo, Robert V. Thurston. "Trimmed Spearman-Karber method for estimating median lethal concentrations in toxicity bioassays." Environ. Sci. Technol., 1977, 11 (7), pp 714–719。我指的是第716页的“第一步”部分。
答案 0 :(得分:2)
根据我对算法的理解,您需要找到p
正在减少的位置,并从每个位置开始,找出(累计)加权平均值减少的时间,以便p
可以逐块更新。如果没有某种循环,我不知道如何做到这一点。一些解决方案可能会隐藏lapply
下的循环或等效但恕我直言,这是那些复杂到足以让我更喜欢旧循环的算法之一。你可能会失去一点效率,但代码读得很好。我的尝试,使用while
循环:
smooth.p <- function(df) {
while (any(diff(df$p) < 0)) {
# where does it start decreasing
idx <- which(diff(df$p) < 0)[1]
# from there, compute the cumulative weighted average
sub <- df[idx:nrow(df), ]
cuml.wavg <- cumsum(sub$n * sub$p) / cumsum(sub$n)
# and see for how long it is decreasing
bad.streak.len <- rle(diff(cuml.wavg) <= 0)$lengths[1]
# these are the indices for the block to average
block.idx <- seq(from = idx, length = bad.streak.len + 1)
# compute and apply the average p
df$p[block.idx] <- sum(df$p[block.idx] * df$n[block.idx]) /
sum(df$n[block.idx])
}
return(df)
}
以下是一些数据,包括您建议的粗略补丁:
df <- data.frame(x = 1:9,
n = rep(1, 9),
p = c(0.1, 0.3, 0.2, 0.6, 0.5, 0.5, 0.8, 1.0, 0.9))
df
# x n p
# 1 1 1 0.1
# 2 2 1 0.3
# 3 3 1 0.2
# 4 4 1 0.6
# 5 5 1 0.5
# 6 6 1 0.5
# 7 7 1 0.8
# 8 8 1 1.0
# 9 9 1 0.9
输出:
smooth.p(df)
# x n p
# 1 1 1 0.1000000
# 2 2 1 0.2500000
# 3 3 1 0.2500000
# 4 4 1 0.5333333
# 5 5 1 0.5333333
# 6 6 1 0.5333333
# 7 7 1 0.8000000
# 8 8 1 0.9500000
# 9 9 1 0.9500000
答案 1 :(得分:0)
在上面的Glen_b之后,汉密尔顿论文中描述的内容相当于CRAN包gpava
中的isotone
。