0

I am a compete novice at using loops and simplifying my code. I have a dataset of places with accompanying data that I would like to plot on separate maps. The individual plotting of the maps I can do. However I would like to automate the process a little bit, I have new data arriving each day and don't want to repeat the process of cleaning the data and rewriting code. So I thought a for loop might be the answer

What I need are separate plots for each Time in the data below So the loop would pull out all the data for each value of Time and then plot it.

dput(df)  
structure(list(Site = c("O242", "O51", "O59", "O71", "C110", 
"C116", "C120", "C13", "C132", "C134", "C139", "C140", "C29", 
"C30", "C33", "C48", "C56", "C9A", "MP25", "MP67", "B30", "MP2", 
"B101", "B11", "B112", "B15", "B197", "B2", "B217", "B22", "B30", 
"B95", "MP21", "MP25", "MP33", "MP51", "MP56", "MP6", "MP60", 
"MP61", "MP67", "MP77", "EX84", "EX92", "SW130", "O31", "O38", 
"O38B", "O48", "O58", "O59", "O68", "O71", "O72", "O81", "O94", 
"O207", "O209", "O210", "O215"), Time = c(-25, -22, -22, -22, 
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, 
-14, -23, -23, -20, -20, -11, -11, -11, -11, -11, -11, -11, -11, 
-11, -11, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, 
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, 
-10, -10, -10, -10), Code = c(1L, 1L, 1L, 1L, 2L, 2L, 1L, 1L, 
2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 3L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 3L, 2L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 
1L, 1L, 1L, 2L, 1L, 2L, 1L, 3L, 2L, 1L, 3L, 1L, 1L, 3L, 2L, 2L, 
3L, 1L, 1L, 2L), lon = c(-1.341280663, -1.343562025, -1.343620358, 
-1.340629756, -1.332551665, -1.329108814, -1.328655294, -1.330835311, 
-1.330715028, -1.33052464, -1.328144549, -1.328287425, -1.329353862, 
-1.329343236, -1.33041446, -1.325353001, -1.327279282, -1.332909331, 
-1.300122834, -1.299148682, -1.310197641, -1.305886812, -1.308725397, 
-1.309505208, -1.309235075, -1.308580716, -1.30959055, -1.308685087, 
-1.309426224, -1.306562029, -1.310197641, -1.307564253, -1.301598673, 
-1.300122834, -1.299510666, -1.299846899, -1.297823339, -1.305388627, 
-1.297220016, -1.297398331, -1.299148682, -1.300378324, -1.333554619, 
-1.338688389, -1.332015649, -1.344951753, -1.344769267, -1.345214102, 
-1.342514477, -1.343145083, -1.343620358, -1.34275518, -1.340629756, 
-1.339067762, -1.338035147, -1.335442485, -1.346461847, -1.34550727, 
-1.34516939, -1.346584124), lat = c(51.76635545, 51.76553293, 
51.76450781, 51.76428383, 51.75689245, 51.75615401, 51.75742817, 
51.75637019, 51.75666667, 51.75740286, 51.7596281, 51.75976378, 
51.75721637, 51.75695556, 51.75701561, 51.75871255, 51.75875955, 
51.75720018, 51.76339382, 51.75986347, 51.76597134, 51.76737513, 
51.76464054, 51.76481595, 51.76542577, 51.76557477, 51.76682149, 
51.7644335, 51.76714421, 51.76681267, 51.76597134, 51.76571265, 
51.76447255, 51.76339382, 51.76268887, 51.76062289, 51.76030512, 
51.76678776, 51.75996884, 51.75968219, 51.75986347, 51.75998767, 
51.76749876, 51.76822905, 51.76474771, 51.76863319, 51.76622254, 
51.7655237, 51.76482531, 51.76430735, 51.76450781, 51.76421526, 
51.76428383, 51.76308822, 51.76434118, 51.76525265, 51.76642077, 
51.7672966, 51.76661139, 51.76598088)), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -60L), groups = structure(list(
    Time = c(-25, -23, -22, -20, -20, -14, -14, -11, -11, -11, 
    -10, -10, -10), Code = c(1L, 1L, 1L, 1L, 3L, 1L, 2L, 1L, 
    2L, 3L, 1L, 2L, 3L), .rows = structure(list(1L, 19:20, 2:4, 
        22L, 21L, c(7L, 8L, 11L, 12L, 13L, 14L, 15L, 16L, 17L
        ), c(5L, 6L, 9L, 10L, 18L), 23:30, 32L, 31L, c(33L, 35L, 
        41L, 42L, 43L, 45L, 47L, 50L, 52L, 53L, 58L, 59L), c(34L, 
        36L, 37L, 38L, 39L, 40L, 44L, 46L, 49L, 55L, 56L, 60L
        ), c(48L, 51L, 54L, 57L)), ptype = integer(0), class = c("vctrs_list_of", 
    "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -13L), .drop = TRUE))

If I was to do this by hand I would use dplyr::filter(Time == "x") then make each plot using leaflet like so

install.packages("leaflet")
library(leaflet)
statecol<- colorFactor(palette = "viridis", df$Code) #create the colour palette

plots<- leaflet() %>% setView(lng = -1.324640, lat = 51.770462, zoom = 13.25)
plots %>% addTiles() %>%
  addCircleMarkers(data = df, label = ~as.character(df$Site), radius = 5, color = ~statecol(Code), stroke = FALSE, fillOpacity = 5) %>%
addLegend('bottomright', pal = statecol, values = df$Code,
          title = 'Codes',
          opacity = 2)

If there is a better solution than a loop I'd be happy to try that too. Hope this is clear and thanks in advance

2 Answers 2

1

One approach would be to put the plotting code in a function which takes a the sole argument a dataframe. To make a map for each unique value of Time you could then split your data by Time and loop over the splitted dataset using the plotting function, where instead of a for loop I use lapply. As a result you get a list with plots for each value of Time:

library(leaflet)
library(dplyr)

df_split <- df %>% 
  ungroup() %>% 
  split(.$Time)

statecol<- colorFactor(palette = "viridis", df$Code) #create the colour palette

plot_fun <- function(x) {
  leaflet() %>% 
    setView(lng = -1.324640, lat = 51.770462, zoom = 13.25) |> 
    addTiles() %>%
    addCircleMarkers(data = x, label = ~as.character(x$Site), radius = 5, color = ~statecol(Code), stroke = FALSE, fillOpacity = 5) %>%
    addLegend('bottomright', pal = statecol, values = x$Code,
              title = 'Codes',
              opacity = 2)  
}

plots <- lapply(df_split, plot_fun)

length(plots)
#> [1] 7

plots[[1]]

EDIT In case you want to keep or use the data from previous plots we could basically use the same code with one small change, i.e. loop over an index and combine (rbind) the datasets up to the index value inside the plotting function:

library(leaflet)
library(dplyr)

df_split <- df %>% 
  ungroup() %>% 
  split(.$Time)

statecol<- colorFactor(palette = "viridis", df$Code) #create the colour palette

plot_fun <- function(ix) {
  x <- do.call(rbind, df_split[seq(ix)])
  leaflet() %>% 
    setView(lng = -1.324640, lat = 51.770462, zoom = 13.25) |> 
    addTiles() %>%
    addCircleMarkers(data = x, label = ~as.character(x$Site), radius = 5, color = ~statecol(Code), stroke = FALSE, fillOpacity = 5) %>%
    addLegend('bottomright', pal = statecol, values = x$Code,
              title = 'Codes',
              opacity = 2)  
}

plots <- lapply(seq_along(df_split), plot_fun)

plots[[3]]

plots[[5]]

Sign up to request clarification or add additional context in comments.

4 Comments

This is great, but I just realised there is a flaw in my plot plan, I also need to keep the previous plots data and add it to the next plot, is that complicated?
See my edit. Hope I got it right.
Hm. Hard to tell what went wrong. I just made an edit to my edit (: and included two of the outputted plots which also shows that the data for plot 3 is also present in plot 5.
Sorry @stefan I deleted the comment that said it wasn't working, there was a section of code that I didn't add, it works perfectly.
1

one approach is to store the Time-wise leaflet maps in a column of your tibble:

## create your base map (only needed once):
base_map <- leaflet() %>% 
               setView(lng = -1.324640, lat = 51.770462, zoom = 13.25) %>%
               addTiles()


all_plots <- 
    df %>% ## df is your tibble given above
    group_by(Time) %>%
    ## compact the data apart from Time as dataframes per Time-group
    ## into column "nested_data"
    nest(nested_data = c(Site, Code, lon, lat)) %>%
    rowwise %>%
    mutate(leaflet_map = list(
               base_map %>%
               addCircleMarkers(data = nested_data, ## the column of dataframes
                                label = ~as.character(Site),
                                radius = 5, 
                                color = ~statecol(Code),
                                stroke = FALSE,
                                fillOpacity = 5
                                )
           )
           ) %>%
           select(Time, leaflet_map)

The resulting tibble can be filtered and iterated (over column leaflet_plot) for automated map display.

## for single map at Time == -25
all_plots %>% 
    filter(Time == -25) %>%
    pull(leaflet_map) %>%
    print


Find solutions to display multiple maps in one go here: Multiple leaflets in a grid

2 Comments

Thanks what I usually do is make all the plots into a GIF
maybe here's some helpful code: stackoverflow.com/questions/33508486/… , or - if no interactivity needed - ggmap might come in handy, esp. the convenient saving in var. file formats: cfss.uchicago.edu/notes/raster-maps-with-ggmap

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.