I'm trying to use R plot_ly to create a line chart which lines can be toggled on and off using filter buttons that filter along "Product" and "Chip_type".
The idea is that suppliers ("Supplier"/"Supplier_text") supply different kinds of chips ("Chip_type") monthly ("Date") for different product segments of a company ("Product"). To get an overview over the top suppliers, I would like to draw one line per supplier, with the "Supplier_text" displayed in the legend, legend entries sorted descendingly by the abs(number) displayed in front of the "Supplier_text". The data tibble is sorted correctly in that regard.
The "Overall" entries refer to the sum of all suppliers for that product.
The full data set is to be found at the end of the post.
sample from dat :
Date(chr) Supplier Supplier_text order(int) Chip_type Product n(chr)
1 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Micro Smartphones 106
2 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Nano Smartphones 16920
3 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 BiMech Smartphones 61216
4 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Titan Smartphones 363698
5 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Quantum Smartphones 50797
6 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Platinum Smartphones 52715
7 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 PlainChip Smartphones 174342
8 2019-11 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Classic Smartphones 9319
9 2019-12 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Micro Smartphones 92
10 2019-12 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 Nano Smartphones 16928
11 2019-12 Overall Smartphones 94757 | 17.9% - Overall Smartphones 1 BiMech Smartphones 40920
17 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Micro Monitors 3
18 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Nano Monitors 1536
19 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 BiMech Monitors 6793
20 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Titan Monitors 45146
21 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Quantum Monitors 7922
22 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Platinum Monitors 5359
23 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 PlainChip Monitors 27390
24 2019-11 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Classic Monitors 1131
25 2019-12 Overall Monitors -33239 | -37.8% - Overall Monitors 2 Micro Monitors 12
33 2019-11 A -17385 | -88.0% - A 3 Titan Smartphones 3619
34 2019-11 A -17385 | -88.0% - A 3 Platinum Smartphones 13
35 2019-11 A -17385 | -88.0% - A 3 Quantum Smartphones 2
To keep the order (and later be able to toggle the correct lines!) I'm looping to add traces to an empty plot_ly object like this:
library(stringr)
library(dplyr)
library(plotly)
# "Rebuilding" the data frame as the loop runs to see if what the loop does to the traces ends up being the same (order) as the original data frame. For that, I create an empty object first:
dat_plotly_object_copy = c()
plotly_object <- plot_ly()
id = 1
# I loop along "order", which marks all data of a single supplier:
for(id in 1:max(dat$order)){
dat_one_supplier <- filter(dat, order == id)
plotly_object <- plotly_object %>% add_trace(., data = dat_one_supplier,
# I filter the data set by supplier, to be able to create a line along the dates (~x) per supplier (~Supplier_text) and Chip_type (~n):
x = ~Date,
y = ~n,
color = ~Supplier_text,
type = "scatter",
mode = "lines")
dat_plotly_object_copy <- dat_plotly_object_copy %>%
rbind(.,dat_one_supplier)
}
identical(dat, dat_plotly_object_copy)
# The created data frame seems to be identical to what the loop does - so the order should match (?)
Using this code to set the legend...
Parts_legend <- list(
font = list(
family = "sans-serif",
size = 12,
color = "#000"),
title = list(text="<b> Delta previous month by Supplier - Absolute </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2,
layout.legend = "constant",
traceorder = "grouped")
.. and showing the object:
plotly_object %>%
layout(legend = Parts_legend,
title = "by supplier delta previous month",
xaxis = list(title = 'Date'),
yaxis = list(title = 'Chip Volume'))
Leaves me with the following chart, which seems correct: Suppliers are entered by the abs(number) preceding the name!
[1]: https://i.stack.imgur.com/bDTWZ.png
Now I will need to add the buttons. In the first step, I create two data frames that are supposed to indicate, if a line will later be visible (TRUE) nor not (FALSE).
I seek to create them in the same format like dat - so that I get a TRUE or FALSE for every line of dat/the values the filtered variable can take:
Parts_product_filter <- select(dat,Supplier_text,order,Product,Chip_type) %>%
mutate(Smartphones = ifelse(Product == "Smartphones",T,F) %>% sapply(.,list),
TVs = ifelse(Product == "TVs",T,F) %>% sapply(.,list),
Monitors = ifelse(Product == "Monitors",T,F) %>% sapply(.,list),
Miscellaneous = ifelse(Product == "Miscellaneous",T,F) %>% sapply(.,list))
Parts_chip_type_filter <- select(dat,Supplier_text,order,Product,Chip_type) %>%
mutate(Micro = ifelse(Chip_type == "Micro",T,F) %>% sapply(.,list),
Nano = ifelse(Chip_type == "Nano",T,F) %>% sapply(.,list),
BiMech = ifelse(Chip_type == "BiMech",T,F) %>% sapply(.,list),
Titan = ifelse(Chip_type == "Titan",T,F) %>% sapply(.,list),
Quantum = ifelse(Chip_type == "Quantum",T,F) %>% sapply(.,list),
Platinum = ifelse(Chip_type == "Platinum",T,F) %>% sapply(.,list),
PlainChip = ifelse(Chip_type == "PlainChip",T,F) %>% sapply(.,list),
Classic = ifelse(Chip_type == "Classic",T,F) %>% sapply(.,list))
Adding the buttons to the plotly_object, I try to set them so that they filter based on the individual columns of the "_filter" data frames created above:
plotly_object %>%
layout(legend = Parts_legend,
title = "by supplier delta previous month",
xaxis = list(title = 'Date'),
yaxis = list(title = 'Chip Volume'),
updatemenus = list(
list(
active = 0,
type = "dropdown",
y = 1.1,
direction = "right",
# See from here:
buttons = list(
list(label = "All",
method = "restyle",
args = list("visible",T)),
list(label = "Smartphones",
method = "restyle",
args = list("visible",Parts_product_filter$Smartphones)),
list(label = "TVs",
method = "restyle",
args = list("visible",Parts_product_filter$TVs)),
list(label = "Monitors",
method = "restyle",
args = list("visible",Parts_product_filter$Monitors)),
list(label = "Miscellaneous",
method = "restyle",
args = list("visible",Parts_product_filter$Miscellaneous))
)
),
list(
active = 0,
type = "dropdown",
y = 1.03,
direction = "right",
buttons = list(
list(label = "All",
method = "restyle",
args = list("visible",T)),
list(label = "Micro",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Micro)),
list(label = "Nano",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Nano)),
list(label = "BiMech",
method = "restyle",
args = list("visible",Parts_chip_type_filter$BiMech)),
list(label = "Titan",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Titan)),
list(label = "Quantum",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Quantum)),
list(label = "Platinum",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Platinum)),
list(label = "PlainChip",
method = "restyle",
args = list("visible",Parts_chip_type_filter$PlainChip)),
list(label = "Classic",
method = "restyle",
args = list("visible",Parts_chip_type_filter$Classic))
)
)
)
)
And exactly that does not work. I must be setting the filters wrong. I know because when I filter the combination of "Product = TVs" and "Chip_type = Nano", no lines appear....
https://i.stack.imgur.com/MaJ5r.png
... although there is data:
> dat %>% filter(Product == "TVs") %>% filter(Chip_type == "Nano")
# A tibble: 8 x 7
Date Supplier Supplier_text order Chip_type Product n
<chr> <chr> <chr> <int> <chr> <chr> <chr>
1 2019-11 Overall TVs 14373 | 6.0% - Overall TVs 4 Nano TVs 4643
2 2019-12 Overall TVs 14373 | 6.0% - Overall TVs 4 Nano TVs 6904
3 2019-11 J 2603 | 5.8% - J 13 Nano TVs 3
4 2019-12 J 2603 | 5.8% - J 13 Nano TVs 3
5 2019-11 M -1711 | -19.4% - M 16 Nano TVs 2
6 2019-12 M -1711 | -19.4% - M 16 Nano TVs 1
7 2019-11 O 1315 | 23.6% - O 19 Nano TVs 2
8 2019-12 O 1315 | 23.6% - O 19 Nano TVs 1
I'm really looking forward to your suggestions how to set the visibility toggle of the buttons correctly!
I know that there is two similiar posts, but focussed on multiple graphs. It may very well be my lack of skill, but I could not get my problem solved with the provided solution and would appreciate your consideration and help!
Switch displayed traces via plotly dropdown menu
Multiple lines/traces for each button in a Plotly drop down menu in R
Something similar, but with one filter, done in Python (not R):
Plotly: How to toggle traces with a button similar to clicking them in legend?
The follow-up would be: Is it possible to select multiple categories, i. e. "Nano" and "Classic", and possibly "Smartphones" and "TVs" from the other filter, at the same time?
Here is a post for Python, but no answers, unfortunately:
Selecting multiple buttons at once in a plotly graph
Thank you so much in advance!
Full data set for import:
<!-- begin snippet: js hide: true -->
dat <- structure(list(Date = c("2019-11", "2019-11", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-11", "2019-11", "2019-12", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-11", "2019-11", "2019-11", "2019-11", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-12", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-11", "2019-11", "2019-11", "2019-11", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-12", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-11", "2019-11", "2019-11", "2019-11", "2019-11",
"2019-12", "2019-12", "2019-12", "2019-12", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-11", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-12", "2019-11", "2019-11", "2019-11", "2019-11",
"2019-11", "2019-12", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-11", "2019-11", "2019-12", "2019-12", "2019-11",
"2019-12", "2019-12", "2019-11", "2019-11", "2019-11", "2019-12",
"2019-12", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-11", "2019-11",
"2019-11", "2019-12", "2019-12", "2019-11", "2019-11", "2019-11",
"2019-12", "2019-12", "2019-12", "2019-11", "2019-11", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-11", "2019-11", "2019-11",
"2019-11", "2019-11", "2019-11", "2019-11", "2019-12", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-11", "2019-11", "2019-12", "2019-12", "2019-12", "2019-12",
"2019-12", "2019-11", "2019-11", "2019-11", "2019-11", "2019-12",
"2019-12", "2019-11", "2019-11", "2019-11", "2019-11", "2019-12",
"2019-12", "2019-12", "2019-12", "2019-11", "2019-11", "2019-11",
"2019-12", "2019-12", "2019-12", "2019-11", "2019-12", "2019-12",
"2019-11", "2019-11", "2019-12", "2019-12"), Supplier = c("Overall Smartphones",
"Overall Smartphones", "Overall Smartphones", "Overall Smartphones",
"Overall Smartphones", "Overall Smartphones", "Overall Smartphones",
"Overall Smartphones", "Overall Smartphones", "Overall Smartphones",
"Overall Smartphones", "Overall Smartphones", "Overall Smartphones",
"Overall Smartphones", "Overall Smartphones", "Overall Smartphones",
"Overall Monitors", "Overall Monitors", "Overall Monitors", "Overall Monitors",
"Overall Monitors", "Overall Monitors", "Overall Monitors", "Overall Monitors",
"Overall Monitors", "Overall Monitors", "Overall Monitors", "Overall Monitors",
"Overall Monitors", "Overall Monitors", "Overall Monitors", "Overall Monitors",
"A", "A", "A", "A", "A", "A", "A", "A", "Overall TVs", "Overall TVs",
"Overall TVs", "Overall TVs", "Overall TVs", "Overall TVs", "Overall TVs",
"Overall TVs", "Overall TVs", "Overall TVs", "Overall TVs", "Overall TVs",
"Overall TVs", "Overall TVs", "Overall TVs", "Overall TVs", "B",
"B", "B", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C",
"C", "C", "C", "C", "D", "D", "D", "D", "D", "D", "D", "D", "D",
"D", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "F",
"F", "F", "F", "G", "G", "G", "H", "H", "H", "H", "H", "I", "I",
"I", "I", "I", "J", "J", "J", "J", "K", "K", "K", "K", "K", "L",
"L", "L", "L", "L", "L", "M", "M", "M", "M", "M", "M", "Overall Miscellaneous",
"Overall Miscellaneous", "Overall Miscellaneous", "Overall Miscellaneous",
"Overall Miscellaneous", "Overall Miscellaneous", "Overall Miscellaneous",
"Overall Miscellaneous", "Overall Miscellaneous", "Overall Miscellaneous",
"Overall Miscellaneous", "Overall Miscellaneous", "Overall Miscellaneous",
"Overall Miscellaneous", "Overall Miscellaneous", "N", "N", "N",
"N", "N", "N", "N", "O", "O", "O", "O", "O", "O", "P", "P", "P",
"P", "P", "P", "P", "P", "C", "C", "C", "C", "C", "C", "Q", "Q",
"Q", "R", "R", "R", "S"), Supplier_text = c("94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "94757 | 17.9% - Overall Smartphones",
"94757 | 17.9% - Overall Smartphones", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-33239 | -37.8% - Overall Monitors",
"-33239 | -37.8% - Overall Monitors", "-17385 | -88.0% - A",
"-17385 | -88.0% - A", "-17385 | -88.0% - A", "-17385 | -88.0% - A",
"-17385 | -88.0% - A", "-17385 | -88.0% - A", "-17385 | -88.0% - A",
"-17385 | -88.0% - A", "14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"14373 | 6.0% - Overall TVs", "14373 | 6.0% - Overall TVs",
"-8387 | -80.6% - B", "-8387 | -80.6% - B", "-8387 | -80.6% - B",
"-8387 | -80.6% - B", "-8387 | -80.6% - B", "-8387 | -80.6% - B",
"-8387 | -80.6% - B", "-8387 | -80.6% - B", "-8387 | -80.6% - B",
"5701 | 79.2% - C", "5701 | 79.2% - C", "5701 | 79.2% - C",
"5701 | 79.2% - C", "5701 | 79.2% - C", "5701 | 79.2% - C",
"5701 | 79.2% - C", "5701 | 79.2% - C", "5701 | 79.2% - C",
"5155 | 49.2% - D", "5155 | 49.2% - D", "5155 | 49.2% - D",
"5155 | 49.2% - D", "5155 | 49.2% - D", "5155 | 49.2% - D",
"5155 | 49.2% - D", "5155 | 49.2% - D", "5155 | 49.2% - D",
"5155 | 49.2% - D", "4977 | 95.4% - E", "4977 | 95.4% - E",
"4977 | 95.4% - E", "4977 | 95.4% - E", "4977 | 95.4% - E",
"4977 | 95.4% - E", "4977 | 95.4% - E", "4977 | 95.4% - E",
"4977 | 95.4% - E", "4977 | 95.4% - E", "4977 | 95.4% - E",
"3676 |18380.0% - F", "3676 |18380.0% - F", "3676 |18380.0% - F",
"3676 |18380.0% - F", "-3132 | -99.4% - G", "-3132 | -99.4% - G",
"-3132 | -99.4% - G", "3065 | 33.6% - H", "3065 | 33.6% - H",
"3065 | 33.6% - H", "3065 | 33.6% - H", "3065 | 33.6% - H",
"-2854 | -56.1% - I", "-2854 | -56.1% - I", "-2854 | -56.1% - I",
"-2854 | -56.1% - I", "-2854 | -56.1% - I", "2603 | 5.8% - J",
"2603 | 5.8% - J", "2603 | 5.8% - J", "2603 | 5.8% - J",
"2564 | 39.4% - K", "2564 | 39.4% - K", "2564 | 39.4% - K",
"2564 | 39.4% - K", "2564 | 39.4% - K", "1843 | 334.5% - L",
"1843 | 334.5% - L", "1843 | 334.5% - L", "1843 | 334.5% - L",
"1843 | 334.5% - L", "1843 | 334.5% - L", "-1711 | -19.4% - M",
"-1711 | -19.4% - M", "-1711 | -19.4% - M", "-1711 | -19.4% - M",
"-1711 | -19.4% - M", "-1711 | -19.4% - M", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1662 | -30.0% - Overall Miscellaneous", "-1662 | -30.0% - Overall Miscellaneous",
"-1439 | -95.6% - N", "-1439 | -95.6% - N", "-1439 | -95.6% - N",
"-1439 | -95.6% - N", "-1439 | -95.6% - N", "-1439 | -95.6% - N",
"-1439 | -95.6% - N", "1315 | 23.6% - O", "1315 | 23.6% - O",
"1315 | 23.6% - O", "1315 | 23.6% - O", "1315 | 23.6% - O",
"1315 | 23.6% - O", "193 | 232.5% - P", "193 | 232.5% - P",
"193 | 232.5% - P", "193 | 232.5% - P", "193 | 232.5% - P",
"193 | 232.5% - P", "193 | 232.5% - P", "193 | 232.5% - P",
"-152 | -38.1% - C", "-152 | -38.1% - C", "-152 | -38.1% - C",
"-152 | -38.1% - C", "-152 | -38.1% - C", "-152 | -38.1% - C",
"-98 | -79.7% - Q", "-98 | -79.7% - Q", "-98 | -79.7% - Q",
"92 | 3066.7% - R", "92 | 3066.7% - R", "92 | 3066.7% - R", "-70 | -90.9% - S"
), order = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L,
2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L,
4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L,
5L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 7L,
7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 8L, 8L, 8L,
8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L, 10L, 10L, 10L, 11L, 11L, 11L,
11L, 11L, 12L, 12L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 14L, 14L,
14L, 14L, 14L, 15L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L,
16L, 16L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L,
17L, 17L, 17L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 19L, 19L,
19L, 19L, 19L, 19L, 20L, 20L, 20L, 20L, 20L, 20L, 20L, 20L, 21L,
21L, 21L, 21L, 21L, 21L, 22L, 22L, 22L, 23L, 23L, 23L, 24L),
Chip_type = c("Micro", "Nano", "BiMech", "Titan", "Quantum",
"Platinum", "PlainChip", "Classic", "Micro", "Nano", "BiMech",
"Titan", "Quantum", "Platinum", "PlainChip", "Classic", "Micro",
"Nano", "BiMech", "Titan", "Quantum", "Platinum", "PlainChip",
"Classic", "Micro", "Nano", "BiMech", "Titan", "Quantum",
"Platinum", "PlainChip", "Classic", "Titan", "Platinum",
"Quantum", "Nano", "Titan", "Platinum", "Nano", "PlainChip",
"Micro", "Nano", "BiMech", "Titan", "Quantum", "Platinum",
"PlainChip", "Classic", "Micro", "Nano", "BiMech", "Titan",
"Quantum", "Platinum", "PlainChip", "Classic", "Titan", "Platinum",
"Quantum", "Nano", "Titan", "Nano", "Quantum", "Platinum",
"PlainChip", "PlainChip", "Platinum", "Nano", "Quantum",
"Classic", "PlainChip", "Platinum", "Nano", "Quantum", "PlainChip",
"Platinum", "Quantum", "Nano", "Classic", "PlainChip", "Nano",
"Platinum", "Quantum", "Classic", "PlainChip", "Quantum",
"Platinum", "Classic", "Nano", "PlainChip", "Quantum", "Platinum",
"Nano", "Classic", "BiMech", "Titan", "Nano", "Titan", "Quantum",
"Titan", "Titan", "Platinum", "PlainChip", "Nano", "Classic",
"PlainChip", "Nano", "PlainChip", "PlainChip", "Quantum",
"Nano", "Classic", "Titan", "Nano", "Titan", "Nano", "Platinum",
"PlainChip", "Quantum", "Platinum", "PlainChip", "Titan",
"PlainChip", "Platinum", "Titan", "PlainChip", "Platinum",
"Titan", "Nano", "Titan", "Platinum", "Nano", "PlainChip",
"Nano", "BiMech", "Titan", "Quantum", "Platinum", "PlainChip",
"Classic", "Micro", "Nano", "BiMech", "Titan", "Quantum",
"Platinum", "PlainChip", "Classic", "Titan", "Quantum", "Titan",
"Nano", "Quantum", "Platinum", "PlainChip", "Titan", "Quantum",
"Nano", "Micro", "Titan", "Nano", "Platinum", "Quantum",
"Nano", "Classic", "Quantum", "Platinum", "Nano", "Classic",
"Classic", "Quantum", "Nano", "Classic", "Quantum", "Nano",
"Quantum", "Quantum", "Nano", "Quantum", "Nano", "Quantum",
"Titan"), Product = c("Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "TVs", "TVs", "TVs", "TVs", "TVs", "TVs",
"TVs", "TVs", "TVs", "TVs", "TVs", "TVs", "TVs", "TVs", "TVs",
"TVs", "Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "TVs", "TVs", "TVs",
"TVs", "Smartphones", "Smartphones", "Smartphones", "Smartphones",
"Smartphones", "TVs", "TVs", "TVs", "TVs", "TVs", "TVs",
"TVs", "TVs", "TVs", "TVs", "TVs", "TVs", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Monitors", "Monitors",
"Monitors", "Monitors", "Monitors", "Monitors", "Monitors",
"TVs", "TVs", "TVs", "TVs", "TVs", "TVs", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous",
"Miscellaneous", "Miscellaneous", "Miscellaneous", "Miscellaneous"
), n = c("106", "16920", "61216", "363698", "50797", "52715",
"174342", "9319", "92", "16928", "40920", "270963", "48605",
"34068", "114333", "4024", "3", "1536", "6793", "45146",
"7922", "5359", "27390", "1131", "12", "1311", "5431", "48107",
"6230", "5133", "21161", "505", "3619", "13", "2", "1", "19720",
"13", "10", "4", "96", "4643", "14534", "166664", "17178",
"17489", "30048", "5010", "96", "6904", "10463", "158060",
"15864", "20149", "24173", "2390", "12102", "7", "2", "1",
"10390", "5", "4", "2", "1", "11036", "329", "224", "2",
"2", "6936", "176", "85", "1", "15335", "55", "53", "48",
"14", "10292", "86", "47", "32", "11", "6559", "667", "631",
"419", "416", "4416", "336", "285", "105", "74", "2", "18",
"2", "18", "2", "86", "3151", "1", "14682", "77", "10", "9098",
"26", "2833", "5083", "2", "1", "1", "41051", "3", "45233",
"3", "10763", "44", "2", "6508", "2", "370", "265", "6",
"461", "86", "4", "5996", "2", "8826", "2", "1", "1", "503",
"5", "79", "3348", "742", "199", "989", "1", "473", "11",
"152", "3681", "363", "54", "804", "1702", "1", "1500", "2",
"1", "1", "1", "4868", "5", "2", "1", "5573", "1", "312",
"113", "3", "3", "42", "30", "6", "5", "371", "53", "19",
"312", "64", "23", "97", "121", "2", "3", "1", "3", "77")), row.names = c(NA,
-182L), class = c("tbl_df", "tbl", "data.frame"))
#[1]: https://i.stack.imgur.com/bDTWZ.png
#[2]: https://i.stack.imgur.com/MaJ5r.png
You created a list of 182 T or F, but what you really needed is a list of 24 T or F because there are 24 lines, 24 groups, 24 traces that plotly is going to show or not.
First, you did a great job of grouping the plot by a field in your data. I have another variation of the first plot- which calls plot_ly one time and still creates all 24 traces, like your loop.
First I made Supplier_text and ordered factor so that I could control the order of the legend.
# this will keep the legend order in order
d2 <- dat %>%
mutate(Supplier_text = ordered(Supplier_text,
levels = rev(unique(dat$Supplier_text))))
Then I created the 24 traces.
(plt <- plot_ly(data = d2,
x = ~Date,
y = ~n,
color = ~Supplier_text,
type = "scatter",
mode = "lines"))
Then I changed the filter you created for products. You have 24 traces and now you have 24 T or F for this filter.
# changed this so it is one for each trace, not one for each row
Parts_product_filter <- d2 %>% select(Supplier_text, Product) %>%
mutate(Smartphones = ifelse(Product == "Smartphones",T,F) %>% sapply(.,list),
TVs = ifelse(Product == "TVs",T, F) %>% sapply(.,list),
Monitors = ifelse(Product == "Monitors",T,F) %>% sapply(.,list),
Miscellaneous = ifelse(Product == "Miscellaneous",T,F) %>% sapply(.,list)) %>%
unique()
The other parts of your code remained the same, with the exception of object name of plotly_object, now plt in any calls connecting the plot (the two layouts and Chip_type filter).
Update REPLACEMENT
This only includes the "both" button.
I was answering your questions in comments and realized that something wasn't right with some traces. So when I fixed that issue, I also added hovering, so you could visualize why you get vertical lines. Remember, n is a factor. Plotly doesn't know which left n value you want to connect with which value on the right, other than by color.
When you make n a numeric field, plotly will add the values together (unless you provide plotly some other way of dividing the content up). Sadly computers can't read minds... yet...
I added a hovertemplate. If you see something in the legend that's not on the plot, there's something plotly didn't know what to do with. If you hover on the plot, you might even get a value, but no line. I have some examples at the end.
d2 <- dat %>%
mutate(Supplier_text = ordered(Supplier_text,
levels = rev(unique(dat$Supplier_text))),
Product = ordered(Product,
levels = sort(unique(dat$Product))),
Chip_type = ordered(Chip_type,
levels = sort(unique(dat$Chip_type))),
n = as.numeric(dat$n) %>% sort(decreasing = T) %>%
as.character() %>% ordered(., levels = unique(.))) %>%
arrange(n)
The all trace:
#------------- base plot ----------------
(plt <- d2 %>%
plot_ly(x = ~Date,
y = ~n,
color = ~Supplier_text,
type = 'scatter',
mode = 'lines',
text = ~Product,
hovertext = ~Chip_type,
visible = T
))
I realized that while I was counting traces, when the plot rendered, I didn't get 45 traces, I was getting a whole lot more! With only the base plot there are 24 traces. With both base and the combination button, there are 127 traces.
This is how I figured it out and validated the correction.
#------------- trace count ----------------
# I used length(plt$x$attrs) to confirm the number of traces
# -- that was a mistake!
# collect data, since it's not in the plotly object (errr)
pj = plotly_json(plt)
# read the JSON back
pjj = jsonlite::fromJSON(pj$x$data)
# number of traces:
nrow(pjj$data)
# [1] 24 # one trace for each color
The combination traces:
#------------- add combination traces ----------------
# each of the possible button groups when both filters are opted
cmb = expand.grid(Product = levels(d2$Product),
Chip_type = levels(d2$Chip_type))
# create combo traces
invisible(
lapply(1:nrow(cmb), # filter for both
function(x){
d3 = d2 %>% filter(Product == cmb[x, 1] %>% toString(),
Chip_type == cmb[x, 2] %>% toString()) %>%
droplevels
if(nrow(d3) < 1) {
print(cmb[x, ]) # let me know what was skipped
return() # if no rows, don't make the trace
} # end if
plt <<- plt %>%
add_trace(inherit = F,
data = d2 %>%
filter(Product == cmb[x, 1] %>% toString(),
Chip_type == cmb[x, 2] %>% toString()),
x = ~Date, y = ~n,
color = ~Supplier_text,
type = 'scatter',
mode = 'lines',
text = ~Product,
hovertext = ~Chip_type,
hovertemplate = paste0("Products: %{text}",
"\nChips: %{hovertext}"),
visible = F #,
#inherit = F
)
})
)
cmb # validate
Check the number of traces now:
#------------- combination traces updated trace count ----------------
# collect count
pj = plotly_json(plt)
# read the JSON back
pjj = jsonlite::fromJSON(pj$x$data)
# number of traces:
nrow(pjj$data)
# [1] 127 # whoa!
Create a data from the traces to make sure that the T/F is right
#------------- trace data frame ----------------
# create data frame of the JSON content so that traces can be match with combos
plt.df = data.frame(nm = pjj$data$name, # this is Supplier_text
# valCount is the number of observations in the trace
valCount = unlist(map(pjj$data$x, ~length(.x))),
# whether it's visible (is it all or not?)
vis = pjj$data$visible)
# inspect what you expect
tail(plt.df)
Combination part of button
#------------- set up for button for combos ----------------
tracs = d2 %>%
group_by(Product, Chip_type, Supplier_text) %>%
summarise(ct = n(), .groups = "drop") %>%
mutate(traces = 25:127)
# is the order the same in the plot?
tail(tracs, 10)
tail(plt.df, 10) # definitely not!
# check?
tracs %>% arrange(Chip_type) %>% tail(10)
# that's the right order
# update tracs' order
tracs <- tracs %>% arrange(Chip_type) %>%
mutate(traces = 25:nrow(plt.df)) # fix trace assignment
# double-check!
plt.df[25:35,]
tracs[1:11,]
# they aren't the same, but plotting groups are
# adjust cmb to be ordered before id trace to group combos
cmb <- cmb %>% arrange(Chip_type)
Now that the data is aligned (order-wise), we need to find exactly what traces go with which groups. There will be a trace for each color in the group (i.e., if Misc Titan has 5 in/ 5 out in 3 different colors, there will be three traces for Misc Titan.
#--------------- collect group to trace number ----------------
# between cmb, d2, and the traces, the three vars - product, chip, and
# supplier text are ordered factors so the order will be the same
cmbo = invisible(
lapply(1:nrow(cmb),
function(x){
rs = tracs %>% filter(Product == cmb[x, 1] %>% toString(),
Chip_type == cmb[x, 2] %>% toString()) %>%
select(traces) %>% unlist() %>% unique(use.names = F)
list(traces = rs)
}) %>% setNames(paste0(cmb[, 1], " ", cmb[, 2])) # add the names
)# 32 start and stop points for the 103 traces
# check
cmbo[1:6]
Now the button layout code can be written:
#---------------------- the button ----------------------
# now for the buttons...finally
# create the empty
raw_v <- rep(F, nrow(plt.df))
cButton <-
lapply(1:length(cmbo),
function(x){
traces <- cmbo[[x]][[1]] %>% unlist()
raw_v[traces] <- T
as.list(unlist(raw_v))
}) %>% setNames(names(cmbo))
# validate
length(cButton[[1]])
# [1] 127
length(cButton)
# [1] 32
# looks good
cmbBtn2 = lapply(1:length(cButton),
function(x){
label = names(cButton)[x] %>% gsub("\\.", " ", x = .)
method = "restyle"
args = list("visible", cButton[[x]])
list(label = label, method = method, args = args)
})
The all part of the button
#------------- set up button for "all" ----------------
all = list(list(label = "All",
method = "restyle",
args = list("visible",
as.list(unlist(
c(rep(T, 24),
rep(F, nrow(plt.df) - 24)
)))) # end args
)) # end list list
Now put it all together:
#---------------------- the layout ----------------------
Parts_legend <- list(
font = list(
family = "sans-serif",
size = 12,
color = "#000"),
title = list(text="<b> Delta previous month by Supplier - Absolute </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2,
layout.legend = "constant",
traceorder = "grouped")
plt %>%
layout(legend = Parts_legend,
title = "by supplier delta previous month",
xaxis = list(title = 'Date'),
yaxis = list(title = 'Chip Volume'),
margin = list(l = 120, t = 100),
updatemenus = list(
list(
active = 0,
type = "dropdown",
y = 1.2,
direction = "down",
buttons = append(all, cmbBtn2)))
) # end layout
Some checks:
# check some of these for accuracy
d2 %>% filter(Product == "TVs", Chip_type == "PlainChip") # correct
d2 %>% filter(Product == "Miscellaneous", Chip_type == "BiMech") # correct
d2 %>% filter(Product == "Monitors", Chip_type == "Classic") # NOT right!
# there are 2 in and 2 out, but 1 in and 1 out match,
# the other's are different colors, so the line's not drawn
d2 %>% filter(Product == "TVs", Chip_type == "Micro") # not correct;
# that's because there is more in than out
The last thing, and I'll finally stop! The vertical lines--I zoomed in on the 'All'. Here are multiple views of the same plot, same zoom, same quasi-horizontal line, same vertical line:
All plotly has for, let's say—rules, is x, y, and color. It doesn't care about the other data, you didn't bind it that way (well, I didn't).