-->

ggvis layer_bars and layer_lines on different y-ax

2019-08-02 03:26发布

问题:

I would like to create a plot with categorical x values and lines and bars.

The example data is as follows:

dataWide <- iris %>%
  group_by(Species) %>%
  summarize(sl = mean(Sepal.Length),
            sw = mean(Sepal.Width),
            pl = mean(Petal.Length),
            pw = mean(Petal.Width))

# Source: local data frame [3 x 5]
# 
# Species    sl    sw    pl    pw
# 1     setosa 5.006 3.428 1.462 0.246
# 2 versicolor 5.936 2.770 4.260 1.326
# 3  virginica 6.588 2.974 5.552 2.026

I want to have:

  1. setosa, versicolor, and virginica on the x-axis. three lines representing sl, sw, and pl, i.e. layer_lines with stroke = ~Species
  2. a layer_bars on a different scale, with axis label on the right.

I'm having trouble putting together the different functions together. Here's my failed attempt:

library(ggvis)
library(tidyr)
library(dplyr)

long <- dataWide %>%
  select(Species, sl, sw, pl) %>%
  gather(variable, value, sl : pl)

longPw <- dataWide %>% select(Species, pw) %>%
  gather(variable, value, pw)

long %>%
  ggvis(x = ~Species, y = ~value, stroke = ~variable) %>%
  layer_lines() %>%
  add_axis("y", "ypw", orient = "right", title = "PW", grid = F) %>%
  layer_bars(x = ~Species, y = ~value, data = longPw, scale = "ypw")

回答1:

You can do it with the following code only, and as you will see there is a limitation which there is no solution for unfortunately:

First of all in order for this to work you need to use ONLY one dataset which will have all of the information you need. Then you use the following code:

Preparation of Data

dataWide <- iris %>%
  group_by(Species) %>%
  summarize(sl = mean(Sepal.Length),
            sw = mean(Sepal.Width),
            pl = mean(Petal.Length),
            pw = mean(Petal.Width))

library(ggvis)
library(tidyr)
library(dplyr)
long <- dataWide %>%
  select(Species, sl, sw, pl) %>%
  gather(variable, value, sl : pl)

longPw <- dataWide %>% select(Species, pw) %>%
  gather(variable, value, pw)

Have all the information you need for the plot in only one data set i.e. dataWide2:

dataWide2 <- cbind(dataWide, longPw[2:3]) 

Solution

dataWide2 %>%
  #start with barchart
  ggvis(x = ~Species, y = ~value) %>%
  layer_bars(opacity := 0.4) %>%

  #details for right axis i.e. the bars
  add_axis("y", orient = "right", title = "My bars" ,title_offset = 50) %>% 

  #details for left axis i.e. the lines + plotting of lines 
  add_axis("y", 'ylines' , orient = "left", title= "My lines" , grid=F ) %>%
  layer_lines(stroke := 'red',   prop('y', ~sl, scale='ylines')) %>%
  layer_lines(stroke := 'blue',  prop('y', ~sw, scale='ylines')) %>%
  layer_lines(stroke := 'green', prop('y', ~pl, scale='ylines')) 

Few words for the above code:

  • First of all you NEED to start with the barchart. I don't know why this happens but the opposite will produce an error.
  • Secondly you need to create the lines separately since you are using one data set and set the colours individually.

Limitation

As you can see in the graph below the bar chart and the lines are not aligned which cannot be fixed so far unfortunately (at least to my current knowledge - check the Edit below). This limitation is actually the reason I made an account on stack overflow in the first place. If you check my profile this is my one and only question.

Hope it helps :)

EDIT

I don't know if this post is the reason for the bug report on ggvis on the github page but it was reported as a bug a few hours ago.

UPDATE

After a bit of researching and a bit of hacking I figured out a workaround which is as follows:

dataWide2 %>%
  #start with barchart
  ggvis(x = ~as.numeric(Species), y = ~value) %>%
  layer_bars(opacity := 0.4) %>%

  #add the initial x axis in order to set x labes to blank
  add_axis('x', title='Species', properties = axis_props(labels=list(fill='blank'))) %>%


  #details for right axis i.e. the bars
  add_axis("y", orient = "right", title = "My bars" ,title_offset = 50) %>% 

  #details for left axis i.e. the lines + plotting of lines 
  add_axis("y", 'ylines' , orient = "left", title= "My lines" , grid=F ) %>%
  layer_lines(stroke := 'red',   prop('y', ~sl, scale='ylines')) %>%
  layer_lines(stroke := 'blue',  prop('y', ~sw, scale='ylines')) %>%
  layer_lines(stroke := 'green', prop('y', ~pl, scale='ylines')) %>%

  #add new axis which will be for our categorical x axis
  add_axis('x', 'myx2', orient='bottom', title='') %>%

  #add categorical data and make lines invisible (we only need the categories anyway)
  layer_lines(prop("x", ~ Species, scale = "myx2"), stroke := 'blank') 

The data set is exactly the same and essentially in order to have aligned bars and lines the bars need to have a numeric x axis. I therefore, created a second overlapping x axis which hosts the categorical data.

The output:

I suppose you could set the grid=F and remove some of the ticks you don't want but this is as good as it can get. Hope it helps!

P.S. you can control the width of the bars with the width argument in layer_bars.



标签: r ggvis