在ggplot2的stat_density2d中指定密度的比例尺

7
我希望创建多个密度图,制作“动态热力图”。由于每个动画帧应该是可比较的,因此我希望每个图形上的密度->颜色映射对于所有图形都相同,即使数据的范围对于每个图形都发生了变化。这是我为每个单独的图形使用的代码:
ggplot(data= this_df, aes(x=X, y=Y) ) + 
    geom_point(aes(color= as.factor(condition)), alpha= .25) +
    coord_cartesian(ylim= c(0, 768), xlim= c(0,1024)) + scale_y_reverse() +
    stat_density2d(mapping= aes(alpha = ..level..), geom="polygon", bins=3, size=1)

假设我使用相同的代码,但是'this_df'在每一帧中都会改变。因此,在一个图形中,密度可能从0到4e-4不等。在另一个图形中,密度范围从0到4e-2不等。
默认情况下,ggplot将为每个密度计算一个不同的密度->颜色映射。但这意味着两个图形--动画的两个帧--实际上并不可比较。如果这是直方图或密度图,我只需调用coord_cartesian并更改x和y lim即可。但对于密度图,我不知道如何更改比例尺。
我能找到的最接近的是: Overlay two ggplot2 stat_density2d plots with alpha channels 但是我没有将两个密度图放在同一图表上的选项,因为我希望它们成为不同的帧。
非常感谢任何帮助!
编辑:
以下是一个可重现的示例:
set.seed(4)
g = list(NA,NA)
for (i in 1:2) {

  sdev = runif(1)
  X = rnorm(1000, mean = 512, sd= 300*sdev)
  Y = rnorm(1000, mean = 384, sd= 200*sdev)

  this_df = as.data.frame( cbind(X = X,Y = Y, condition = 1:2) )

  g[[i]] = ggplot(data= this_df, aes(x=X, y=Y) ) + 
    geom_point(aes(color= as.factor(condition)), alpha= .25) +
    coord_cartesian(ylim= c(0, 768), xlim= c(0,1024)) + scale_y_reverse() +
    stat_density2d(mapping= aes(alpha = ..level.., color= as.factor(condition)), geom="contour", bins=4, size= 2) 

}
print(g) # level has a different scale for each

你正在将 alpha 映射到级别,而不是颜色。你可以通过添加 scale_alpha_continuous(limits=...) 来控制 alpha 比例尺,其中 limits 是一个向量,指定以 ..level.. 为单位的限制范围,因此我猜是 (0,4e-2)。如果您提供数据集,可能会有人愿意给您更多帮助。 - jlhoward
谢谢您的回复!我已经添加了带有虚假数据的可重现代码(无法共享真实数据)。请注意,如果您在此代码末尾添加 + scale_alpha_continuous(limits= c(0, 2e-6)),它会使alpha缩放连续,但不会修复轮廓。如何使两个图形中的轮廓缩放保持一致? - jwdink
你是说你想在两个图中为相同的..level..值绘制轮廓线吗?如果是这样,在stat_density2d(...)中使用breaks=... - jlhoward
3个回答

12

我想要为这个问题留下一个更新。截至2016年7月,stat_density2d不再接受breaks参数。为了重新绘制这张图形,你需要将breaks=1e-6*seq(0,10,by=2)移动到scale_alpha_continuous()中。

set.seed(4)
g = list(NA,NA)
for (i in 1:2) {
    sdev = runif(1)
    X = rnorm(1000, mean = 512, sd= 300*sdev)
    Y = rnorm(1000, mean = 384, sd= 200*sdev)
    this_df = as.data.frame( cbind(X = X,Y = Y, condition = 1:2) )

g[[i]] = ggplot(data= this_df, aes(x=X, y=Y) ) +
         geom_point(aes(color= as.factor(condition)), alpha= .25) +
         coord_cartesian(ylim= c(0, 768), xlim= c(0,1024)) +
         scale_y_reverse() +
         stat_density2d(mapping= aes(alpha = ..level.., color= as.factor(condition)),
         geom="contour", bins=4, size= 2) +
         scale_alpha_continuous(limits=c(0,1e-5), breaks=1e-6*seq(0,10,by=2))+
         scale_color_discrete("Condition")
    }

do.call(grid.arrange,c(g,ncol=2))

10
因此,要使两个图表显示相同级别的轮廓,请在stat_densit2d(...)中使用breaks = ... 参数。 要使两个图表具有相同的alpha映射到级别,请使用scale_alpha_continuous(limits = ...)
以下是完整代码示例:
library(ggplot2)
set.seed(4)
g = list(NA,NA)
for (i in 1:2) {
  sdev = runif(1)
  X = rnorm(1000, mean = 512, sd= 300*sdev)
  Y = rnorm(1000, mean = 384, sd= 200*sdev)
  this_df = as.data.frame( cbind(X = X,Y = Y, condition = 1:2) )

  g[[i]] = ggplot(data= this_df, aes(x=X, y=Y) ) + 
    geom_point(aes(color= as.factor(condition)), alpha= .25) +
    coord_cartesian(ylim= c(0, 768), xlim= c(0,1024)) + scale_y_reverse() +
    stat_density2d(mapping= aes(alpha = ..level.., color= as.factor(condition)), 
                   breaks=1e-6*seq(0,10,by=2),geom="contour", bins=4, size= 2)+
    scale_alpha_continuous(limits=c(0,1e-5))+
    scale_color_discrete("Condition")
}
library(gridExtra)
do.call(grid.arrange,c(g,ncol=2))

And the result...


1
这是我今天所需要的解决方案。我运行了代码并发现stat_density2d在当前的ggplot2(ggplot2_2.1.0)中不接受breaks参数。你能想到其他实现相同效果的方法吗? - jazzurro
在ggplot中,是否可以分配自己计算的密度到stat_density函数之外的其他地方并使用它? - Adel

0

不确定这对你有多大用处,但我发现使用以下方式更容易:

scale_fill_gradient(low = "purple", high = "yellow", limits = c(0, 1000))

在编程中,您可以轻松地覆盖图形的限制、选择颜色等。您只需将其添加到代码末尾即可,它会覆盖大多数需要的内容,因此易于使用。

或者使用类似的解决方案: library(viridis)#热力图颜色

  scale_fill_viridis(option = 'inferno')+  
  scale_fill_viridis_c(limits = c(0, 1000))

感谢您的贡献。我不太清楚这如何回答问题。您能否添加完整的绘图代码并嵌入结果?完成后请告诉我。 - tjebo
在我的情况下,我有一个地图(芝加哥市),我想展示某些事情发生的频率。但是我有不同的数据框架来存储这些数据。由于如果您不指定,则会有默认比例尺,因此热力图不适合进行比较。例如,一个比例尺为0-500,另一个比例尺为0-1500。因此,这些热力图将表示不同的含义。在这种情况下,添加scale_fill_viridis(option = 'inferno')+ scale_fill_viridis_c(limits = c(0, 1000))将为所有热力图提供相同的比例尺。 - Douwe
另外,我发现这样做更容易,因为我不必处理 breaks=1e-6*seq(0,10,by=2) 这些东西。但现在我很好奇你是否同意我的看法,或者你会说另一种方式更清晰/更干净/更灵活之类的。 - Douwe

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