分割填充ggplot geom_tile(或热图):两种颜色的第三个值

时间:2016-07-21 13:59:14

标签: r ggplot2 visualization heatmap

我有分类数据,我想要映射使用热图(geom_tile)的频率,非常类似于下面的示例:

data("mtcars")
freq <- data.frame(xtabs(~cyl + gear, mtcars)) #count number of 4,6,8 cyl cars by gear
ggplot(freq, aes(cyl, gear)) +
  geom_tile(aes(fill = Freq)) + 
  scale_fill_gradient(low = "white",high = "steelblue")

standard frequency count heatmap

但我想根据重要或非重要结果(0-1值)的比例分割每个图块。在这个例子中,我会生成相同的频率计数但区分自动和手动传输(am

freq_am <- data.frame(xtabs(~cyl + gear + am, mtcars))
print(freq_am)
   #cyl gear am Freq
      4    3  0    1
      6    3  0    2
      8    3  0   12
      4    4  0    2
      6    4  0    2
      8    4  0    0
      4    5  0    0
      6    5  0    0
      8    5  0    0
      4    3  1    0
      6    3  1    0
      8    3  1    0
      4    4  1    6
      6    4  1    2
      8    4  1    0
      4    5  1    2
      6    5  1    1
      8    5  1    2

对于am==0,生成的热图将具有(例如) blue ,用于am==1 red 的值。根据自动(am==0)或手动(am==1)的那种类型的汽车比例,每个瓷砖将被划分(沿对角线?)。蓝色和红色的阴影将与计数成比例,就像渐变已经反映出来一样。

例如:

  • 左上方的瓷砖(4,5)将是完全浅红色,因为所有4缸,5档汽车(计数= 2)都是手动的

  • 左中间瓷砖(4,4)为1/4蓝色和3/4红色,因为25%的4档4缸汽车是自动的(计数= 2),75%是手册(计数= 6)

  • 左下方的瓷砖(4,3)将是完全最轻的蓝色,因为所有4缸,3档汽车(计数= 1)都是自动的

2 个答案:

答案 0 :(得分:3)

这是第二次并且完全尝试通过操纵频率计数来回答问题,以便它们变为am==1的负数。与first attempt的区别在于使用geom_col(position = "fill")代替geom_tile()进行绘图。

注意:我没有编辑the first answer,因为OP已经对它进行了评论,我最终可能会删除第一个和不完整的答案。

准备数据

freq_am <-data.frame(xtabs(~cyl + gear + am, mtcars))
freq_am$Freq_am <- freq_am$Freq * (-1)^as.integer(as.character(freq_am$am))

如果Freq_am(手动),则会创建一个新列Freq,其中-1计数乘以am == 1。使用逻辑值取幂是避免ifelse的一种技巧。

绘图

有两种可能性来实现所需的热图外观。

变式1

p <- ggplot(freq_am, (aes(x = cyl, y = Freq, fill = Freq_am))) + 
  geom_col(position = "fill", width = 1) + 
  scale_fill_gradient2() +
  facet_grid(gear ~ ., as.table = FALSE, switch = "y") + 
  scale_y_continuous(expand = c(0, 0)) + 
  scale_x_discrete(expand = c(0, 0))
p

使用Freq创建cylgeom_col()的堆积条形图,其中条形图垂直拉伸(position = "fill")和水平拉伸(width = 1)填写绘图区域。此外,expand = c(0, 0)函数的scale参数告诉ggplot 像往常一样展开轴。请注意,x轴是离散的,因为xtabs()已将cyl强制为因子。

facet_grid()用于模拟y轴,grid值按递增顺序(as.table = FALSE)。 switch = "y"将面板条移动到左侧。

scale_fill_gradient2()默认使用方便的分色配色方案,以便自动变速器的车辆数量显示为蓝色,手动变速器的车辆数量显示为红色。

enter image description here

现在,我们需要删除热图所不需要的所有装饰和空间。最后,重命名y轴标签:

p + theme(panel.grid = element_blank()
          , axis.ticks = element_blank()
          , axis.text.y = element_blank()
          , strip.background = element_blank()
          , panel.spacing.y = unit(0, "pt")
) + 
  ylab("gear")

enter image description here

这种方法的缺点是瓷砖之间缺乏边界。因此,如果相邻的瓷砖具有相同的颜色,例如6缸,3档和4档,则很难区分计数的份额。

变式2

此变体在图块之间添加边框。边框的宽度可以灵活调整:

p <- ggplot(freq_am, (aes(x = 1, y = Freq, fill = Freq_am))) + 
  geom_col(position = "fill") + 
  scale_fill_gradient2() +
  facet_grid(gear ~ cyl, as.table = FALSE, switch = "both") +
  scale_y_continuous(expand = c(0, 0)) + 
  scale_x_continuous(expand = c(0, 0))
p

在这里,我们将facet_grid()用于两个方向。对于每个面板,如上所述使用Freq绘制1与虚拟变量geom_col()的关系。由于虚拟变量1是数字,我们不需要width参数geom_col()。两个轴现在都是连续的。

enter image description here

同样,我们需要删除一些装饰并重命名x和y轴上的标签:

p + theme(panel.grid = element_blank()
        , axis.ticks = element_blank()
        , axis.text = element_blank()
        , strip.background = element_blank()
        # , panel.spacing = unit(0, "pt")
  ) + 
  xlab("cyl") + ylab("gear")

enter image description here

现在,我们确实有一个瓷砖之间有边框的热图。要删除边框或调整宽度,您可以使用panel.spacing取消注释该行并更改该值。

答案 1 :(得分:1)

这是首次尝试通过操纵频率计数找到Q的(不完整)答案,使它们对am==0变为负数。

请注意,问题并不完全清楚。 ?mtcarsam定义为

  

传输(0 =自动,1 =手动)。

虽然OP已定义

  

自动(am==1)或手动(am==0

这恰恰相反。此外,OP已请求热图显示{em>蓝色以显示am==1的值am==0红色

准备数据

freq_am <-data.frame(xtabs(~cyl + gear + am, mtcars))
freq_am$Freq_am <- -freq_am$Freq * (-1)^as.integer(as.character(freq_am$am))
freq_am$gear_am <- factor(paste(as.character(freq_am$gear), as.character(freq_am$am), sep = "_"))

freq_am
#freq_am
#   cyl gear am Freq Freq_am gear_am
#1    4    3  0    1      -1     3_0
#2    6    3  0    2      -2     3_0
#3    8    3  0   12     -12     3_0
#4    4    4  0    2      -2     4_0
#5    6    4  0    2      -2     4_0
#6    8    4  0    0       0     4_0
#7    4    5  0    0       0     5_0
#8    6    5  0    0       0     5_0
#9    8    5  0    0       0     5_0
#10   4    3  1    0       0     3_1
#11   6    3  1    0       0     3_1
#12   8    3  1    0       0     3_1
#13   4    4  1    6       6     4_1
#14   6    4  1    2       2     4_1
#15   8    4  1    0       0     4_1
#16   4    5  1    2       2     5_1
#17   6    5  1    1       1     5_1
#18   8    5  1    2       2     5_1

请注意xtabs()已强制am因素:

str(freq_am$am)
# Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 2 ...

要将am转换为数字,我们必须使用as.integer(as.character(freq_am$am))。 (您可以使用(as.integer(am) - 1)将关卡编号直接转换为原始数值,但这样可以减少保存。)

在绘制热图时,

gear_am将用作新的y轴。

绘图

library(ggplot2)
ggplot(freq_am, aes(cyl, gear_am, fill = Freq_am)) +
  geom_tile() + 
  scale_fill_gradient2() + 
  theme_minimal() + 
  theme(panel.grid = element_blank())
默认情况下,

scale_fill_gradient2()使用方便的分散颜色方案。 现在,y轴上gear的切片已被分割为am==0am==1的切片。

enter image description here

&#34;不完全&#34;答案

OP已经要求即使零计数也应该完全填充现在拆分的区块。这可以通过进一步操纵freq_am来实现。但是,我发现当前图表以清晰,不明确的方式传达结果。