Code
library(ggplot2)
knitr::opts_chunk$set(tidy = "styler")This page documents some of the steps in our planning process.
W3TM drafted and distributed on 2024-05-28 a member survey using a Google Form. Here are some of the preliminary results.
To reveal the code that generates the following outputs, select the ‘Show All Code’ item from the ‘</> Code’ dropdown menu at upper right.
We source some packages to make calls to their functions more convenient.
The Google Form application saves the survey responses as a Google Sheet:
https://docs.google.com/spreadsheets/d/1Rr2kPK16v0tG-c8dm03gl3jqBNqB_DGyQtVcCVmFadg/edit?resourcekey#gid=283208367
if (!dir.exists("csv")) {
message("Creating missing `csv/`.")
dir.create("csv")
}
update_figs <- TRUE
full_survey_data_fn <- file.path("csv", params$survey_data_fn)
if (params$update_data) {
options(gargle_oauth_email = Sys.getenv("GMAIL_ROG"))
googledrive::drive_auth()
new_fn <- file.path("csv", paste0("_", params$survey_data_fn))
googledrive::drive_download(
params$survey_sheet_fn,
path = full_survey_data_fn,
type = "csv",
overwrite = TRUE
)
message("Data updated.")
} else {
if (!file.exists(full_survey_data_fn)) {
warning("File not found: '", full_survey_data_fn, "'.")
update_figs <- FALSE
} else {
message("Using previously stored data.")
}
}Auto-refreshing stale OAuth token.
File downloaded:
• 'Field Day 2025 volunteer sign-up (Responses)'
<id: 1S6ImyrbuiT_HNMaS50KgfxU0XfCneEAcRP_gmmhda8s>
Saved locally as:
• 'csv/narc-fd-2025-survey.csv'
Data updated.
Now, we load the downloaded CSV
[1] "Timestamp"
[2] "Your Name"
[3] "Callsign"
[4] "Phone"
[5] "Email"
[6] "Do you plan to attend NARC's Field Day 2025?"
[7] "Do you plan to operate on Field Day?"
[8] "Are you a night owl and want to operate late into the night or camp out to snag some rare DX?"
[9] "If you plan to operate, which of the following mode(s) would interest you? \n\nNote: We will have knowledgeable helpers for all modes we use on Field Day, so you are encouraged to try a new mode and will have help if you do so."
[10] "I'm interested in helping with the following activities to promote Field Day and amateur radio to the public..."
[11] "Comments or suggestions"
Then we clean the data by renaming the variables.
survey_resps <- survey_resps |>
dplyr::rename(
timestamp = "Timestamp",
name = "Your Name",
callsign = "Callsign",
phone = "Phone",
email = "Email",
attend = "Do you plan to attend NARC's Field Day 2025?",
operate = "Do you plan to operate on Field Day?",
overnight = "Are you a night owl and want to operate late into the night or camp out to snag some rare DX?",
help_with = "I'm interested in helping with the following activities to promote Field Day and amateur radio to the public...",
comments = "Comments or suggestions"
)
names(survey_resps)[9] <- "modes"Then we parse variables with multiple values, and conforming some values so that they are easier to visualize.
survey_resps <- survey_resps |>
# Change timestamp to proper date and time
dplyr::mutate(timestamp = lubridate::mdy_hms(timestamp, tz = "America/New_York")) |>
# Change values for 'attend' to make them more easily readable
dplyr::mutate(
attend = dplyr::recode(
attend,
"Yes, on Saturday June 28" = "sat",
"Yes, on Sunday June 29" = "sun",
"Yes, on Saturday June 28, Yes, on Sunday June 29" = "sat_sun",
"Not sure yet" = "not_sure",
"Not this year" = "no"
)
) |>
# Change values for 'operate' to make them lower case
dplyr::mutate(operate = tolower(operate)) |>
# Change values for 'overnight'
dplyr::mutate(overnight = dplyr::recode(
overnight,
"Yes, absolutely" = TRUE,
"Heck no" = FALSE
)) |>
# Create new variable for each mode
dplyr::mutate(
phone = stringr::str_detect(modes, "Phone"),
cw = stringr::str_detect(modes, "CW"),
ft8 = stringr::str_detect(modes, "FT8/FT4"),
varac = stringr::str_detect(modes, "VarAC"),
fldigi = stringr::str_detect(modes, "fldigi"),
js8call = stringr::str_detect(modes, "JS8Call"),
sstv = stringr::str_detect(modes, "SSTV")
) |>
# Create new variables for each volunteer role
dplyr::mutate(
pr = stringr::str_detect(help_with, "PR"),
info_table = stringr::str_detect(help_with, "table"),
satellite = stringr::str_detect(help_with, "satellite"),
mode_expert = stringr::str_detect(help_with, "expert"),
ed_activity = stringr::str_detect(help_with, "Educational"),
invite_elected = stringr::str_detect(help_with, "elected"),
youth = stringr::str_detect(help_with, "youth"),
food = stringr::str_detect(help_with, "food"),
clean_up = stringr::str_detect(help_with, "clean-up"),
test_equipment = stringr::str_detect(help_with, "equipment")
)
survey_resps$overnight[is.na(survey_resps$overnight)] <- FALSE
survey_resps$pr[is.na(survey_resps$pr)] <- FALSE
survey_resps$info_table[is.na(survey_resps$info_table)] <- FALSE
survey_resps$satellite[is.na(survey_resps$satellite)] <- FALSE
survey_resps$mode_expert[is.na(survey_resps$mode_expert)] <- FALSE
survey_resps$ed_activity[is.na(survey_resps$ed_activity)] <- FALSE
survey_resps$invite_elected[is.na(survey_resps$invite_elected)] <- FALSE
survey_resps$youth[is.na(survey_resps$youth)] <- FALSE
survey_resps$food[is.na(survey_resps$food)] <- FALSE
survey_resps$clean_up[is.na(survey_resps$clean_up)] <- FALSE
survey_resps$test_equipment[is.na(survey_resps$test_equipment)] <- FALSEIt will be easier if we make separate variables for attendance on Sat and Sun, so we do that now.
We make all the callsigns uppercase for consistency.
Finally, we export the cleaned file.
Here are the number of respondents who plan to attend and their plans to operate.
operate
attend maybe no yes
not_sure 3 0 0
sat 1 1 4
sat_sun 0 0 4
Sorry, not this year 1 2 1
sun 0 0 1
survey_resps |>
dplyr::select(callsign, attend_sat, overnight, attend_sun) |>
dplyr::filter(!is.na(attend_sat), !is.na(overnight), !is.na(attend_sun)) |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(2,
color = ifelse(survey_resps$attend_sat == TRUE, "green", "red")
) |>
kableExtra::column_spec(4,
color = ifelse(survey_resps$attend_sun == TRUE, "green", "red")
) |>
kableExtra::column_spec(3,
color = ifelse(survey_resps$overnight == TRUE, "green", "red")
)| callsign | attend_sat | overnight | attend_sun |
|---|---|---|---|
| W3TM | TRUE | TRUE | TRUE |
| K3CWP | FALSE | FALSE | FALSE |
| N3LI | TRUE | TRUE | TRUE |
| W3EDP | TRUE | FALSE | TRUE |
| WA7HUB | FALSE | FALSE | FALSE |
| K3IWI | TRUE | FALSE | FALSE |
| KF0JZM | TRUE | TRUE | FALSE |
| W3SWL | TRUE | FALSE | FALSE |
| N3IW | TRUE | FALSE | FALSE |
| KC2VCK | FALSE | TRUE | FALSE |
| N3YUW | FALSE | FALSE | FALSE |
| K3FOF | FALSE | FALSE | FALSE |
| K3SKS KC3JAS | FALSE | FALSE | TRUE |
| K9SDW | FALSE | FALSE | FALSE |
| KC3AF | TRUE | FALSE | FALSE |
| W3TRN | TRUE | FALSE | FALSE |
| KD3BHE | FALSE | FALSE | FALSE |
| KM3AJ | TRUE | FALSE | TRUE |
Here are respondents’ interests by mode:
attendees <- survey_resps |>
dplyr::filter(attend_any == TRUE)
modes <- attendees |>
dplyr::filter(
!is.na(callsign),
!is.na(phone),
!is.na(cw),
!is.na(ft8),
!is.na(varac),
!is.na(fldigi),
!is.na(js8call),
!is.na(sstv)
) |>
dplyr::select(callsign, phone, cw, ft8, varac, fldigi, js8call, sstv)
modes |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(column = 2, color = ifelse(modes$phone == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 3, color = ifelse(modes$cw == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 4, color = ifelse(modes$ft8 == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 5, color = ifelse(modes$varac == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 6, color = ifelse(modes$fldigi == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 7, color = ifelse(modes$js8call == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 8, color = ifelse(modes$sstv == TRUE, "green", "red"))| callsign | phone | cw | ft8 | varac | fldigi | js8call | sstv |
|---|---|---|---|---|---|---|---|
| W3TM | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE |
| N3LI | TRUE | FALSE | TRUE | FALSE | TRUE | TRUE | TRUE |
| W3EDP | TRUE | FALSE | TRUE | FALSE | TRUE | FALSE | TRUE |
| K3IWI | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| KF0JZM | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE |
| N3IW | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE |
| K3SKS KC3JAS | TRUE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE |
| KC3AF | FALSE | FALSE | TRUE | FALSE | TRUE | TRUE | FALSE |
| KM3AJ | TRUE | FALSE | FALSE | FALSE | TRUE | TRUE | FALSE |
And here are respondents’ interests in volunteering:
volunteers <- survey_resps |>
dplyr::select(
callsign, pr, info_table, satellite, mode_expert,
ed_activity, invite_elected, youth, food, clean_up, test_equipment
)
volunteers |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(2,
color = ifelse(volunteers$pr == TRUE, "green", "red")
) |>
kableExtra::column_spec(3,
color = ifelse(volunteers$info_table == TRUE, "green", "red")
) |>
kableExtra::column_spec(4,
color = ifelse(volunteers$satellite == TRUE, "green", "red")
) |>
kableExtra::column_spec(5,
color = ifelse(volunteers$mode_expert == TRUE, "green", "red")
) |>
kableExtra::column_spec(6,
color = ifelse(volunteers$ed_activity == TRUE, "green", "red")
) |>
kableExtra::column_spec(7,
color = ifelse(volunteers$invite_elected == TRUE, "green", "red")
) |>
kableExtra::column_spec(8,
color = ifelse(volunteers$youth == TRUE, "green", "red")
) |>
kableExtra::column_spec(9,
color = ifelse(volunteers$food == TRUE, "green", "red")
) |>
kableExtra::column_spec(10,
color = ifelse(volunteers$clean_up == TRUE, "green", "red")
) |>
kableExtra::column_spec(11,
color = ifelse(volunteers$test_equipment == TRUE, "green", "red")
)| callsign | pr | info_table | satellite | mode_expert | ed_activity | invite_elected | youth | food | clean_up | test_equipment |
|---|---|---|---|---|---|---|---|---|---|---|
| W3TM | TRUE | TRUE | FALSE | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE |
| K3CWP | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| N3LI | TRUE | TRUE | FALSE | TRUE | TRUE | TRUE | FALSE | TRUE | TRUE | TRUE |
| W3EDP | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | TRUE | TRUE | TRUE |
| WA7HUB | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| K3IWI | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| KF0JZM | TRUE | FALSE | FALSE | FALSE | FALSE | TRUE | TRUE | FALSE | FALSE | FALSE |
| W3SWL | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| N3IW | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| KC2VCK | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| N3YUW | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE |
| K3FOF | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| K3SKS KC3JAS | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE |
| K9SDW | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE | TRUE | FALSE |
| KC3AF | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | TRUE | FALSE | TRUE |
| W3TRN | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE |
| KD3BHE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
| KM3AJ | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | TRUE | TRUE |
---
title: "Survey"
format:
html:
code-fold: true
code-tools: true
toc: true
params:
update_data: true
data_path: include/csv
survey_data_fn: narc-fd-2025-survey.csv
survey_sheet_fn: "Field Day 2025 volunteer sign-up (Responses)"
---
## About
This page documents some of the steps in our planning process.
W3TM drafted and distributed on 2024-05-28 a member survey using a Google Form.
Here are some of the preliminary results.
::: {.callout-note}
To reveal the code that generates the following outputs, select the 'Show All Code' item from the '</> Code' dropdown menu at upper right.
:::
## Set-up
We source some packages to make calls to their functions more convenient.
```{r}
library(ggplot2)
knitr::opts_chunk$set(tidy = "styler")
```
## Gathering
The Google Form application saves the survey responses as a Google Sheet:
https://docs.google.com/spreadsheets/d/1Rr2kPK16v0tG-c8dm03gl3jqBNqB_DGyQtVcCVmFadg/edit?resourcekey#gid=283208367
```{r}
if (!dir.exists('csv')) {
message("Creating missing `csv/`.")
dir.create("csv")
}
update_figs = TRUE
full_survey_data_fn <- file.path("csv", params$survey_data_fn)
if (params$update_data) {
options(gargle_oauth_email = Sys.getenv("GMAIL_ROG"))
googledrive::drive_auth()
new_fn <- file.path("csv", paste0("_", params$survey_data_fn))
googledrive::drive_download(
params$survey_sheet_fn,
path = full_survey_data_fn,
type = "csv",
overwrite = TRUE
)
message("Data updated.")
} else {
if (!file.exists(full_survey_data_fn)) {
warning("File not found: '", full_survey_data_fn, "'.")
update_figs = FALSE
} else {
message("Using previously stored data.")
}
}
```
Now, we load the downloaded CSV
```{r load-survey-data}
if (update_figs) {
survey_resps <-
readr::read_csv(full_survey_data_fn, show_col_types = FALSE)
}
names(survey_resps)
```
## Cleaning
Then we clean the data by renaming the variables.
```{r rename-variables}
survey_resps <- survey_resps |>
dplyr::rename(
timestamp = "Timestamp",
name = "Your Name",
callsign = "Callsign",
phone = "Phone",
email = "Email",
attend = "Do you plan to attend NARC's Field Day 2025?",
operate = "Do you plan to operate on Field Day?",
overnight = "Are you a night owl and want to operate late into the night or camp out to snag some rare DX?",
help_with = "I'm interested in helping with the following activities to promote Field Day and amateur radio to the public...",
comments = "Comments or suggestions"
)
names(survey_resps)[9] <- "modes"
```
Then we parse variables with multiple values, and conforming some values so that they are easier to visualize.
```{r recode-variables, tidy = "styler"}
survey_resps <- survey_resps |>
# Change timestamp to proper date and time
dplyr::mutate(timestamp = lubridate::mdy_hms(timestamp, tz = "America/New_York")) |>
# Change values for 'attend' to make them more easily readable
dplyr::mutate(
attend = dplyr::recode(
attend,
'Yes, on Saturday June 28' = "sat",
'Yes, on Sunday June 29' = "sun",
'Yes, on Saturday June 28, Yes, on Sunday June 29' = "sat_sun",
'Not sure yet' = "not_sure",
'Not this year' = "no"
)
) |>
# Change values for 'operate' to make them lower case
dplyr::mutate(operate = tolower(operate)) |>
# Change values for 'overnight'
dplyr::mutate(overnight = dplyr::recode(
overnight,
'Yes, absolutely' = TRUE,
'Heck no' = FALSE)) |>
# Create new variable for each mode
dplyr::mutate(
phone = stringr::str_detect(modes, "Phone"),
cw = stringr::str_detect(modes, "CW"),
ft8 = stringr::str_detect(modes, "FT8/FT4"),
varac = stringr::str_detect(modes, "VarAC"),
fldigi = stringr::str_detect(modes, "fldigi"),
js8call = stringr::str_detect(modes, "JS8Call"),
sstv = stringr::str_detect(modes, "SSTV")
) |>
# Create new variables for each volunteer role
dplyr::mutate(
pr = stringr::str_detect(help_with, "PR"),
info_table = stringr::str_detect(help_with, "table"),
satellite = stringr::str_detect(help_with, "satellite"),
mode_expert = stringr::str_detect(help_with, "expert"),
ed_activity = stringr::str_detect(help_with, "Educational"),
invite_elected = stringr::str_detect(help_with, "elected"),
youth = stringr::str_detect(help_with, "youth"),
food = stringr::str_detect(help_with, "food"),
clean_up = stringr::str_detect(help_with, "clean-up"),
test_equipment = stringr::str_detect(help_with, "equipment")
)
survey_resps$overnight[is.na(survey_resps$overnight)] <- FALSE
survey_resps$pr[is.na(survey_resps$pr)] <- FALSE
survey_resps$info_table[is.na(survey_resps$info_table)] <- FALSE
survey_resps$satellite[is.na(survey_resps$satellite)] <- FALSE
survey_resps$mode_expert[is.na(survey_resps$mode_expert)] <- FALSE
survey_resps$ed_activity[is.na(survey_resps$ed_activity)] <- FALSE
survey_resps$invite_elected[is.na(survey_resps$invite_elected)] <- FALSE
survey_resps$youth[is.na(survey_resps$youth)] <- FALSE
survey_resps$food[is.na(survey_resps$food)] <- FALSE
survey_resps$clean_up[is.na(survey_resps$clean_up)] <- FALSE
survey_resps$test_equipment[is.na(survey_resps$test_equipment)] <- FALSE
```
It will be easier if we make separate variables for attendance on Sat and Sun, so we do that now.
```{r}
survey_resps <- survey_resps |>
dplyr::mutate(
attend_sat = stringr::str_detect(attend, "sat"),
attend_sun = stringr::str_detect(attend, "sun")
) |>
dplyr::mutate(attend_any = (attend_sat | attend_sun))
```
We make all the callsigns uppercase for consistency.
```{r}
survey_resps <- survey_resps |>
dplyr::mutate(callsign = toupper(callsign))
```
Finally, we export the cleaned file.
```{r}
readr::write_csv(survey_resps, file.path("csv", paste0("clean_", params$survey_data_fn)))
```
## Visualizing
<!-- As of `{r} Sys.time()`, we've had `{r} dim(survey_resps)[1]` responses. -->
### Attending and operating
Here are the number of respondents who plan to attend and their plans to operate.
```{r}
xtabs(formula = ~ attend + operate, data = survey_resps)
```
```{r}
survey_resps |>
dplyr::select(callsign, attend_sat, overnight, attend_sun) |>
dplyr::filter(!is.na(attend_sat), !is.na(overnight), !is.na(attend_sun)) |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(2,
color = ifelse(survey_resps$attend_sat == TRUE, "green", "red")) |>
kableExtra::column_spec(4,
color = ifelse(survey_resps$attend_sun == TRUE, "green", "red")) |>
kableExtra::column_spec(3,
color = ifelse(survey_resps$overnight == TRUE, "green", "red"))
```
### Modes
Here are respondents' interests by mode:
```{r}
attendees <- survey_resps |>
dplyr::filter(attend_any == TRUE)
modes <- attendees |>
dplyr::filter(
!is.na(callsign),
!is.na(phone),
!is.na(cw),
!is.na(ft8),
!is.na(varac),
!is.na(fldigi),
!is.na(js8call),
!is.na(sstv)
) |>
dplyr::select(callsign, phone, cw, ft8, varac, fldigi, js8call, sstv)
modes |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(column = 2, color = ifelse(modes$phone == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 3, color = ifelse(modes$cw == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 4, color = ifelse(modes$ft8 == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 5, color = ifelse(modes$varac == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 6, color = ifelse(modes$fldigi == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 7, color = ifelse(modes$js8call == TRUE, "green", "red")) |>
kableExtra::column_spec(column = 8, color = ifelse(modes$sstv == TRUE, "green", "red"))
```
### Volunteering
And here are respondents' interests in volunteering:
```{r}
volunteers <- survey_resps |>
dplyr::select(callsign, pr, info_table, satellite, mode_expert,
ed_activity, invite_elected, youth, food, clean_up, test_equipment)
volunteers |>
kableExtra::kbl(format = "html") |>
kableExtra::kable_classic() |>
kableExtra::column_spec(2,
color = ifelse(volunteers$pr == TRUE, "green", "red")) |>
kableExtra::column_spec(3,
color = ifelse(volunteers$info_table == TRUE, "green", "red")) |>
kableExtra::column_spec(4,
color = ifelse(volunteers$satellite == TRUE, "green", "red")) |>
kableExtra::column_spec(5,
color = ifelse(volunteers$mode_expert == TRUE, "green", "red")) |>
kableExtra::column_spec(6,
color = ifelse(volunteers$ed_activity == TRUE, "green", "red")) |>
kableExtra::column_spec(7,
color = ifelse(volunteers$invite_elected == TRUE, "green", "red")) |>
kableExtra::column_spec(8,
color = ifelse(volunteers$youth == TRUE, "green", "red")) |>
kableExtra::column_spec(9,
color = ifelse(volunteers$food == TRUE, "green", "red")) |>
kableExtra::column_spec(10,
color = ifelse(volunteers$clean_up == TRUE, "green", "red")) |>
kableExtra::column_spec(11,
color = ifelse(volunteers$test_equipment == TRUE, "green", "red"))
```
<!-- ## Observations -->
<!-- - We have about half a dozen attendees who want to operate on Saturday and a larger group on Sunday. -->
<!-- - W3TM will have some company (W3EDP and KM4LAO) late at night or overnight. -->
<!-- - We have a number of operators interested in digital (FT8, fldigi, and JS8Call) modes. -->
<!-- - We should have 5-10 helpers to assist with strike/tear-down. -->