ggplot - facet wrap - 调整比例以显示值之间的明显差异

时间:2018-01-11 04:23:05

标签: r ggplot2

我有一个如下数据框:

text <- "
brand   a   b   c   d   e   f
nissan  99.21   99.78   6496    1.28    216 0.63
toyota  99.03   99.78   7652    1.39    205 0.60
"
df <- read.table(textConnection(text), sep="\t", header = T)

我正在尝试使用face_wrap在单个ggplot中绘制两组的所有变量,如下所示:

library(reshape2)
library(ggplot2)
library(ggthemes)
library(RColorBrewer)
ggplot(melt(df, id = "brand")) + 
  aes(brand, value, fill = brand) + 
  geom_bar(stat = "identity", position='dodge') +
  geom_text(data=melt(df, id = "brand"), angle = 0, 
               aes(brand, value,
                   label = ifelse(value > 100, round(value, 0), value) ) ) +
  facet_wrap(~ variable, scales = "free_y") + 
  scale_fill_brewer(palette = "Paired") + 
    theme(
      legend.position = "top",
      strip.text.y = element_text(angle = 0),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank()
      )

除了一件事以外它运作良好。变量值组之间的明显差异在视觉上并不是很好。例如,对于变量a,我希望条形的高度能够以更容易的方式清晰显示哪个更高。如何使这些接近值之间的高度差更大?

enter image description here

4 个答案:

答案 0 :(得分:15)

我相信人们会很高兴地告诉你如何破解ggplot2,这样你就可以让条形码以任意y值开始。 但是,您需要注意结果将是无意义的垃圾图表,,特别是如果您去除轴刻度的y轴。我建议您在principle of proportional ink.

上阅读此博文

您可以采用的一种解决方案是绘制代表两个变量比率的条形图,如下所示:

text <- "
brand   a   b   c   d   e   f
nissan  99.21   99.78   6496    1.28    216 0.63
toyota  99.03   99.78   7652    1.39    205 0.60
"

df_wide <- read.table(textConnection(text), sep="\t", header = T)

library(ggplot2)
library(tidyr)
library(dplyr)

df_long <- gather(df_wide, variable, value, -brand) %>%
  spread(brand, value) %>%
  mutate(ratio = nissan/toyota,
         label = paste(signif(nissan, 3), signif(toyota, 3), sep = " / "),
         vjust = ifelse(ratio >= 1, -.5, 1.5)) %>%
  mutate(ratio = ifelse(ratio == 1, 1.001, ratio))

ggplot(df_long, aes(variable, ratio, fill = (ratio>=1))) + 
  geom_col() +
  geom_text(aes(label = label, vjust = vjust)) +
  scale_y_log10(name = "ratio Nissan / Toyota",
                breaks = c(.85, .9, .95, 1, 1.05, 1.1),
                expand = c(.15, 0)) +
  scale_fill_brewer(palette = "Paired", guide = "none")

enter image description here

每个配对的结果只有一个条形,但条形高度准确反映了两个变量的相对大小。而且你似乎对绝对大小感兴趣,因为你在原始的facet_wrap()命令中使用了自由y轴缩放。

答案 1 :(得分:5)

修改

(我不打算回到这里,因为我认为所有的答案都是在同一方面。但它一直在我的饲料上突然出现,直到我终于想到了一个没有的角度。已被提及。)

我认为在这种情况下使用条形图有点合理的唯一方法是使非常明确条形高度代表等级,而不是

df2 <- melt(df, id = "brand") %>%
  group_by(variable) %>%
  mutate(rank = rank(value))

> df2
# A tibble: 12 x 4
# Groups: variable [6]
   brand  variable    value  rank
   <fctr> <fctr>      <dbl> <dbl>
 1 nissan a          99.2    2.00
 2 toyota a          99.0    1.00
 3 nissan b          99.8    1.50
 4 toyota b          99.8    1.50
 5 nissan c        6496      1.00
 6 toyota c        7652      2.00
 7 nissan d           1.28   1.00
 8 toyota d           1.39   2.00
 9 nissan e         216      2.00
10 toyota e         205      1.00
11 nissan f           0.630  2.00
12 toyota f           0.600  1.00

使用等级为y值,其他所有内容都可以自然流动而无需引入geom_rect()&amp;等等:

ggplot(df2,
       aes(x = brand, y = rank, fill = brand,
           label = value)) +
  geom_col() +
  geom_text(vjust = 0) +
  facet_wrap(~variable) + 
  scale_fill_brewer(palette = "Paired") + 
  theme(
    legend.position="top",
    strip.text.y = element_text(angle = 0),
    axis.text=element_blank(),
    axis.ticks = element_blank()
  )

(不包括绘制图像。它与下面的图表基本相同,除了y轴标题为&#34;排名&#34;现在。)

结果还是图表垃圾吗?我说是的,但是如果你必须向其他人显示任意条形,告诉他们高度是基于排名,它应该使更有意义。

以下原始答案

如果您确实需要使用条形图,则可以使用geom_rect&amp;为每个方面手动定义不同的ymin。

修改数据框:

library(dplyr)

df2 <- melt(df, id = "brand") %>%
  group_by(variable) %>%
  mutate(ymax = value, 
         ymin = ifelse(diff(value) == 0, 0,
                       min(value) - (max(value) - min(value)) / 2),
         yblank = ifelse(diff(value) == 0, value * 2,
                         max(value) + (max(value) - min(value)) / 2),
         x = as.integer(brand),
         xmin = x - 0.4,
         xmax = x + 0.4,
         label = ifelse(value > 100, round(value, 0), value)) %>%
  ungroup()

> df2
# A tibble: 12 x 10
   brand  variable    value     ymax     ymin   yblank     x  xmin  xmax    label
   <fctr> <fctr>      <dbl>    <dbl>    <dbl>    <dbl> <int> <dbl> <dbl>    <dbl>
 1 nissan a          99.2     99.2     98.9     99.3       1 0.600  1.40   99.2  
 2 toyota a          99.0     99.0     98.9     99.3       2 1.60   2.40   99.0  
 3 nissan b          99.8     99.8      0      200         1 0.600  1.40   99.8  
 4 toyota b          99.8     99.8      0      200         2 1.60   2.40   99.8  
 5 nissan c        6496     6496     5918     8230         1 0.600  1.40 6496    
 6 toyota c        7652     7652     5918     8230         2 1.60   2.40 7652    
 7 nissan d           1.28     1.28     1.23     1.44      1 0.600  1.40    1.28 
 8 toyota d           1.39     1.39     1.23     1.44      2 1.60   2.40    1.39 
 9 nissan e         216      216      200      222         1 0.600  1.40  216    
10 toyota e         205      205      200      222         2 1.60   2.40  205    
11 nissan f           0.630    0.630    0.585    0.645     1 0.600  1.40    0.630
12 toyota f           0.600    0.600    0.585    0.645     2 1.60   2.40    0.600

这会产生条形,使得每个小平面中的较短条占据小平面高度的四分之一,而较高的条占据四分之三。如果两个杆的高度完全相同,则它们都占据了刻面高度的一半。如果你想调整外观,只需更改ymin / yblank。

简介:

ggplot(df2,
       aes(x = x, y = ymax, fill = brand)) + 
  geom_rect(aes(xmin = xmin, xmax = xmax,
                ymin = ymin, ymax = ymax)) +
  geom_text(aes(label = label), 
            vjust = -1) + # position labels slightly above top of each bar
  geom_blank(aes(y = yblank)) +
  facet_wrap(~ variable, scales = "free_y") + 
  scale_fill_brewer(palette = "Paired") + 
  theme(
    legend.position="top",
    strip.text.y = element_text(angle = 0),
    axis.text=element_blank(),
    axis.ticks = element_blank()
  )

plot

答案 2 :(得分:5)

我同意答案和评论,操纵相对条形长度不是一个好主意。如果您只想显示当前情节中哪个条形较高,您可以使用另一种美学来突出显示它,例如将较高的条形放在如下图所示的框中。

enter image description here

library(reshape2)
library(ggplot2)
library(ggthemes)
library(RColorBrewer)

library(data.table)
library(magrittr)


# add a winner column to mark the winner in red
aaa <- melt(df, id = "brand") %>%
    setDT() %>%
    .[, winner := ifelse(value > mean(value), "red", "NA"), by = variable]

# plot and show the higher bar in read box
ggplot(aaa, aes(brand, value, fill = brand)) + 
    geom_bar(aes(color = winner), stat = "identity", position='dodge') +
    geom_text(data=melt(df, id = "brand"), angle = 0, 
              aes(brand, value,
                  label = ifelse(value > 100, round(value, 0), value) ) ) +
    facet_wrap(~ variable, scales = "free_y") + 
    scale_fill_brewer(palette = "Paired") + 
    scale_color_identity() +
    theme(
        legend.position = "top",
        strip.text.y = element_text(angle = 0),
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks = element_blank()
    )

答案 3 :(得分:1)

在这个线程中指出的任意y值开始缩放都是误导。

尚未考虑的一个答案是使用scale_y_log10()将比例放在对数刻度上,并与geom_text标签结合使用以显示刻面之间的差异基团。

数据

df <- data_frame(
  pair = letters[1:6] %>% rep(2),
  brand = c("nissan", "toyota") %>% rep(each = 6),
  value = c(99.21, 99.78, 6496, 1.28, 216, 0.63, 
            99.03, 99.78, 7652, 1.39, 205, 0.60)
)

df %>% 
  ggplot(aes(x = brand, y = value)) +
  geom_col(aes(fill = brand)) +
  geom_text(aes(label = value), vjust = -1) +
  scale_y_log10() +
  facet_wrap(~pair) +
  theme_bw()

enter image description here