我正在 RMarkdown 中使用plotly 创建带有下拉菜单列表的交互式绘图来过滤年份。
我在尝试忽略每年的空类别时遇到了麻烦。我只想要每年的前 5 个类别。这是我正在使用的代码:
cid <- import("R:/COE/GIE/0 SERVIDORES/Lana Meijinhos/CID-10-SUBCATEGORIAS.CSV") %>%
select(SUBCAT, DESCRABREV)
malf <- nv %>%
filter(idanomal == "1")
malf$codanomal <- gsub("(.{4})", "*\\1", malf$codanomal)
malf_cid <- malf %>%
separate_rows(codanomal, sep = "\\*") %>%
filter(codanomal != "") %>%
mutate(codanomal = ifelse(codanomal == "Q699", "Q690", codanomal)) %>%
group_by(anonasc, codanomal) %>%
summarise(frequency = n(), .groups = "drop") %>%
arrange(anonasc, desc(frequency)) %>%
spread(., key=anonasc, value=frequency) %>%
adorn_totals("col")
malf_cid[is.na(malf_cid)] <- 0
res <- nv %>%
group_by(anonasc) %>%
tally %>%
spread(., key=anonasc, value=n)
colnames(res) <- paste0("nv", colnames(res))
malf_cid <- bind_cols(malf_cid, res)
malf_cid$tx20 <- round((malf_cid$"2020"/res$nv2020)*1000,1)
malf_cid$tx21 <- round((malf_cid$"2021"/res$nv2021)*1000,1)
malf_cid$tx22 <- round((malf_cid$"2022"/res$nv2022)*1000,1)
malf_cid$tx23 <- round((malf_cid$"2023"/res$nv2023)*1000,1)
malf_cid$tx24 <- round((malf_cid$"2024"/res$nv2024)*1000,1)
malf_cid <- malf_cid %>%
left_join(cid, by = c("codanomal" = "SUBCAT")) %>%
select(-codanomal) %>%
rename("codanomal" = "DESCRABREV") %>%
arrange(desc(Total))
graf <- malf_cid %>%
select(codanomal, "2020":"2024") %>%
pivot_longer(cols = starts_with("20"), names_to = "year", values_to = "n")
graf2 <- malf_cid %>%
select(codanomal, tx20:tx24) %>%
pivot_longer(cols = starts_with("tx"), names_to = "year", values_to = "frequency")
graf2$year <- ifelse(graf2$year == "tx20", "2020",
ifelse(graf2$year == "tx21", "2021",
ifelse(graf2$year == "tx22", "2022",
ifelse(graf2$year == "tx23", "2023",
ifelse(graf2$year == "tx24", "2024", NA)))))
graf3 <- malf_cid %>%
select(codanomal, nv2020:nv2024) %>%
pivot_longer(cols = starts_with("nv"), names_to = "year", values_to = "nv")
graf3$year <- ifelse(graf3$year == "nv2020", "2020",
ifelse(graf3$year == "nv2021", "2021",
ifelse(graf3$year == "nv2022", "2022",
ifelse(graf3$year == "nv2023", "2023",
ifelse(graf3$year == "nv2024", "2024", NA)))))
graf <- graf %>%
left_join(graf2, by = c("year", "codanomal")) %>%
left_join(graf3, by = c("year", "codanomal"))
graf <- graf %>%
group_by(year) %>%
mutate(codanomal = reorder(codanomal, frequency)) %>%
ungroup() %>%
mutate(frequency = ifelse(frequency == 0, NA, frequency),
n = ifelse(n == 0, NA, n),
nv = ifelse(is.na(n), NA, nv),
year = ifelse(is.na(n), NA, year))
fig <- plot_ly() %>%
add_trace(data = graf %>% filter(year == "2020" & frequency > 0) %>% arrange(desc(frequency)) %>% slice(1:5),
x = ~frequency,
y = ~codanomal,
type = 'bar',
name = '2020',
hoverinfo = 'text',
textposition = "none",
text = ~paste('</br> Ano do Nascimento: ', year,
'</br> Causa: ', codanomal,
'</br> Número de Anomalias: ', n,
'</br> Número de Nascidos Vivos: ', nv,
'</br> Prevalência: ', frequency),
visible = TRUE) %>%
add_trace(data = graf %>% filter(year == "2021" & frequency > 0) %>% arrange(desc(frequency)) %>% slice(1:5),
x = ~frequency,
y = ~codanomal,
type = 'bar',
name = '2021',
hoverinfo = 'text',
textposition = "none",
text = ~paste('</br> Ano do Nascimento: ', year,
'</br> Causa: ', codanomal,
'</br> Número de Anomalias: ', n,
'</br> Número de Nascidos Vivos: ', nv,
'</br> Prevalência: ', frequency),
visible = FALSE) %>%
add_trace(data = graf %>% filter(year == "2022" & frequency > 0) %>% arrange(desc(frequency)) %>% slice(1:5),
x = ~frequency,
y = ~codanomal,
type = 'bar',
name = '2022',
hoverinfo = 'text',
textposition = "none",
text = ~paste('</br> Ano do Nascimento: ', year,
'</br> Causa: ', codanomal,
'</br> Número de Anomalias: ', n,
'</br> Número de Nascidos Vivos: ', nv,
'</br> Prevalência: ', frequency),
visible = FALSE) %>%
add_trace(data = graf %>% filter(year == "2023" & frequency > 0) %>% arrange(desc(frequency)) %>% slice(1:5),
x = ~frequency,
y = ~codanomal,
type = 'bar',
name = '2023',
hoverinfo = 'text',
textposition = "none",
text = ~paste('</br> Ano do Nascimento: ', year,
'</br> Causa: ', codanomal,
'</br> Número de Anomalias: ', n,
'</br> Número de Nascidos Vivos: ', nv,
'</br> Prevalência: ', frequency),
visible = FALSE) %>%
add_trace(data = graf %>% filter(year == "2024" & frequency > 0) %>% arrange(desc(frequency)) %>% slice(1:5),
x = ~frequency,
y = ~codanomal,
type = 'bar',
name = '2024',
hoverinfo = 'text',
textposition = "none",
text = ~paste('</br> Ano do Nascimento: ', year,
'</br> Causa: ', codanomal,
'</br> Número de Anomalias: ', n,
'</br> Número de Nascidos Vivos: ', nv,
'</br> Prevalência: ', frequency),
visible = FALSE) %>%
layout(width = 820,
yaxis = list(title = " ", linecolor = 'black'),
xaxis = list(side = 'bottom', title = 'Prevalência de Malformação Congênita (/1.000 nascidos vivos)', showgrid = F, zeroline = T,
linecolor = 'black', range = c(0, max(graf$frequency)+2)),
colorway = c("#4567a9", "#118dff", "#107dac", "#1ebbd7", "#064273"),
showlegend = F,
margin = list(l = 0, r = 0, b = 0, t = 0, pad = 0), # Adjusted to remove margins
xaxis = list(
showline = TRUE, # Added to show x-axis line
showgrid = FALSE # Added to hide x-axis grid
),
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, FALSE, FALSE, FALSE, FALSE)),
label = "2020"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE, FALSE, FALSE, FALSE)),
label = "2021"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, TRUE, FALSE, FALSE)),
label = "2022"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, TRUE, FALSE)),
label = "2023"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, FALSE, TRUE)),
label = "2024")
),
direction = "down",
pad = list(r = 10, t = 10),
showactive = TRUE,
x = -0.4,
xanchor = "left",
y = 1.1,
yanchor = "top"
)
)
)
fig
这是它为 2024 年生成的图,例如:
我只想保留实际有条形的类别并忽略空的条形。我已经尝试了一切,但似乎没有任何效果。
有什么建议吗?
有几种方法可以实现此目的,但由于您使用了按钮的可见性,因此我们将从该方法开始。
如果我想让第一个按钮仅显示
y
中 not 空的条目,我必须定义 categoryarray
,它位于 layout(yaxis = list(categoryarray =...
内。
类别数组是您在 y 轴上列出的每个唯一值的列表 - 您的
codanomal
。
可见性的控制位于
restyle
的权限内,正如您在按钮中所标识的那样。但是,categoryarray
位于relayout
之下,因此要更新两者,您需要使用方法update
。
使用
update
与其他方法的主要区别在于,args
中至少需要两个列表:一个用于 restyle
的元素列表,另一个用于 relayout
的元素列表。当然,其中可以嵌套许多列表。
参数将如下所示:
args = list(list(visible = ...),
list(yaxis = list(categoryarray = ...)))
或者就您的代码而言,它看起来像这样(其中
dta
是数据):
args = list(list("visible", list(TRUE, FALSE, FALSE, FALSE, FALSE)),
list(yaxis = list(categoryarray = unique(dta[dta$year == 2020,]$codanomal))))
特定于您的代码的更多详细信息位于本答案的末尾。
如果没有可重现的问题,我不一定能给你准确的答案。为了让事情变得更简单,我创建了一些简单的数据作为示例,以小得多的规模模拟您在那里发生的事情。
library(tidyverse)
library(plotly)
set.seed(35446)
dta <- data.frame(
animals = sample(c("penguin", "dolphin", "dolphin", "horse", "cat"), 25, replace = T),
consumed = sample(10:1000, 100, replace = T),
year = sample(2020:2024, 100, replace = T)
) %>% arrange(year, desc(animals)) # just for plotly -- keep it in order! (sigh)
我可以单独绘制每年,但只有当我不想让图表同时显示所有年份时,我才会这样做。如果我只想一次显示一年,我会使用
add_trace
。
在此示例中,
plot_ly()
是可见的迹线,其中 add_trace()
包含其余不可见的数据。之所以仍然使用 visible
作为按钮参数,是因为参数 split
。
plot_ly(filter(dta, year == min(dta$year)), type = "bar", visible = T,
x = ~consumed, y = ~animals, split = ~year, showlegend = F) %>%
add_trace(inherit = F,
data = filter(dta, year != min(dta$year)), type = "bar", visible = F,
x = ~consumed, y = ~animals, split = ~year, showlegend = F)
为了制作按钮,我可以写出每个按钮,但我不必这样做。
我将使用
purrr
的 imap()
这样我就能得到我正在排序的内容和迭代次数。我将对年份进行排序,因为这就是按钮分割数据的方式。
我知道我有 5 条迹线(通常是 5 种颜色 == 5 条迹线)。这意味着我需要为每一个分配可见性(正如您在按钮中所做的那样)。我还需要每个按钮的
categoryarray
。
btns <- imap(unique(sort(dta$year)), \(j, k) {
vis <- rep(F, 5) # create an array of F for each trace
vis[k] <- T # change the current iteration to TRUE
dtb <- dta %>% filter(year == j) # identify the trace data
list(method = "update", label = as.character(j), # year as the label
args = list(list(visible = as.list(vis)), # visibility; restyle args
# only the categories on this data; relayout args
list(yaxis = list(categoryarray = unique(dtb$animals))))
)
})
接下来是绘图和按钮的组装。
plot_ly(filter(dta, year == min(dta$year)), type = "bar", visible = T,
x = ~consumed, y = ~animals, split = ~year, showlegend = F) %>%
add_trace(inherit = F,
data = filter(dta, year != min(dta$year)), type = "bar", visible = F,
x = ~consumed, y = ~animals, split = ~year, showlegend = F) %>%
layout(updatemenus = list(list(buttons = btns)))
顺便说一句:
您在代码中准备数据的方式看起来好像您也可以按如下方式总结数据,其中消耗的数据代表您的数据中的
frequency
。
dta %>% filter(consumed > 0) %>% group_by(year) %>%
arrange(desc(consumed)) %>% slice(1:5)
正如我所说,如果没有可重现的问题,我无法确定下一段代码是否会达到我的预期。然而,我非常有信心这可以取代你所有的情节调用
话虽这么说,我遗漏了你对
hovertext
的呼吁。你期待这个做什么?它正在做你所期望的吗?我已将其替换为 hovertemplate
以及您在悬停内容中想要的 我认为。
dta <- filter(graf, frequency > 0) %>% group_by(year) %>%
arrange(desc(frequency)) %>% slice(1:5)
btns <- imap(unique(sort(dta$year)), \(j, k) {
vis <- rep(F, 5) # create an array of F for each trace
vis[k] <- T # change the current iteration to TRUE
dtb <- dta %>% filter(year == j) # identify the trace data
list(method = "update", label = as.character(j), # year as the label
args = list(list(visible = as.list(vis)), # visibility; restyle args
# only the categories on this data; relayout args
list(yaxis = list(categoryarray = unique(dtb$codanomal))))
)
})
plot_ly(type = "bar",
data = filter(dta, year == min(dta$year)),
name = ~year, x = ~frequency, y = ~codanomal,
customdata = ~pmap(list(year, n, nv), list) # connect the data
hovertemplate = paste0('Ano do Nascimento: %{customdata[0]}', # year
'</br> Causa: %{y}', # codanomal in {y}
'</br> Número de Anomalias: %{customdata[1]}', # n
'</br> Número de Nascidos Vivos: %{customdata[2]}', # nv
'</br> Prevalência: %{x}'), # frequency in {x}
visible = T) %>%
add_trace(inherit = F, type = "bar",
data = filter(dta, year != min(dta$year)),
name = ~year, x = ~frequency, y = ~codanomal,
customdata = ~pmap(list(year, n, nv), list) # connect the data
hovertemplate = paste0('Ano do Nascimento: %{customdata[0]}', # year
'</br> Causa: %{y}', # codanomal in {y}
'</br> Número de Anomalias: %{customdata[1]}', # n
'</br> Número de Nascidos Vivos: %{customdata[2]}', # nv
'</br> Prevalência: %{x}'), # frequency in {x}
visible = T) %>%
layout(xaxis = list(title = 'Prevalência de Malformação Congênita (/1.000 nascidos vivos)',
showgrid = F, showline = T),
yaxis = list(showgrid = F, showline = T),
colorway = c("#4567a9", "#118dff", "#107dac", "#1ebbd7", "#064273"),
showlegend = F,
margin = list(0),
updatemenus = list(list(
buttons = btns,
direction = "down",
pad = list(r = 10, t = 10), showactive = TRUE,
x = -0.4, xanchor = "left", y = 1.1, yanchor = "top"
))
)