1 Abstract

Distributed rooftop solar arrays that are interconnected into the electrical distribution grid have variable output in space and time which may not match well with demand. Small utilities may not have good methods to analyze when and where generation is happening. Using the interactive visualization capabilities of a Shiny web application based on historical smart meter data, can offer a way to analyze and understand when and where distributed generation is happening. Using smart meter data from May of 2023 from a small utility in the San Juan Islands of Washington State, I have built an interactive dashboard application that can be used for exploratory visual data analysis. The application uses the reactive programming principles of the Shiny web framework to implement spatial and temporal filtering on distributed generation data. The data is visualized in the dashboard to show where, when, and how much generation there was. A visual analysis using this application showed that for this study period, generation is much more variable in the temporal dimension than in the spatial dimension.

2 Introduction

As decarbonization efforts continue, renewable energy sources are making up a larger proportion of electrical generation capacity. Many people and businesses are adding solar panels to their properties that are connected to the existing grid so they can feed excess power back into it. However, renewable energy sources like these are variable in space and time. These resources are distributed throughout an electrical network as opposed to traditional fossil fuel generation that is centralized. As more generation capacity is added to a local grid, it becomes important to understand how much power is being generated and when and where, to determine what benefits or detriments these arrays create for a distribution grid.

Using a small subset of historical smart meter data from a small electric distribution utility, this analysis will explore how much power is generated from rooftop solar panels at what time. Previous research by Niu et al (2021) presented an approach for visualizing spatio-temporal changes in electrical demand using a visual analytics platform that grid operators or engineers could use to analyze demand and make decisions. Using the basic idea of interactive visualization of the historical state of an electic grid, this analysis will use a Shiny web application for interactive data filtering in the spatial and temporal dimension to answer the questions: where is solar generation happening, when, and how much?

Figure 1: Screenshot of the resulting application showing the map visualization and mean generation plot for the month of May 2023. The map shows generation for the time period as a graduated symbol and color and the plot shows the mean daily generation per meter in the included study area. This image shows all locations and time steps for the study period, so the total generation number of 591.31 MWh represents total distributed solar generation for the month of May 2023.
Figure 1: Screenshot of the resulting application showing the map visualization and mean generation plot for the month of May 2023. The map shows generation for the time period as a graduated symbol and color and the plot shows the mean daily generation per meter in the included study area. This image shows all locations and time steps for the study period, so the total generation number of 591.31 MWh represents total distributed solar generation for the month of May 2023.

3 Data

The data for this analysis is proprietary data from Orcas Power and Light Cooperative (OPALCO), a small electric distribution utility in the San Juan Islands of Washington State. The data will be extracted from remote smart metering technology. After meters record usage and generation data, it is stored in a Meter Data Management System (MDMS). The MDMS uses validation, estimation, and editing (VEE) rules to handle missing and invalid data. These rules are configured by the utility and set up by the system administrator. The MDMS has a data extract function that allows for data export based on different filter criteria. Data filtered by solar interconnected classes and a time period between May 1st and May 31st, 2023, will be used for this analysis. This period represents a period when the generation potential is high due to day length but variability may also be high due to prevailing climate conditions in the study area. Using a relatively small data set will allow for experimentation that can inform later analysis or modeling on larger data sets.

The data will consist of usage readings in kilowatt-hours, unique identifiers for location and equipment, date and time stamps, power flow direction, and VEE estimations if necessary. Generation is considered reverse power flow at the meter. This happens when a solar array is producing more energy than a household is using and is therefore able to contribute power back to the grid. The MDMS registers when this happens, and the data set can be filtered to include only this generation data. After extract from the MDMS, the data is stored as a csv file that can be read into R, and pre-processed before analysis.

Once pre-processing is done, the data is in a “tidy” data frame that has one record for each location and time point (Wickham, 2014). Each record has a variable for reading data, one for geometry, and several categorical attributes. These attributes include the generation reading in kilowatt hours (KWh) for each hour-long period, a location identifier, phase identifier, and a circuit identifier. Over short time periods like this month long pilot study, the only value that will change regularly is the reading data and those values are the ones of interest in this analysis. Other variables like circuit and phase are for aggregation purposes because they represent network connection characteristics for the data.

Other data that will be used are geographic data in a shapefile that represent physical locations of connection points between solar arrays and the grid. These points will be tied to generation data to give the generation capacity a location. These point locations could be situated within the larger electrical network through circuit and phase identifiers. Without depicting the entire network, solar arrays can be aggregated by circuit and phase to determine total generation capacity at any given time on each of these sub-networks. All points with a common phase and circuit are connected. Points with a common circuit but differing phases are not connected. Points on different circuits are also not connected. There is a relatively complex network of physical connections between devices that will not be considered beyond the aggregation by circuit and network described here.

4 Methodology

To answer the questions proposed in the introduction, a data analysis dashboard will be built (Figure 1) that demonstrates spatial and temporal filtering capability to analyze distributed energy generation. This dashboard will allow users to interactively visualize solar generation in space and time. This capability will allow for some basic analysis of real distributed generation by grid operators.

To do this, several steps are necessary. Using the data described in the previous step, reading data and shapefile data will be cleaned and then joined, forming a large spatially enabled data frame that functions as the data source for the application. The application functions will filter these data spatially by drawing on an interactive map, and temporally through a date range input. Once the data is filtered, sum and mean are calculated and plotted in the application. This interactivity allows for exploratory visual analysis by application users who may be engineers or grid operators. Using this tool may allow for a general understanding of the patterns of generation in a given time period.

The interactive application is built with the Shiny web framework for R. Several R packages, including janitor, tidyverse, lubridate, here, and sf allow for data cleaning. Other packages including bslib, bsicons, leaflet, leaflet.extras, and leafpop, add the necessary functionality to the application.

The flowchart in Figure 2 demonstrates the methodology of this study. Once data pre-processing is done, interactive temporal filtering is handled by Shiny functionality using a date range input. Spatial filtering is a more complex process that relies on the leaflet.extras package for drawing capability, the sf package for spatial operations, and the tidyverse package dplyr. Reactive programming principles built into the Shiny framework allow for automatic re-generation from the user inputs.

##### This is a spatial data analysis application aimed at
#####   allowing grid designers and operators to learn where
#####     and when distributed solar arrays are providing 
#####       generation to the local grid

# import packages
library(shiny) # interactive application building framework
library(bslib) # themes and layouts for shiny application
library(bsicons) # bootstrap icons for use in application
library(tidyverse) # data cleaning and analysis library
library(here) # directory handling
library(sf) # spatial data
library(janitor) # data cleaning functions
library(lubridate) # parsing date times
library(leaflet) # interactive mapping
library(leafpop) # pop ups for interactive mapping
library(leaflet.extras) # drawing tools for spatial filtering
library(kableExtra)# for formatting within this document
Figure 2: This flow chart shows the analysis process of the application from data pre-processing to visualization. Background data processes inform the initial map which is updated reactively by user input.
Figure 2: This flow chart shows the analysis process of the application from data pre-processing to visualization. Background data processes inform the initial map which is updated reactively by user input.

4.1 Data Cleaning

# read in meter reading data
test_data <- read_csv(here("data", "may2023.csv"))

# read in spatial data via sf
meters <- st_read(here("data", "points_3212024.shp"))

Once this data is read into R from the csv file and shapefile, several processing steps must be to get the data ready for visualization and querying. Because the MDMS allows for filtering during the extract process, the data tidying process for the csv file is already partially done. But several operations still need to be completed including:

  • run the clean_names() function from janitor package to make names easily readable

  • filter flow direction and unit of measure so that only reverse flow and measurements in kilowatt hours (KWh)

  • use if/else from dplyr to create one readings column from the actual readings and the VEE readings

  • convert location identifier to text field for joining to spatial data

  • parse date and hour from the date time field using lubridate

  • select only relevant columns

# tidy csv data
test_data <- test_data %>% 
  clean_names() %>% 
  filter(flow_direction == "R",
         unit_of_measure == "KWH_USAGE") %>% 
  mutate(reading = if_else(ami_value == 0,
                           vee_value,
                           ami_value,
                           missing = NULL),
         service_location = as.factor(service_location),
         hour = hour(end_date_time),
         date = date(end_date_time)) %>% 
  select(service_location,
         hour,
         date,
         end_date_time,
         reading)

 knitr::kable(head(test_data, 10),
               caption = "<b><i>Table 1: First ten rows of the readings dataset after data tidying process. This data is now ready to be joined to meter geometry and attributes</i></b>") %>%  
   kable_styling("striped")
Table 1: First ten rows of the readings dataset after data tidying process. This data is now ready to be joined to meter geometry and attributes
service_location hour date end_date_time reading
7613 2 2023-05-10 2023-05-10 02:00:00 2.286
9040 10 2023-05-09 2023-05-09 10:00:00 0.000
11572 9 2023-05-09 2023-05-09 09:00:00 0.000
10813 18 2023-05-09 2023-05-09 18:00:00 0.000
6108 3 2023-05-10 2023-05-10 03:00:00 0.031
9220 0 2023-05-10 2023-05-10 00:00:00 0.454
13050 21 2023-05-09 2023-05-09 21:00:00 2.971
9858 16 2023-05-09 2023-05-09 16:00:00 0.667
11818 22 2023-05-09 2023-05-09 22:00:00 3.414
9436 8 2023-05-09 2023-05-09 08:00:00 0.000

The shapefile data also needs to be processed to anonymize and simplify it. Only relevant fields will be included after tidying which strips any personally identifying information and removes unnecessary attributes.

Tidying steps include:

  • concatenate substation and feeder identifiers to make a circuit field (for future use)

  • select only relevant fields

  • rename fields to more meaningful values

  • transform data from local geographical projection to the WGS 84 coordinate system using the st_transform() function from the sf package

# subset and tidy meter data
meters <- meters %>% 
  mutate(circuit = paste(gs_substat, gs_feeder_, sep = "")) %>% 
  select(gs_service,
         gs_servi_1,
         gs_phase,
         gs_substat,
         circuit) %>% 
  rename(service_location = gs_service,
         substation = gs_substat,
         map_loc = gs_servi_1,
         phase = gs_phase) %>% 
  st_transform(geometry = geometry,
               crs = 4326) 

Then the data need to be joined. This creates a data frame from with one record for each time step at each location.

# join reading data to sf data. this data frame is functionally
#   the 'database' for this application
test_data <- test_data %>% 
  left_join(meters, 
            by = join_by(service_location), 
            relationship = "many-to-one")

 knitr::kable(head(test_data, 10),
               caption = "<b><i>Table 2: First ten rows of the joined dataset after combining spatial data with reading data. This data is in a long table format that has one record for each location at each time step.</i></b>") %>% 
   kable_styling("striped")
Table 2: First ten rows of the joined dataset after combining spatial data with reading data. This data is in a long table format that has one record for each location at each time step.
service_location hour date end_date_time reading map_loc phase substation circuit geometry
7613 2 2023-05-10 2023-05-10 02:00:00 2.286 2023452-002 A 10 102 POINT (-122.904 48.69625)
9040 10 2023-05-09 2023-05-09 10:00:00 0.000 2160226-001 C 08 081 POINT (-122.8674 48.65155)
11572 9 2023-05-09 2023-05-09 09:00:00 0.000 3134162-001 B 02 021 POINT (-122.8916 48.52597)
10813 18 2023-05-09 2023-05-09 18:00:00 0.000 2301303-001 C 04 042 POINT (-122.9333 48.59435)
6108 3 2023-05-10 2023-05-10 03:00:00 0.031 1744371-004 C 11 113 POINT (-123.0271 48.47967)
9220 0 2023-05-10 2023-05-10 00:00:00 0.454 2177340-001 C 04 041 POINT (-122.955 48.63981)
13050 21 2023-05-09 2023-05-09 21:00:00 2.971 3522441-024 C 02 022 POINT (-122.8368 48.44347)
9858 16 2023-05-09 2023-05-09 16:00:00 0.667 2232432-001 B 08 081 POINT (-122.8491 48.62532)
11818 22 2023-05-09 2023-05-09 22:00:00 3.414 3182250-004 A 02 023 POINT (-122.9066 48.51949)
9436 8 2023-05-09 2023-05-09 08:00:00 0.000 2198151-035 B 04 041 POINT (-122.9939 48.6372)

Before this data can be mapped however, distinct locations need to be extracted and some summary statistics can be run on the aggregated location data. This also creates a table that can be used for pop ups in the interactive map so that when a user clicks on a point, some basic information is displayed in table form.

# table of summary statistics for pop up values and map symbolization
pop_up_table <- test_data %>% 
  group_by(geometry) %>% 
  mutate(monthly_sum = sum(reading), hourly_mean = mean(reading)) %>% 
  arrange(service_location, hourly_mean)

pop_up_table <- pop_up_table %>% 
  distinct(service_location, .keep_all = TRUE) %>% 
  select(service_location,
         map_loc, 
         circuit, 
         phase, 
         hourly_mean, 
         monthly_sum, 
         geometry) %>%
  as.data.frame() %>% 
  st_as_sf() %>% 
  st_transform(4326)

4.2 Interactive filtering

Once the data is processed in this way, it can be displayed in a leaflet map that is embedded in the Shiny application. Figure 3 shows a screen shot of the map embedded in the user interface. The tool bar on the left is for drawing polygon or rectangular shapes for spatial filtering. Using this tool a user can filter locations to analyze generation data for a given subset of meters.

When the user selects a subset of meter locations, the map removes all other meters from the map and re-symbolizes the selected locations. Likewise, the mean daily generation plot is also recomputed. Whatever values are in the date range input boxes is the date range for which the new data is visualized in the map, the plot, and the value box in the lower left of the application that calculates total generation (see Figure 1).

Figure 3 also shows the date input for temporal filtering. Using these input boxes, a user can define a date range that will automatically re-compute the plot and the total generation for the chosen date range and spatial filter.

################################################################################
##### This is the code to set up the UI for the application with the side bar and
####    map visible in Figure 3.


# cards list
cards <- list(card(card_header(textOutput("text")),
                   leafletOutput("map")
                   ),
              
              card(plotOutput("plot")),
              
              card(value_box(
                title = "Total Solar Generation: ",
                value = textOutput("total"),
                showcase = bs_icon("sun"),
                theme_color = "secondary"
              )
              ))


##### set up ui
ui <- page_sidebar(
  theme = bs_theme(bootswatch = "darkly"),
  title = "Exploring Distributed Solar Generation in the San Juan Islands",
  sidebar = sidebar("This tool is designed to be used to gather information
                    about where and when distributed energy resources are providing 
                    power back to the grid.",
                    
                    p("Use the map tools to select a subset of solar arrays, and
                      use the date input to narrow or expand the time frame. Data
                      available covers May of 2023 as a pilot demonstration."),

                    # date range widget in sidebar
                    dateRangeInput("dates",
                              "Date",
                              start = "2023-05-01",
                              end = "2023-05-31",
                              min = "2023-05-01",
                              max = "2023-05-31",
                              format = "yyyy-mm-dd")),

  
  layout_column_wrap(
    width = 1/2,
    height = 300,
    cards[[1]],
    layout_column_wrap(
      width = 1,
      heights_equal = "row",
      cards[[2]], cards[[3]]
    )
  )
)
Figure 3: The map in the application showing spatial filtering using the draw tool bar from the leaflet.extras package.
Figure 3: The map in the application showing spatial filtering using the draw tool bar from the leaflet.extras package.

4.3 Reactive programming

This can all be done with the reactive programming model employed by the Shiny framework. Reactive programming is a way to make the user experience seamless and fast by carefully organizing how and when expressions need to be run in the code that sets up the app (Grolemund, 2015). A simple implementation of this principle is found in the date range inputs in this app. There are data filtering functions that rely on the date range inputs to filter the data temporally. When these functions run, they use the value in the date range input. They also leave a “context” with the input value which is essentially a list of instructions to execute if the value in the user input changes. When the user changes the date value, the context instructions run and send the new value back to the filtering functions. The application monitors those functions on a time scale that a computer can parse but humans do not. If any of those functions have registered that the input values that they rely on have changed, they are re-run which produces new output based on the new user input values (Grolemund, 2015). All of this happens automatically based on the Shiny framework and that reactivity that appears nearly instantaneous to a user is what facilitates interactive analysis in Shiny applications.

################################################################################
##### Server


# set up server logic
server <- function(input, output) {
  
  ##### leaflet map data
  # color pallette for points and legend
  pal <- colorNumeric("YlOrRd", 
                      domain = NULL)
  
 # create static map components 
  output$map <- renderLeaflet({
    leaflet(pop_up_table) %>% 
      addProviderTiles(providers$CartoDB.DarkMatter) %>% 
      setView(lng = -122.9563, 
              lat = 48.555,
              zoom =10) %>% 
      addDrawToolbar( polylineOptions = FALSE,
                      polygonOptions = TRUE,
                      rectangleOptions = TRUE,
                      circleOptions = FALSE,
                      markerOptions = FALSE,
                      circleMarkerOptions = FALSE,
                      singleFeature = TRUE,
                      editOptions = editToolbarOptions()) %>% 
      addCircleMarkers(color = ~pal(monthly_sum),
                       stroke = FALSE,
                       fillOpacity = 0.75,
                       radius = ~ monthly_sum/1000,
                       popup = popupTable(st_drop_geometry(pop_up_table))) %>%
      addLegend(position = "bottomright",
                values = pop_up_table$monthly_sum,
                pal = pal,
                title = "Generation in KwH")
    })
  
  
  
  # get input from draw tool
  observeEvent(input$map_draw_all_features, {
    
    # if there is a drawn feature do some operations
    if (!is.null(input$map_draw_all_features) && length(input$map_draw_all_features$features) > 0) {
    
      # the selection is a list but its a list of the drawn features coords
      #   the coords are nested lists
      selection <- input$map_draw_all_features$features
     
      # number of drawn features (always 1)
      numFeat <- length(selection)
  
      # pull coords out from feature
      coords <- input$map_draw_all_features$features[[numFeat]]$geometry$coordinates
      
      # unlist them once
      coords <- unlist(coords, recursive = FALSE)
    
      # apply a function across the list
      point_list <- lapply(coords, function(x) {st_point(as.numeric(x))})
      
      
      # create a bounding box object from the points list and convert to an
      #   sfc (sf geometry column)
      bbox <- st_as_sfc(point_list, crs= 4326) %>% 
        st_bbox() %>%
        st_as_sfc()
      
      
      # run intersect function on the data table
      spatially_filtered <- pop_up_table %>%
        st_intersection(bbox) %>% 
        select(service_location) %>% 
        st_drop_geometry()
      
      print(class(spatially_filtered))
        
     
    
      # first filter the data by spatial selection
      joined_df <- reactive({
      
        # join list from spatial filter to full data frame
        filtered_data <- test_data %>% 
          right_join(spatially_filtered, 
                      by = join_by(service_location), 
                    relationship = "many-to-one")
      })
    
      test_df <- joined_df()
      glimpse(test_df)
    
      # now filter that further by date
      final_filter <- reactive ({
      
        # filter spatial selection by time
        joined_df() %>% 
          filter(date >= date(input$dates[1]) & date <= date(input$dates[2])) %>%
          group_by(date) %>% 
          mutate(monthly_sum = sum(reading, na.rm = TRUE), daily_mean = mean(reading, na.rm=TRUE)) %>% 
          arrange(service_location, daily_mean)
      })
    
      # reactive expression for plotting selected points by time-frame sum
      plot_points <- reactive({
      
        pop_up_table <- joined_df() %>% 
          group_by(geometry) %>% 
          mutate(monthly_sum = sum(reading, na.rm=TRUE), hourly_mean = mean(reading, na.rm=TRUE)) %>% 
          arrange(service_location, hourly_mean)
      
        pop_up_table <- pop_up_table %>% 
          distinct(service_location, .keep_all = TRUE) %>% 
          select(service_location,
                map_loc, 
                circuit, 
                phase, 
                hourly_mean, 
                monthly_sum, 
                geometry) %>% 
          st_as_sf()
      
      })
    
      print(plot_points())
    
      total <- reactive({joined_df() %>%
         filter(date >= date(input$dates[1]) & date <= date(input$dates[2])) %>%
          mutate(sum = sum(reading))
      })
   
      # needs to narrow down to non-joined data but keep values from joined df 

      leafletProxy("map", data = plot_points()) %>%
        clearMarkers() %>% 
        addCircleMarkers(color = ~pal(monthly_sum),
                        stroke = FALSE,
                        fillOpacity = 0.75,
                        radius = ~ monthly_sum/1000,
                        popup = popupTable(st_drop_geometry(pop_up_table)))

    
      # create plot output
      output$plot <- renderPlot({
        final_filter() %>%
          ggplot() +
          geom_line(aes(x = date, 
                        y = daily_mean),
                    linewidth = 1,
                    color = "#800000",
                    show.legend = FALSE) +
          labs(title = "Mean Daily Generation per Meter for Selected Locations",
               subtitle = paste("From ", input$dates[1], " to ", input$dates[2])) +
          xlab("Day") +
          ylab("Daily Mean per Meter") +
          theme_gray()
      })
    
    
    # output total power generation for time and location
    output$total <- renderText({paste(round(sum(total()$reading, na.rm = TRUE)/1000, 2), " MWh")})
    
    } else {
      
      leafletProxy("map", data = pop_up_table) %>%
        clearMarkers() %>%
        addCircleMarkers(color = ~pal(monthly_sum),
                         stroke = FALSE,
                         fillOpacity = 0.75,
                         radius = ~ monthly_sum/1000,
                         popup = popupTable(st_drop_geometry(pop_up_table)))
    }
    
    
    })              
  
               
  # reactive text for map title
  output$text <- renderText(paste("Generation for: ", input$dates[1], " to ", input$dates[2]))


}


shinyApp(ui= ui, server = server)

4.4 Visual analysis

Because the user can affect the outputs in the dashboard application through these reactive programming principles, they are able to explore the data interactively and gain insight from the perceptible changes in the map, the plot, and the value box holding the total generation for the selection. This is a crucial element to the use of this type of application. This type of visual analysis can allow for data exploration that leads to insights or new questions. Those questions can then be the guide to new analyses, whether they are statistical or visual.

5 Results

One result of this interactive data exploration is the insight that the shape of the generation plot is very similar from one set of locations to another if the date range stays static. Figure 4 shows three images from the application that demonstrate this. In each image a different set of locations is selected for dates between May 1st and May 9th. For each location, the generation plot shows a pattern of increasing generation from May 1st to May 3rd, then a sharp decrease on the 4th and 5th, with generally increasing generation between the 6th and 9th.

This is likely due to data aggregation, with mean value by meter causing a smoothing effect. But it also suggests that at the temporal resolution of daily readings, solar generation is more variable in time than in space. An interesting question this raises, is whether this pattern is the same at an hourly resolution. Would spatial variation be more pronounced if the data were not aggregated in this way?

Figure 4: Three different spatial selections for the same date range show a similar pattern of generation, suggesting that at the temporal resolution, spatial variability is less than temporal variability.

6 Reflection and further analysis

This project was a very challenging effort for me. I do not have much experience with user interface development or reactive programming. However, the application does provide a pretty good demonstration of what I set out to do. I really wanted to create a tool that someone could use to examine distributed generation in a way that took spatial and temporal dimensions into account. Using interactive spatial and temporal filtering in Shiny did achieve that goal.

One part of the application that I could not make work was aggregation by network features. The data behind the app has phase and circuit information still included, but I was unable to get this functionality to work. The goal was to be able to filter by sub network to get an idea of which sub networks had more or less generation.

On the other hand, this application could be extended to take network connectivity into account, calculate different descriptive statistics, or perform some exploratory spatial data analysis. One way to extend this application is through hot spot analysis. Calculating the Getis-Ord Gi* statistic at each time step and visualizing the results with animation based on a user input date range would be relatively easy to achieve in an application like this (Parry, n.d.). It would also create a visualization of the relative change on the system over time. This might be a way to characterize the spatio-temporal relationship of variable solar generation.

Another way to extend this application and create more practical analyses would be to add demand information. The big problem with renewable variability is matching supply to demand. The generation that is analyzed in this application represents excess solar power that flows back to the grid. Matching that excess power to the demand at the same time step would allow for a more meaningful analysis of how much these types of resources can contribute to the system overall and could point to where, when, and how much more renewable generation is needed. This in turn could contribute to planning efforts for utility scale renewable sources like large solar fields or tidal energy generation.

7 References:

Grolemund, G. (2015, May 21). How to understand reactivity in R. Shiny. https://shiny.posit.co/r/articles/build/understanding-reactivity/

Niu, Z., Wu, J., Liu, X., Huang, L., & Nielsen, P. S. (2021). Understanding energy demand behaviors through spatio-temporal smart meter data analysis. Energy, 226, 120493. https://doi.org/10.1016/j.energy.2021.120493

Parry, J. (n.d.). Emerging hot spot analysis. • sfdep. https://sfdep.josiahparry.com/articles/understanding-emerging-hotspots

Wickham, H. . (2014). Tidy Data. Journal of Statistical Software, 59(10), 1–23. https://doi.org/10.18637/jss.v059.i10

R Packages:

citation("shiny")
## To cite package 'shiny' in publications use:
## 
##   Chang W, Cheng J, Allaire J, Sievert C, Schloerke B, Xie Y, Allen J,
##   McPherson J, Dipert A, Borges B (2024). _shiny: Web Application
##   Framework for R_. R package version 1.8.1.1,
##   <https://CRAN.R-project.org/package=shiny>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {shiny: Web Application Framework for R},
##     author = {Winston Chang and Joe Cheng and JJ Allaire and Carson Sievert and Barret Schloerke and Yihui Xie and Jeff Allen and Jonathan McPherson and Alan Dipert and Barbara Borges},
##     year = {2024},
##     note = {R package version 1.8.1.1},
##     url = {https://CRAN.R-project.org/package=shiny},
##   }
citation("bslib")
## To cite package 'bslib' in publications use:
## 
##   Sievert C, Cheng J, Aden-Buie G (2023). _bslib: Custom 'Bootstrap'
##   'Sass' Themes for 'shiny' and 'rmarkdown'_. R package version 0.5.1,
##   <https://CRAN.R-project.org/package=bslib>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {bslib: Custom 'Bootstrap' 'Sass' Themes for 'shiny' and 'rmarkdown'},
##     author = {Carson Sievert and Joe Cheng and Garrick Aden-Buie},
##     year = {2023},
##     note = {R package version 0.5.1},
##     url = {https://CRAN.R-project.org/package=bslib},
##   }
citation("bsicons")
## To cite package 'bsicons' in publications use:
## 
##   Sievert C (2023). _bsicons: Easily Work with 'Bootstrap' Icons_. R
##   package version 0.1.2, <https://CRAN.R-project.org/package=bsicons>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {bsicons: Easily Work with 'Bootstrap' Icons},
##     author = {Carson Sievert},
##     year = {2023},
##     note = {R package version 0.1.2},
##     url = {https://CRAN.R-project.org/package=bsicons},
##   }
citation("tidyverse")
## To cite package 'tidyverse' in publications use:
## 
##   Wickham H, Averick M, Bryan J, Chang W, McGowan LD, François R,
##   Grolemund G, Hayes A, Henry L, Hester J, Kuhn M, Pedersen TL, Miller
##   E, Bache SM, Müller K, Ooms J, Robinson D, Seidel DP, Spinu V,
##   Takahashi K, Vaughan D, Wilke C, Woo K, Yutani H (2019). "Welcome to
##   the tidyverse." _Journal of Open Source Software_, *4*(43), 1686.
##   doi:10.21105/joss.01686 <https://doi.org/10.21105/joss.01686>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Article{,
##     title = {Welcome to the {tidyverse}},
##     author = {Hadley Wickham and Mara Averick and Jennifer Bryan and Winston Chang and Lucy D'Agostino McGowan and Romain François and Garrett Grolemund and Alex Hayes and Lionel Henry and Jim Hester and Max Kuhn and Thomas Lin Pedersen and Evan Miller and Stephan Milton Bache and Kirill Müller and Jeroen Ooms and David Robinson and Dana Paige Seidel and Vitalie Spinu and Kohske Takahashi and Davis Vaughan and Claus Wilke and Kara Woo and Hiroaki Yutani},
##     year = {2019},
##     journal = {Journal of Open Source Software},
##     volume = {4},
##     number = {43},
##     pages = {1686},
##     doi = {10.21105/joss.01686},
##   }
citation("here")
## To cite package 'here' in publications use:
## 
##   Müller K (2020). _here: A Simpler Way to Find Your Files_. R package
##   version 1.0.1, <https://CRAN.R-project.org/package=here>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {here: A Simpler Way to Find Your Files},
##     author = {Kirill Müller},
##     year = {2020},
##     note = {R package version 1.0.1},
##     url = {https://CRAN.R-project.org/package=here},
##   }
citation("sf")
## To cite package sf in publications, please use:
## 
##   Pebesma, E., & Bivand, R. (2023). Spatial Data Science: With
##   Applications in R. Chapman and Hall/CRC.
##   https://doi.org/10.1201/9780429459016
## 
##   Pebesma, E., 2018. Simple Features for R: Standardized Support for
##   Spatial Vector Data. The R Journal 10 (1), 439-446,
##   https://doi.org/10.32614/RJ-2018-009
## 
## To see these entries in BibTeX format, use 'print(<citation>,
## bibtex=TRUE)', 'toBibtex(.)', or set
## 'options(citation.bibtex.max=999)'.
citation("janitor")
## To cite package 'janitor' in publications use:
## 
##   Firke S (2023). _janitor: Simple Tools for Examining and Cleaning
##   Dirty Data_. R package version 2.2.0,
##   <https://CRAN.R-project.org/package=janitor>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {janitor: Simple Tools for Examining and Cleaning Dirty Data},
##     author = {Sam Firke},
##     year = {2023},
##     note = {R package version 2.2.0},
##     url = {https://CRAN.R-project.org/package=janitor},
##   }
citation("lubridate")
## To cite lubridate in publications use:
## 
##   Garrett Grolemund, Hadley Wickham (2011). Dates and Times Made Easy
##   with lubridate. Journal of Statistical Software, 40(3), 1-25. URL
##   https://www.jstatsoft.org/v40/i03/.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Article{,
##     title = {Dates and Times Made Easy with {lubridate}},
##     author = {Garrett Grolemund and Hadley Wickham},
##     journal = {Journal of Statistical Software},
##     year = {2011},
##     volume = {40},
##     number = {3},
##     pages = {1--25},
##     url = {https://www.jstatsoft.org/v40/i03/},
##   }
citation("leaflet")
## To cite package 'leaflet' in publications use:
## 
##   Cheng J, Karambelkar B, Xie Y (2023). _leaflet: Create Interactive
##   Web Maps with the JavaScript 'Leaflet' Library_. R package version
##   2.1.2, <https://CRAN.R-project.org/package=leaflet>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {leaflet: Create Interactive Web Maps with the JavaScript 'Leaflet'
## Library},
##     author = {Joe Cheng and Bhaskar Karambelkar and Yihui Xie},
##     year = {2023},
##     note = {R package version 2.1.2},
##     url = {https://CRAN.R-project.org/package=leaflet},
##   }
citation("leafpop")
## To cite package 'leafpop' in publications use:
## 
##   Appelhans T, Detsch F (2021). _leafpop: Include Tables, Images and
##   Graphs in Leaflet Pop-Ups_. R package version 0.1.0,
##   <https://CRAN.R-project.org/package=leafpop>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {leafpop: Include Tables, Images and Graphs in Leaflet Pop-Ups},
##     author = {Tim Appelhans and Florian Detsch},
##     year = {2021},
##     note = {R package version 0.1.0},
##     url = {https://CRAN.R-project.org/package=leafpop},
##   }
citation("leaflet.extras")
## To cite package 'leaflet.extras' in publications use:
## 
##   Karambelkar B, Schloerke B (2018). _leaflet.extras: Extra
##   Functionality for 'leaflet' Package_. R package version 1.0.0,
##   <https://CRAN.R-project.org/package=leaflet.extras>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {leaflet.extras: Extra Functionality for 'leaflet' Package},
##     author = {Bhaskar Karambelkar and Barret Schloerke},
##     year = {2018},
##     note = {R package version 1.0.0},
##     url = {https://CRAN.R-project.org/package=leaflet.extras},
##   }
citation("kableExtra")
## To cite package 'kableExtra' in publications use:
## 
##   Zhu H (2024). _kableExtra: Construct Complex Table with 'kable' and
##   Pipe Syntax_. R package version 1.4.0,
##   <https://CRAN.R-project.org/package=kableExtra>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {kableExtra: Construct Complex Table with 'kable' and Pipe Syntax},
##     author = {Hao Zhu},
##     year = {2024},
##     note = {R package version 1.4.0},
##     url = {https://CRAN.R-project.org/package=kableExtra},
##   }