When you add a colour (or any “split”) aesthetic to a ggplot, ggplotly will turn each level of that aesthetic into a separate trace. Inside each trace, event_data("plotly_hover")$pointNumber
is the index of the point within that trace, so it always starts back at zero when you move from one colour‐trace to the next.
There are two ways to deal with it:
Re‐compute the original row by hand, using both curveNumber
(the index of the trace) and pointNumber
(the index within the trace). You’d have to know how many rows are in each group so that for, say, trace 1 (“b”) you add the size of group “a” to its pointNumber
.
Carry the row‐index through into Plotly’s “key” field, and then pull it back out directly. This is much cleaner and will work even if you change the grouping.
library(shiny)
library(ggplot2)
library(plotly)
ui <- fluidPage(
titlePanel("Plotly + key aesthetic"),
fluidRow(
plotlyOutput("plotlyPlot", height = "500px"),
verbatimTextOutput("memberDetails")
)
)
server <- function(input, output, session) {
# add an explicit row‐ID
df <- data.frame(
id = seq_len(10),
x = 1:10,
y = 1:10,
col = c(rep("a", 4), rep("b", 6)),
stringsAsFactors = FALSE
)
output$plotlyPlot <- renderPlotly({
p <- ggplot(df, aes(
x = x,
y = y,
color = col,
# carry the row‐id into plotly
key = id,
# still use `text` if you want hover‐labels
text = paste0("colour: ", col, "<br>row: ", id)
)) +
geom_point(size = 3)
ggplotly(p, tooltip = "text")
})
output$memberDetails <- renderPrint({
ed <- event_data("plotly_hover")
if (is.null(ed) || is.null(ed$key)) {
cat("Hover over a point to see its row‑ID here.")
return()
}
# key comes back as character, so convert to numeric
row <- as.integer(ed$key)
cat("you hovered row:", row, "\n")
cat(" colour:", df$col[df$id == row], "\n")
cat(" x, y :", df$x[df$id == row], ",", df$y[df$id == row], "\n")
})
}
shinyApp(ui, server)