I'm trying to plot an age pyramid in R using ploty, and I also want to add a dropdown menu list to filter the plot by year.
Here's the code I'm using:
dadosbr <- data.frame(
SEXO = sample(c("Masculino", "Feminino"), 7525, replace = TRUE),
FAIXA_ETARIA = sample(c("0-19", "20-29", "30-39", "40-49", "50-59", "60 ou mais"), 7525, replace = TRUE),
ANO_TRAT = sample(2019:2024, 7525, replace = TRUE))
sexo <- dadosbr %>%
group_by(SEXO, FAIXA_ETARIA, ANO_TRAT) %>%
tally() %>%
ungroup()
sexo <- sexo %>%
group_by(ANO_TRAT) %>%
mutate(percent = round(n / sum(n) * 100,1))
sexo <- sexo %>%
mutate(percent = ifelse(SEXO == "Feminino", -percent, percent))
plot_ly(sexo, hoverinfo = 'text', textposition = "none",
text = ~paste('</br> Ano do Início do Tratamento: ', ANO_TRAT,
'</br> Sexo: ', SEXO,
'</br> Número de Casos de TBDR: ', n,
'</br> %: ', percent)) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2019, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2019', visible = TRUE) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2020, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2020', visible = FALSE) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2021, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2021', visible = FALSE) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2022, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2022', visible = FALSE) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2023, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2023', visible = FALSE) %>%
add_trace(data = sexo[sexo$ANO_TRAT == 2024, ],
x = ~FAIXA_ETARIA, split = ~SEXO, y = ~n, type = 'bar', name = '2024', visible = FALSE) %>%
layout(width = 820,
xaxis = list(title = "Raça/cor", linecolor = 'black', showline = TRUE, showgrid = FALSE),
yaxis = list(title = 'Número de Casos de TBR', showgrid = FALSE, zeroline = TRUE,
linecolor = 'black', range = c(0, 100)),
colorway = c("#4567a9", "#118dff", "#107dac", "#1ebbd7", "#064273", "#71c7ec"),
barmode = 'stack',
showlegend = FALSE,
updatemenus = list(
list(
active = 0,
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)),
label = "2019"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)),
label = "2020"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)),
label = "2021"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)),
label = "2022"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE)),
label = "2023"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, FALSE, FALSE, TRUE)),
label = "2024")
)
)
),
margin = list(l = 0, r = 0, b = 0, t = 0, pad = 0)
)
I don't know what's going wrong. I even tried to chage it to "stack", but it doesn't work. I want the output to be something like this:
I made this one with ggplot, but I don't know how to do it with plotly.
Here is a solution to what you've asked based on what I think you're looking for. (I used this Python answer to answer your question in R.)
Using the data as you've already structured it:
I used set.seed(23)
to create the data consistently when working through this answer (if you wanted to see the same results).
To align the male and female bars you need barmode = "relative"
.
To create horizontal bars, in the call for plot_ly
, add orientation = "h"
.
xaxis
and yaxis
in the call to layout()
To make the content you've assigned to text
hover content (a tooltip), change the assignment from text =
to hovertext =
. (Since you've set textposition = "none"
, I'm assuming that was your objective... although, your image shows text content...so...)
x, y
that shows at the top of each tooltip, as it is redundant, then assign this to hovertemplate
instead of hovertext
or text
. The last image in this answer has this change. (You can compare the tooltips in the last 2 images.)I'm not sure what the objective was with the color assignments. In your code you've set a color for what seems like each year, but your ideal (based on the image) has colors moderated by gender. Once you've used the buttons by year, colors by year is a bit redundant. Here is an example of how you could assign colors by gender, so that it matches the visual you've provided. If you wanted to stick to color by year let me know, and I'll add content on how to do it that way, as well.
I've added code to make the percentages that are negative for the sake of coding, positive values
ticktext
& tickvals
.hovertext =
, added abs()
to show absolute values. Otherwise, this content matches your original code assigned to text =
. (I annotated this change in my comments in the code, as well.)The code you've assigned to
buttons =
is identical to what I created withlst =
&lapply()
lst <- rep(F, length(unique(sexo$ANO_TRAT))) # visibility by trace
# create btns
btns <- lapply(1:length(lst), \(w) { # create buttons
lst[w] <- T # change one button's visibility
list(method = "restyle", label = sort(unique(sexo$ANO_TRAT))[w],
args = list("visible", lst))
})
# the plot
plot_ly(data = sexo, type = "bar", orientation = "h",
name = ~SEXO, color = ~SEXO, split = ~ANO_TRAT, # split traces by year
x = ~percent, y = ~FAIXA_ETARIA, # data to plot
# tooltip content
hovertext = ~paste('</br> Ano do Início do Tratamento: ', ANO_TRAT,
'</br> Sexo: ', SEXO,
'</br> Número de Casos de TBDR: ', n,
'</br> %: ', abs(percent))) %>% # <---- I added abs() here!
layout(xaxis = list(title = "Raça/cor",
tickmode = "array",
tickvals = seq(-10, 10, length.out = 5), # positive axis vals
ticktext = abs(seq(-10, 10, length.out = 5))),
# standoff = space btw title & labels
yaxis = list(title = list(text = 'Número de Casos de TBR', standoff = 20)),
barmode = "relative", # align the genders' bars horizontally
updatemenus = list(list(
pad = list(r = 55), # create space between graph & dropdown
showactive = T, # highlight visible trace's button
buttons = btns # from lapply
))) %>%
style(visible = F, traces = c(2:6, 8:12)) # set 2019 as only visible trace(s)
With the tooltip code set to hovertemplate
instead of hovertext
or text
, this is what it looks like:
If you have any questions or if there's anything else, let me know.