# Proportion plot in CairoMakie

**URL:** <https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743>\
**Category:** Visualization\
**Created:** [October 9, 2023, 6:38am UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743 "2023-10-09T06:38:28Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![alicepiller](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@alicepiller](https://discourse.julialang.org/u/alicepiller)\
**Post date:** [October 9, 2023, 6:38am UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743/1 "2023-10-09T06:38:28Z")

</div>

I am trying to make a proportion plot, where the x-axis is the year (2000 - 2004) and the y-axis is from 0 - 1. Each bar has a height of 1 and should be stacked to show the proportion of the different health score categories (1 - 6) per year.

I have been using CairoMakie for other visualisations, but I can’t find a way to make a proportion plot of my description. If there is a way using CairoMakie, then that would be preferable, but other packages are also fine.

Also, is there a way to derive the proportions column without so many steps?

```julia
using DataFrames

years = [2000, 2000, 2000, 2000, 2000, 
        2001, 2001, 2001, 2001, 2001, 2001,
        2002, 2002, 2002, 2002, 2002, 2002, 2002,
        2003, 2003, 2003, 2003, 2003, 
        2004, 2004, 2004, 2004, 2004, 2004]

health_scores = [3, 2, 3, 5, 3, 
                2, 5, 1, 6, 5, 6,
                3, 2, 3, 4, 5, 1, 1,
                3, 1, 4, 6, 2, 
                4, 3, 3, 2, 2, 3]

df = DataFrame(year = years, health = health_scores)

# Adding column for the number of observations in each year
df2 = combine(groupby(df, :year), :health, :year => length)

# Adding number of count per health score category per year
df3 = combine(groupby(df2, [:year, :health]), :health => length, :year_length)

# Adding the proportions of health score categories per year
proportions = df3.health_length./df3.year_length
df4 = hcat(df3, proportions)
rename!(df4, :x1 => :proportions)

> df4
29×5 DataFrame
 Row │ year health health_length year_length proportions 
     │ Int64 Int64 Int64 Int64 Float64
─────┼────────────────────────────────────────────────────────
   1 │ 2000 2 1 5 0.2
   2 │ 2000 3 3 5 0.6
   3 │ 2000 3 3 5 0.6
  ⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮
  28 │ 2004 3 3 6 0.5
  29 │ 2004 4 1 6 0.166667

```

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [October 9, 2023, 8:35am UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743/2 "2023-10-09T08:35:30Z")

</div>

DataFrames has a function `proprow` to get the proportion of rows in each group. In your case, you want the proportion of each `health` group, for each `year` group, so you need to call `groupby` on each year group. It’s probably best to define a helper function for that:

```julia
using DataFrames
using CairoMakie

year = [2000, 2000, 2000, 2000, 2000, 
        2001, 2001, 2001, 2001, 2001, 2001,
        2002, 2002, 2002, 2002, 2002, 2002, 2002,
        2003, 2003, 2003, 2003, 2003, 
        2004, 2004, 2004, 2004, 2004, 2004]

health = [3, 2, 3, 5, 3, 
          2, 5, 1, 6, 5, 6,
          3, 2, 3, 4, 5, 1, 1,
          3, 1, 4, 6, 2, 
          4, 3, 3, 2, 2, 3]

df = DataFrame(; year, health)

proportions(tbl) = combine(groupby(DataFrame(tbl), :health), proprow => :proportion)

df2 = combine(groupby(df, :year), AsTable(:) => proportions => AsTable)

```

Result:

```julia
20×3 DataFrame
 Row │ year health proportion 
     │ Int64 Int64 Float64    
─────┼───────────────────────────
   1 │ 2000 2 0.2
   2 │ 2000 3 0.6
   3 │ 2000 5 0.2
   4 │ 2001 1 0.166667
...

```

(Instead of `proprow` you could also use `proportions` or `proportionmap` from StatsBase.jl)

For plotting the bars it’s easy if you don’t need a legend:

```julia
barplot(df2.year, df2.proportion, stack=df2.health, color=df2.health)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/d/8/d8467b213fd3a3d15fae9419320efd354b3563da.png)

Getting the legend however seems complicated, based on an [example](https://docs.makie.org/stable/reference/plots/barplot/) from the documentation:

```julia
colors = Makie.wong_colors()
fig, = barplot(df2.year, df2.proportion, stack=df2.health, color=colors[df2.health])
labels = string.(1:maximum(df2.health))
elements = [PolyElement(polycolor=colors[i]) for i in 1:length(labels)]
Legend(fig[1,2], elements, labels, "Health")
fig

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/d/0/d0dfae065456404641af7efdfa29b687f074197b.png)

Maybe @jules or @sdanisch can say if there’s a simpler way to get the legend.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [October 9, 2023, 9:32am UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743/3 "2023-10-09T09:32:48Z")

</div>

Not really, yet. In the future we’ll have a stricter categorical colormap type with which it will make sense to add legend overloads. Right now this is too intermingled with how continuous color works.

---

<div class="post-metadata">

**Author:** ![brucala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brucala/32/14229_2.png) [@brucala](https://discourse.julialang.org/u/brucala)\
**Post date:** [October 9, 2023, 11:10am UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743/4 "2023-10-09T11:10:43Z")

</div>

> [@alicepiller](#):
>
> If there is a way using CairoMakie, then that would be preferable, but other packages are also fine.

In case you are interested about solutions from other packages. Here is a solution for [Deneb.jl](https://github.com/brucala/Deneb.jl) using your original `df` dataframe:

```julia
Data(df) * Mark(:bar) * Encoding(
    x=:year, 
    y=field("count(health)", stack=:normalize), 
    color=:health
)

```

![image](https://global.discourse-cdn.com/julialang/original/3X/f/5/f53630bd9625be00f2826b62858239c65e98f659.png)

See also [this example](https://brucala.github.io/Deneb.jl/dev/examples/single_view_plots/bar_charts/normalized_bar_chart/#Normalized-bar-chart) in the documentation gallery.

---

<div class="post-metadata">

**Author:** ![alicepiller](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@alicepiller](https://discourse.julialang.org/u/alicepiller)\
**Post date:** [October 16, 2023, 1:01pm UTC](https://discourse.julialang.org/t/proportion-plot-in-cairomakie/104743/5 "2023-10-16T13:01:16Z")

</div>

Thank you! These were very helpful!
