ggplot中的概率热图

5

一年前我曾提出这个问题,并得到了一个用于制作“概率热力图”的代码: heatmap

numbet <- 32
numtri <- 1e5
prob=5/6
#Fill a matrix 
xcum <- matrix(NA, nrow=numtri, ncol=numbet+1)
for (i in 1:numtri) {
x <- sample(c(0,1), numbet, prob=c(prob, 1-prob), replace = TRUE)
xcum[i, ] <- c(i, cumsum(x)/cumsum(1:numbet))
}
colnames(xcum) <- c("trial", paste("bet", 1:numbet, sep=""))

mxcum <- reshape(data.frame(xcum), varying=1+1:numbet, 
idvar="trial", v.names="outcome", direction="long", timevar="bet")


library(plyr)
mxcum2 <- ddply(mxcum, .(bet, outcome), nrow)
mxcum3 <- ddply(mxcum2, .(bet), summarize, 
            ymin=c(0, head(seq_along(V1)/length(V1), -1)), 
            ymax=seq_along(V1)/length(V1),
            fill=(V1/sum(V1)))
head(mxcum3)

library(ggplot2)

p <- ggplot(mxcum3, aes(xmin=bet-0.5, xmax=bet+0.5, ymin=ymin, ymax=ymax)) + 
geom_rect(aes(fill=fill), colour="grey80") + 
scale_fill_gradient("Outcome", formatter="percent", low="red", high="blue") +
scale_y_continuous(formatter="percent") +
xlab("Bet")

print(p)

(可能需要稍微更改此代码,因为这个问题

这几乎就是我想要的。除了每个垂直轴应该有不同数量的条形图,即第一个应该有2,第二个应该有3,第三个应该有4(N+1)。在图表中,第6+7个轴具有相同数量的条形图(7),而7应该有8(N+1)。

如果我没错的话,代码之所以会这样做,是因为它是观察到的数据,如果我运行更多试验,我们将获得更多的条形图。我不想依靠试验次数来获得正确的柱子数。

如何调整此代码以提供正确数量的条形图?


@Arun 但这是否意味着无法绘制图形? - Frank Zafka
这段代码不是我写的。它作为对之前问题的回答进行发布。它几乎可以实现... - Frank Zafka
1
因为我打算给你的回答发放赏金。 - Frank Zafka
1个回答

14

我已经使用R的dbinom函数生成了n=1:32次试验中正面朝上的频率,并绘制了图表。这是您所期望的结果。我阅读了您在SO和math.stackexchange上的一些早期帖子,但仍然不明白为什么您要“模拟”实验而不是从二项式随机变量中生成。如果您能解释一下,那就太好了!我将尝试使用@Andrie提供的模拟解决方案来检查是否可以匹配下面显示的输出。现在,这里有一些您可能感兴趣的内容。

set.seed(42)
numbet <- 32
numtri <- 1e5
prob=5/6

require(plyr)
out <- ldply(1:numbet, function(idx) {
    outcome <- dbinom(idx:0, size=idx, prob=prob)
    bet     <- rep(idx, length(outcome))
    N       <- round(outcome * numtri)
    ymin    <- c(0, head(seq_along(N)/length(N), -1))
    ymax    <- seq_along(N)/length(N)
    data.frame(bet, fill=outcome, ymin, ymax)
})

require(ggplot2)
p <- ggplot(out, aes(xmin=bet-0.5, xmax=bet+0.5, ymin=ymin, ymax=ymax)) + 
geom_rect(aes(fill=fill), colour="grey80") + 
scale_fill_gradient("Outcome", low="red", high="blue") +
xlab("Bet")

情节:

ggplot2

编辑:解释你的旧代码为什么不能达成你的意图以及Andrie的代码是如何工作的。

基本上,Andrie所做的(或者说是一种看待它的方式)是利用了这样一个思想:如果你有两个二项分布,X ~ B(n, p)Y ~ B(m, p),其中 n, m = sizep = probability of success,那么他们的和,X + Y = B(n + m, p) (1)。因此,xcum 的目的是为了获得所有 n = 1:32 次投掷的结果,但为了更好地解释它,让我逐步构造代码。随着解释的进行,xcum 的代码也将非常明显,可以在很短的时间内构建出来(无需任何必要的 for-loop 和每次构建 cumsum)。

如果你到目前为止都跟上了我的思路,那么我们的想法首先是创建一个 numtri * numbet 矩阵,每个列(length = numtri)都有 0's1's,概率分别为 5/61/6。也就是说,如果你有 numtri = 1000,那么你将会有大约 834 个 0's 和 166 个 1's * 对于每一个 numbet 列(这里是32)。让我们先构造并测试这个矩阵。

numtri <- 1e3
numbet <- 32
set.seed(45)
xcum <- t(replicate(numtri, sample(0:1, numbet, prob=c(5/6,1/6), replace = TRUE)))

# check for count of 1's
> apply(xcum, 2, sum)
[1] 169 158 166 166 160 182 164 181 168 140 154 142 169 168 159 187 176 155 151 151 166 
163 164 176 162 160 177 157 163 166 146 170

# So, the count of 1's are "approximately" what we expect (around 166).

现在,每个列都是二项分布的样本,其中n = 1size = numtri。如果我们将前两列相加并用这个总和替换第二列,则根据(1),由于概率相等,我们最终将得到一个n = 2的二项分布。同样地,如果您将前三列相加并将第3列替换为此总和,那么您将获得一个n = 3的二项分布,以此类推... 概念是,如果您累计添加每列,那么您将得到numbet个二项分布(这里是1到32)。所以,让我们这样做。
xcum <- t(apply(xcum, 1, cumsum))

# you can verify that the second column has similar probabilities by this:
# calculate the frequency of all values in 2nd column.
> table(xcum[,2])
  0   1   2 
694 285  21 

> round(numtri * dbinom(2:0, 2, prob=5/6))
[1] 694 278  28
# more or less identical, good!

如果您按照以下方式对每行使用 cumsum(1:numbet),将我们迄今为止生成的 xcum 进行除法:

xcum <- xcum/matrix(rep(cumsum(1:numbet), each=numtri), ncol = numbet)

这将与for-loop生成的xcum矩阵完全相同(如果您使用相同的种子生成它)。然而,我不太理解Andrie进行这种除法的原因,因为这并不是生成所需图形所必需的。不过,我想这可能与您在math.stackexchange上的早期帖子中提到的frequency值有关。

现在让我们谈谈为什么您难以获得我附加的图形(具有n+1个箱):

对于具有n=1:32次试验、尾部(失败)概率为5/6和正面(成功)概率为1/6的二项式分布,k个正面的概率如下:

nCk * (5/6)^(k-1) * (1/6)^k # where nCk is n choose k

对于我们生成的测试数据,对于n=7n=8(试验),出现k=0:7k=0:8枚硬币正面朝上的概率如下:

# n=7
   0    1    2     3     4     5 
.278 .394 .233  .077  .016  .002 

# n=8
   0    1    2    3     4      5 
.229 .375 .254 .111  .025   .006 

为什么它们两个都只有6个箱子,而不是8个或9 个?当然,这与numtri=1000的值有关。通过使用dbinom从二项分布直接生成概率,让我们看看每个这些8和9个箱子的概率,以了解为什么会发生这种情况。
# n = 7
dbinom(7:0, 7, prob=5/6)
# output rounded to 3 decimal places
[1] 0.279 0.391 0.234 0.078 0.016 0.002 0.000 0.000

# n = 8
dbinom(8:0, 8, prob=5/6)
# output rounded to 3 decimal places
[1] 0.233 0.372 0.260 0.104 0.026 0.004 0.000 0.000 0.000

您会发现,与 n=7n=8 相关的 k=6,7k=6,7,8 的概率约为 0。它们的值非常低。这里的最小值实际上是 5.8 * 1e-7n=8k=8)。这意味着,如果您模拟了 1/5.8 * 1e7 次,您就有机会获得 1 个值。如果您检查 n=32k=32 的情况,则该值为 1.256493 * 1e-25。因此,您将需要模拟那么多的值,才能获得至少一个结果,其中所有 32 种结果都是正面朝上的,适用于 n=32
这就是为什么您的结果在某些箱子中没有值的原因,因为对于给定的 numtri,其发生的概率非常低。出于同样的原因,直接从二项分布生成概率可以克服这个问题/限制。
我希望我写得足够清楚,让您能够理解。如果您阅读有困难,请告诉我。 编辑 2: 当我用 numtri=1e6 模拟上面编辑的代码时,对于 n=7n=8,并计算 k=0:7k=0:8 的正面朝上的次数,我得到了以下结果:
# n = 7
     0      1      2      3      4      5      6      7 
279347 391386 233771  77698  15763   1915    117      3 

# n = 8
     0      1      2      3      4      5      6      7      8 
232835 372466 259856 104116  26041   4271    392     22      1 

请注意,现在对于n=7和n=8,有k = 6和k = 7。另外,对于n = 8,k = 8的值为1。随着numtri的增加,您将获得更多其他缺失的bin。但这将需要大量的时间/内存(如果可能的话)。

那看起来很棒,Arun。我花了一些时间尝试理解ggplot,但这个有点太复杂了。所以你做得很好。我希望这个可以补充加权图[链接](https://dev59.com/_Gw05IYBdhLWcg3wXQsh)的内容。至于为什么我要模拟它,我不太明白你的意思,但通常是为了帮助我理解。虽然这个是为了好玩。谢谢。我确实想要能够操纵命中率等等... - Frank Zafka
我也是这么想的。只要我能够操纵条件(概率、试验长度等),那么我就很乐意使用这个快捷方式。 - Frank Zafka
最后一条评论。我对模拟数据感兴趣的唯一原因是将随机数生成器(例如random.org)的输出与数据进行比较。如果您能够提供不依赖于dbinom的版本,我也会非常乐意接受。 :) - Frank Zafka
1
非常有趣的扩展分析。我认为它可能是基于观察到的数据。嗯,你的版本暂时可以用!我想如果我们有更多的试验,Andrie的版本可能会起作用。总之都是好东西。再次感谢Arun。 - Frank Zafka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接