livebook-dev / vega_lite

Elixir bindings for Vega-Lite

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Determine length and width of bar mark/smbol in vega lite

saurabh-ironman opened this issue · comments

Hi,

Is there a way to find width and length of bar chart in vegalite? Please find below explanation on why i need it:

If you refer "The Parts of Box Plots" section in below link then it tells us that boxplot mark is a composite mark that means made up of basic marks like bar, line, point etc.
https://vega.github.io/vega-lite/docs/boxplot.html#parts

This is where am trying to draw my own box plot instead of using inbuilt :boxplot mark. Please find below an example made up of dummy data.

Here am able to draw everything in livebook except the white line in bar chart. This line represents median inside bar and i want this line to go all the way up and down and touch the boundaries of bar on both side like we get in inbuilt boxplot.

basically i need help with highlighted section in below to dynamically get the height and length of bar and then accordingly draw the median line inside bar. Curious to know how inbuild boxplot mark does this?

Example:

Mix.install([
{:vega_lite, "> 0.1.0"},
{:kino, "
> 0.1.0"}
])
alias VegaLite, as: Vl

data = [
%{
"atl_iqr" => 3.6189,
"atl_lo_outlier" => 1,
"atl_lo_whisker" => 3.176,
"atl_median" => 8.894,
"atl_q1" => 8.605,
"atl_q3" => 12.224,
"atl_up_outlier" => 20,
"atl_up_whisker" => 17.6525
}
]

Vl.new(height: 480, width: 500, title: "Composite Boxplot W ATLExecution & CPUUsage")
|> Vl.layers([
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:bar, tooltip: true)
|> Vl.encode(:size, value: 20)
|> Vl.encode_field(:x, "atl_q1", type: :quantitative, title: "ATLExecutionTime")
|> Vl.encode_field(:x2, "atl_q3"),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:tick, color: :white, size: 10, tooltip: true)
|> Vl.encode_field(:x, "atl_median", type: :quantitative),

Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:rule, size: 2, ticks: true, tooltip: true)
|> Vl.encode_field(:x, "atl_q1", type: :quantitative)
|> Vl.encode_field(:x2, "atl_lo_whisker"),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:rule, size: 2, ticks: true, tooltip: true)
|> Vl.encode_field(:x, "atl_q3", type: :quantitative)
|> Vl.encode_field(:x2, "atl_up_whisker"),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:tick, color: :black, size: 16, thickness: 2, tooltip: true)
|> Vl.encode_field(:x, "atl_lo_whisker", type: :quantitative),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:tick, color: :black, size: 16, thickness: 2, tooltip: true)
|> Vl.encode_field(:x, "atl_up_whisker", type: :quantitative),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:point, color: :red, tooltip: true)
|> Vl.encode_field(:x, "atl_lo_outlier", type: :quantitative),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:point, color: :red, tooltip: true)
|> Vl.encode_field(:x, "atl_up_outlier", type: :quantitative)
])

Hey @saurabh-ironman! In your specification you have a custom size for the bar |> Vl.encode(:size, value: 20) and a custom size for the white tick |> Vl.mark(:tick, color: :white, size: 10, tooltip: true). I believe that if you use the same size for both it will do what you want :)

sure @jonatanklosko this is what i have been playing around but right now it is hard coded and which i don't want to do. So if i remove |> Vl.encode(:size, value: 20) line then what should be the value to rely on? is there a way to get this?
Also am curious how already defined boxplot mark handles this?

Without explicit size there would just be some default value, as the size doesn't depend on the data, right? So in that case I think it's fine to just pick some arbitrary size, or make it a function and make the size configurable.

As for the built-in boxplot composite mark, that's exactly what they do. This looks like the relevant part, they have size as the boxplot parameter and it's reflected in both the bar and the tick.

sure @jonatanklosko This makes sense, thanks for pointing to the code :) am sorry but i have one more follow up query here in case of a 2 Dimensional boxplot, this logic may not work.
Please refer below sample example which i tried. here in this example i have 2 set of data one corresponds to horizontal bar and other one corresponds to vertical bar and they both are combined to get a 2 dimensional view.
So for this example setting same value may not help right?

data = [
%{
"atl_median" => 8.894,
"atl_q1" => 8.605,
"atl_q3" => 12.224,

"cpu_median" => 25.0, 
"cpu_q1" => 22.0, 
"cpu_q3" => 30.0,

}
]

Vl.new(height: 480, width: 500, title: "Composite Boxplot W ATLExecution & CPUUsage")
|> Vl.layers([
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:bar, tooltip: true)
|> Vl.encode(:size, value: 20)
|> Vl.encode_field(:x, "atl_q1", type: :quantitative, title: "ATLExecutionTime")
|> Vl.encode_field(:x2, "atl_q3")
|> Vl.encode_field(:y, "cpu_q1", type: :quantitative, title: "ATL CPU Usage")
|> Vl.encode_field(:y2, "cpu_q3"),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:tick, color: :white, size: 20, tooltip: true, orient: :horizontal)
|> Vl.encode_field(:x, "atl_median", type: :quantitative)
|> Vl.encode_field(:y, "cpu_median", type: :quantitative),
Vl.new()
|> Vl.data_from_values(data)
|> Vl.mark(:tick, color: :white, size: 20, tooltip: true)
|> Vl.encode_field(:y, "cpu_median", type: :quantitative)
|> Vl.encode_field(:x, "atl_median", type: :quantitative)
])

Refer attached screen shot for visualization...
image

Oh, in the 2D case we have the exact locations so we can use the rule mark instead!

Vl.new(height: 480, width: 500, title: "Composite Boxplot W ATLExecution & CPUUsage")
|> Vl.layers([
  Vl.new()
  |> Vl.data_from_values(data)
  |> Vl.mark(:bar, tooltip: true)
  |> Vl.encode_field(:x, "atl_q1", type: :quantitative, title: "ATLExecutionTime")
  |> Vl.encode_field(:x2, "atl_q3")
  |> Vl.encode_field(:y, "cpu_q1", type: :quantitative, title: "ATL CPU Usage")
  |> Vl.encode_field(:y2, "cpu_q3"),
  Vl.new()
  |> Vl.data_from_values(data)
  |> Vl.mark(:rule, color: :white, tooltip: true)
  |> Vl.encode_field(:x, "atl_median", type: :quantitative)
  |> Vl.encode_field(:y, "cpu_q1", type: :quantitative)
  |> Vl.encode_field(:y2, "cpu_q3", type: :quantitative),
  Vl.new()
  |> Vl.data_from_values(data)
  |> Vl.mark(:rule, color: :white, tooltip: true)
  |> Vl.encode_field(:y, "cpu_median", type: :quantitative)
  |> Vl.encode_field(:x, "atl_q1", type: :quantitative)
  |> Vl.encode_field(:x2, "atl_q3", type: :quantitative),
])

perfect make sense thank you so much @jonatanklosko . Am good now... Appreciate your response :)

Surething, happy to help :)