基于R实现可点击修改属性的交互式选举人团热力地图技术求助
Great question! I've built similar interactive map tools in Shiny before, and here's a solid solution that addresses your core needs: click-to-toggle region results, persist those changes, and calculate aggregated totals in real-time.
Solution Overview
We'll build a Shiny app using ggplot2 + plotly for interactive mapping, paired with Shiny's reactive values to track state result changes. This approach works because:
- Plotly lets us capture click events and extract the exact region that was clicked
- Reactive values in Shiny let us update the underlying data and automatically refresh the map/totals
- We can easily integrate custom metrics (like electoral votes) for post-modification calculations
Step-by-Step Implementation
1. Install Required Packages
First, make sure you have these packages installed:
install.packages(c("shiny", "ggplot2", "maps", "plotly", "dplyr", "usdata"))
2. Full Shiny App Code
library(shiny) library(ggplot2) library(maps) library(plotly) library(dplyr) library(usdata) # Get base state map data usa <- map_data("state") # Get electoral college votes per state (clean up to match map region names) electoral_votes <- usdata::electoral_college() %>% mutate(region = tolower(state)) %>% select(region, votes) # Initialize state results with your default probabilities set.seed(123) # For reproducibility initial_results <- usa %>% distinct(region) %>% mutate( result = sample( c("Rep", "Dem", "Toss-Up"), size = n(), prob = c(0.30, 0.40, 0.30), replace = TRUE ) ) %>% left_join(electoral_votes, by = "region") # Merge initial results with map data initial_map_data <- usa %>% left_join(initial_results, by = "region") ui <- fluidPage( titlePanel("Interactive State Result Map"), fluidRow( column(8, plotlyOutput("state_map")), column(4, h3("Electoral Vote Totals"), verbatimTextOutput("vote_totals") ) ) ) server <- function(input, output) { # Reactive value to store the current state of the map data (updates when clicks happen) current_map_data <- reactiveVal(initial_map_data) # Capture plotly click events observeEvent(event_data("plotly_click"), { clicked_state <- event_data("plotly_click")$customdata # Update the result for the clicked state (cycle through Rep → Dem → Toss-Up → Rep) updated_data <- current_map_data() %>% group_by(region) %>% mutate( result = case_when( region == clicked_state & result == "Rep" ~ "Dem", region == clicked_state & result == "Dem" ~ "Toss-Up", region == clicked_state & result == "Toss-Up" ~ "Rep", TRUE ~ result ) ) %>% ungroup() # Update the reactive value with the new data current_map_data(updated_data) }) # Render the interactive map output$state_map <- renderPlotly({ p <- ggplot() + geom_map( data = current_map_data(), map = usa, aes(long, lat, map_id = region, fill = result, customdata = region), color = "black" ) + scale_fill_manual( values = c("Dem" = "blue", "Rep" = "red", "Toss-Up" = "grey"), name = "Result" ) + coord_map() + theme_void() # Convert ggplot to plotly (preserves interactivity) ggplotly(p) %>% layout(hovermode = "closest") %>% event_register("plotly_click") }) # Calculate and display electoral vote totals output$vote_totals <- renderPrint({ vote_summary <- current_map_data() %>% distinct(region, result, votes) %>% group_by(result) %>% summarize(total_votes = sum(votes, na.rm = TRUE)) %>% filter(result != "Toss-Up") cat("Democratic:", vote_summary$total_votes[vote_summary$result == "Dem"], "\n") cat("Republican:", vote_summary$total_votes[vote_summary$result == "Rep"], "\n") }) } shinyApp(ui, server)
3. Key Features Explained
- Reactive Data Storage:
current_map_data()is a reactive value that holds the latest state of our map data. Every time a region is clicked, we update this value, which triggers an automatic refresh of both the map and vote totals. - Click Handling: The
observeEvent()listens forplotly_clickevents. We extract the clicked region's name fromcustomdata(added to the ggplot aes so Plotly can track it) and cycle the result value as needed. - Aggregated Calculation: We use
distinct()to get one row per region, then group byresultto sum up votes for each party. - Map Customization: The
scale_fill_manual()matches your requested colors, andtheme_void()cleans up the map to focus on the regions themselves.
4. Why This Solves Your Earlier Issues
- When you tried Plotly/Leaflet before, you likely didn't tie click events to reactive data updates in Shiny. This is the missing piece—Shiny's reactive framework lets us persist changes and propagate them across the entire app.
mapeditis great for selecting features, but it's not designed for direct data modification in a Shiny workflow. This approach gives you full control over how clicks modify your data.
Customization Tips
- Adjust the result cycle logic (e.g., only toggle between two options instead of three) by modifying the
case_when()statement. - Add a reset button to revert all regions to their initial state by adding an
actionButton()and updatingcurrent_map_data()back toinitial_map_data. - Replace electoral vote data with your own custom metrics (since your actual use case isn't elections) by swapping out the
electoral_votesdata frame with your own dataset.
内容的提问来源于stack exchange,提问作者tifu
相关产品推荐
相关产品推荐

