R语言 在geom_tile中按面分割重叠的图块

ymdaylpp  于 2023-10-13  发布在  其他
关注(0)|答案(3)|浏览(94)

我堆叠了一个 Dataframe ,显示group s上每idvalue s:

df <- tibble::tibble(id = c(LETTERS[1:6], LETTERS[1:5]),
                     value = c(paste0("V", 1:6), paste0("V", 1:5)),
                     group = c(rep("group_1", 6), rep("group_2", 5)))

df
#> # A tibble: 11 x 3
#>    id    value group  
#>    <chr> <chr> <chr>  
#>  1 A     V1    group_1
#>  2 B     V2    group_1
#>  3 C     V3    group_1
#>  4 D     V4    group_1
#>  5 E     V5    group_1
#>  6 F     V6    group_1
#>  7 A     V1    group_2
#>  8 B     V2    group_2
#>  9 C     V3    group_2
#> 10 D     V4    group_2
#> 11 E     V5    group_2

我想创建一个热图,显示每个group s(fill)中每个id(y)的每个value(x)的“可用性”:

ggplot(df, aes(x = id, y = value, fill = group)) + 
  geom_tile()

问题是fill重叠:我所能看到的是,F/V6只在group_1(而不是在group_2)。然而,对于ID A到E,值V1到V5在两个组中都可用,因此group_2的颜色在group_1的顶部,使其看起来像它们只在group_2中可用。
如果我使用facet_wrap(),可用性更明显:

ggplot(df, aes(x = id, y = value, fill = group)) + 
  geom_tile() + 
  facet_wrap("group")

然而,在我的真实的设置中,热图非常大,所以很难比较哪个组中有哪些值可用。
如果值在两个组中都可用,是否可以将每个图块一分为二,如果值只存在于一个组中,是否可以将其保持完整?因此,在上面的第一个图中,蓝色瓷砖将被一分为二(同时显示蓝色和红色),红色瓷砖将保持不变。

更新

感谢stefan对使用position = "dodge"的出色提示。然而,我注意到我的问题实际上比上面的reprex更复杂:每个value可以出现在每个group的多个id中。当使用position = "dodge"时,ggplot 2会将每个id“列””划分为与id中每个value的出现次数一样多的部分:

df <- tibble::tibble(id = c("A", "A",  "A", "B", "B", "C", "C", "C", "A", "A", "B", "B", "C", "C"),
                     value = c("V1", "V2", "V3", "V1", "V3", "V1", "V2", "V4", "V1", "V2", "V1", "V3", "V1", "V4"),
                     group = c(rep("group_1", 8), rep("group_2", 6)))

df
#> # A tibble: 14 x 3
#>    id    value group  
#>    <chr> <chr> <chr>  
#>  1 A     V1    group_1
#>  2 A     V2    group_1
#>  3 A     V3    group_1
#>  4 B     V1    group_1
#>  5 B     V3    group_1
#>  6 C     V1    group_1
#>  7 C     V2    group_1
#>  8 C     V4    group_1
#>  9 A     V1    group_2
#> 10 A     V2    group_2
#> 11 B     V1    group_2
#> 12 B     V3    group_2
#> 13 C     V1    group_2
#> 14 C     V4    group_2

ggplot(df, aes(x = id, y = value, fill = group)) + 
  geom_tile(position = "dodge")

你可以看到,在“A列”中,三个瓷砖被放置在彼此的上方和旁边,将可用空间分成三部分。我想实现的是将这三对图块在“A列”中绘制在彼此的顶部,以便它们对齐,使用分配给每个值的“A列”的整个可用空间。

hc8w905p

hc8w905p1#

一个选项是使用position="dodge"

library(ggplot2)

ggplot(df, aes(x = id, y = value, fill = group)) + 
  geom_tile(position = "dodge")

更新

你可以尝试在group aes上Map组:

ggplot(df, aes(x = id, y = value, fill = group, group = group)) + 
  geom_tile(position = "dodge", color = "black") # adding 'color' for borders

tct7dpnv

tct7dpnv2#

如果你想要三角形,你可能需要手动使用一些wrangling和geom_polygon,比如:

library(ggplot2)

df <- tibble::tibble(x = c(LETTERS[1:6], LETTERS[1:5]),
                     y = c(paste0("V", 1:6), paste0("V", 1:5)),
                     group = c(rep("group_1", 6), rep("group_2", 5)))

df1    <- df[!duplicated(interaction(df$x, df$y)),]
df2    <- df[duplicated(interaction(df$x, df$y)),]
df2    <- df[rep(seq(nrow(df)), each = 3),]
df2$x1 <- as.numeric(as.factor(df2$x))
df2$y1 <- as.numeric(as.factor(df2$y))
df2$x1 <- df2$x1 + c(-0.5, 0.5, 0.5)
df2$y1 <- df2$y1 + c(-0.5, -0.5, 0.5)
df2$z  <- rep(seq(nrow(df2)/3), each = 3)

ggplot(df1, aes(x = x, y = y, fill = group)) + 
  geom_tile() +
  geom_polygon(data = df2, aes(x = x1, y = y1, group = z))

创建于2022-02-16由reprex package(v2.0.1)

5sxhfpxr

5sxhfpxr3#

我刚刚实现了一个新的geom GeomSplitTile,它允许在矩阵图的每个字段中绘制两个相关的值。除了添加所需的定义外,这更容易使用,并且应该允许与faceting和position s的任意组合:

library(rlang)
library(ggplot2)

# Provides a diagonally split version of GeomTile
# by [jan-glx](https://github.com/jan-glx), based on ggplot2::GeomTile by the [ggplot2 authors](https://github.com/tidyverse/ggplot2/graphs/contributors)

draw_key_split_tile <- function(data, params, size) {
  
  data$width <- data$width %||% params$width %||% 1
  data$height <- data$height %||% params$height %||% 1
  data$width[is.na(data$width)] <- 1
  data$height[is.na(data$height)] <- 1
  if (isTRUE(data$split)) {
    x <- c(0, 1, 0)
    y <- c(0, 1, 1)
  } else {
    x <- c(0, 1, 1)
    y <- c(0, 1, 0)
  }
  x <- 0.5 + (x-0.5) * data$width
  y <- 0.5 + (y-0.5) * data$height
  
  grid::polygonGrob(
    x = x,
    y = y,
    default.units = "npc",
    gp = grid::gpar(
      col = data$colour,
      fill = alpha(data$fill, data$alpha)
    )
  )
}

geom_split_tile <- function(mapping = NULL, data = NULL,
                            stat = "identity", position = "identity",
                            ...,
                            linejoin = "mitre",
                            na.rm = FALSE,
                            show.legend = NA,
                            inherit.aes = TRUE) {
  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomSplitTile,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = rlang:::list2(
      linejoin = linejoin,
      na.rm = na.rm,
      ...
    )
  )
}

GeomSplitTile <- ggproto(
  "GeomSplitTile", GeomPolygon,
  extra_params = c("na.rm"),
  
  setup_data = function(data, params) {
    data$width <- data$width %||% params$width %||% resolution(data$x, FALSE)
    data$height <- data$height %||% params$height %||% resolution(data$y, FALSE)
    data$split <- as.factor(data$split %||% params$split %||% FALSE)
    
    K = 3
    n <- nrow(data)
    new_data <- data.frame(
      x = rep(data$x, each=K) + rep(3-as.integer(data$split)*2, each=K) * rep(c(-1,  1, 1), n) * rep(data$width / 2, each=K),
      y = rep(data$y, each=K) + rep(3-as.integer(data$split)*2, each=K) * rep(c(-1, -1, 1), n) * rep(data$height / 3, each=K),
      group = rep(seq_len(n), each=K)
    )
    new_data <- cbind(new_data, data[rep(seq_len(n), each = K), setdiff(colnames(data), c("x", "y", "group")), drop = FALSE])
    new_data
  },
  
  default_aes = aes(fill = "grey20", colour = NA, linewidth = 0.1, linetype = 1,
                    alpha = NA, width = NA, height = NA),
  
  required_aes = c("x", "y", "split"),
  
  draw_key = draw_key_split_tile
)

scale_split <- function(..., scale_name="scale_direction", palette = function(n) if(n>2) error(paste0(scale_name, " can handle at most 2 levels")) else c(FALSE, TRUE)) discrete_scale(aesthetics = "split", scale_name=scale_name, palette = palette, ... )

使用您的示例:

df <- tibble::tibble(id = c(LETTERS[1:6], LETTERS[1:5]),
                     value = c(paste0("V", 1:6), paste0("V", 1:5)),
                     group = c(rep("group_1", 6), rep("group_2", 5)))

ggplot(df, aes(x = id, y = value, fill = group, split=group)) + 
  geom_split_tile() + scale_split()

创建于2023-09-29由reprex package(v2.0.1)
希望这有帮助,让我知道,如果你想在一个独立的/其他一些R包!

相关问题