Chapter 4 texting
canvass <- read_csv("data/canvassing_results.csv")
## Parsed with column specification:
## cols(
## van_id = col_double(),
## date = col_date(format = ""),
## vol_yes = col_double()
## )
van_names <- read_csv("data/van_names.csv")
## Parsed with column specification:
## cols(
## van_id = col_double(),
## first_name = col_character(),
## last_name = col_character(),
## date_of_birth = col_date(format = ""),
## age = col_double()
## )
turf_lookup <- read_csv("data/van_turf_lookup.csv")
## Parsed with column specification:
## cols(
## van_id = col_double(),
## turf_code = col_character()
## )
universe <- inner_join(canvass, van_names) %>%
group_by(van_id) %>%
# for duplicates grab the one where they indicated vol yes
top_n(1, wt = vol_yes) %>%
# make sure there is only 1 observation per person in the case that
# duplicates had the same vol_yes result
sample_n(size = 1) %>%
ungroup() %>%
#join region for hypothetical polling location / organizer info
left_join(turf_lookup)
## Joining, by = "van_id"
## Joining, by = "van_id"
We want the message to be coming from the proper regional organizing director (ROD).
We will make some fake names for our RODs. We will create a named vector where the name is the turf code and the value is the organizer’s name (sampled from the babynames package / dataset cite here).
organizers <- c("Rosaleen", "Larissa", "Lafayette", "Theo", "Zamere", "Colleen")
names(organizers) <- LETTERS[1:6]
organizers
## A B C D E F
## "Rosaleen" "Larissa" "Lafayette" "Theo" "Zamere" "Colleen"
We will use stringr::str_replace_all()
to create an organizer
column
universe %>%
mutate(organizer = str_replace_all(turf_code, organizers)) %>%
select(organizer, everything())
## # A tibble: 6,004 x 9
## organizer van_id date vol_yes first_name last_name date_of_birth
## <chr> <dbl> <date> <dbl> <chr> <chr> <date>
## 1 Larissa 1 2019-01-05 1 Timika Ehrsam 1970-01-03
## 2 Zamere 2 2019-01-30 1 Johanna Gorden 1973-12-15
## 3 Zamere 3 2019-02-27 0 Parys Stoelting 1997-02-15
## 4 Larissa 4 2019-01-06 0 Lavell Dewall 1992-10-23
## 5 Lafayette 5 2019-03-02 0 Brenisha Pachter 1977-07-05
## 6 Zamere 6 2019-01-13 1 Hoang Millon 1992-02-29
## 7 Larissa 7 2019-01-14 1 Rishith Pisciotto 1987-09-25
## 8 Lafayette 8 2019-01-28 1 Maximilian Arakawa 1999-05-14
## 9 Theo 9 2019-02-18 0 Timmie Schlag 1965-06-12
## 10 Rosaleen 10 2019-02-05 0 Swetha Spreitzer 2000-12-08
## # … with 5,994 more rows, and 2 more variables: age <dbl>, turf_code <chr>
Say each region has their own unique polling location (realistically this will be a much more fine grain dataset that you can join on).
We can specify the polling locations using a case_when()
function call. We will build upon the previous pipe line. In case when you specify a logical statement and then return a value using the ~
—i.e. something == TRUE ~ "if true value"
.
universe_locations <- universe %>%
mutate(organizer = str_replace_all(turf_code, organizers),
polling_place = case_when(
turf_code == "A" ~ "Community Center",
turf_code == "B" ~ "High School",
turf_code == "C" ~ "Town Hall",
turf_code == "D" ~ "Elementary School",
turf_code == "E" ~ "Rotary Club",
turf_code == "F" ~ "Senior Center"
)
)
universe_locations
## # A tibble: 6,004 x 10
## van_id date vol_yes first_name last_name date_of_birth age
## <dbl> <date> <dbl> <chr> <chr> <date> <dbl>
## 1 1 2019-01-05 1 Timika Ehrsam 1970-01-03 49
## 2 2 2019-01-30 1 Johanna Gorden 1973-12-15 46
## 3 3 2019-02-27 0 Parys Stoelting 1997-02-15 22
## 4 4 2019-01-06 0 Lavell Dewall 1992-10-23 27
## 5 5 2019-03-02 0 Brenisha Pachter 1977-07-05 42
## 6 6 2019-01-13 1 Hoang Millon 1992-02-29 27
## 7 7 2019-01-14 1 Rishith Pisciotto 1987-09-25 32
## 8 8 2019-01-28 1 Maximilian Arakawa 1999-05-14 20
## 9 9 2019-02-18 0 Timmie Schlag 1965-06-12 54
## 10 10 2019-02-05 0 Swetha Spreitzer 2000-12-08 19
## # … with 5,994 more rows, and 3 more variables: turf_code <chr>,
## # organizer <chr>, polling_place <chr>
Generally, it is useful to segment texting scripts to allow for more tailored messaging. It is recommended to treat your potential volunteers differently than those who have not indicated a desire to volunteer.
Let’s go ahead and create two different tibbles, one for vol yes and vol no. Based on this, we will create custom scripts.
vol_yes <- filter(universe_locations, vol_yes == 1)
vol_no <- filter(universe_locations, vol_yes == 0)
At this point you should always check to see if your segmentation has missed anyone. The sum of the number of rows in your two tables should add up to the total number of rows in the original tibble (universe_locations
). Let’s perform that sanity check before moving on.
nrow(vol_yes) + nrow(vol_no) == nrow(universe_locations)
## [1] TRUE
This returns TRUE
, we are good to move onward! If there were any missing rows, I would recommend finding a way to incorporate them into some generic universe.
The next step is to create the script. The package glue
allows us to create character strings with the expressions or values from a tibble. Learn more here.
vol_yes_message <- vol_yes %>%
mutate(message = glue::glue("Hi {first_name} this is {organizer} with Abraham Lincoln for the Union! The election is right around the corner. We need all the help we can get, can we count on you to volunteer at {polling_place} on election day?"))
vol_no_message <- vol_no %>%
mutate(message = glue::glue("Hi {first_name} this is {organizer} with Abraham Lincoln for the Union! The election is right around the corner. Your polling location is at the {polling_place}. Can we count on your vote?"))
messages <- bind_rows(vol_yes_message, vol_no_message)
## Warning in bind_rows_(x, .id): Vectorizing 'glue' elements may not preserve
## their attributes
## Warning in bind_rows_(x, .id): Vectorizing 'glue' elements may not preserve
## their attributes
select(messages, message)
## # A tibble: 6,004 x 1
## message
## <chr>
## 1 Hi Timika this is Larissa with Abraham Lincoln for the Union! The elect…
## 2 Hi Johanna this is Zamere with Abraham Lincoln for the Union! The elect…
## 3 Hi Hoang this is Zamere with Abraham Lincoln for the Union! The electio…
## 4 Hi Rishith this is Larissa with Abraham Lincoln for the Union! The elec…
## 5 Hi Maximilian this is Lafayette with Abraham Lincoln for the Union! The…
## 6 Hi Deeksha this is Lafayette with Abraham Lincoln for the Union! The el…
## 7 Hi Therron this is Lafayette with Abraham Lincoln for the Union! The el…
## 8 Hi Lonney this is Lafayette with Abraham Lincoln for the Union! The ele…
## 9 Hi Robby this is Theo with Abraham Lincoln for the Union! The election …
## 10 Hi Siah this is Rosaleen with Abraham Lincoln for the Union! The electi…
## # … with 5,994 more rows
Once you have created your custom messaging you can write this to a csv
and upload it into a peer to peer texting platform like Relay and Hustle. In there you can, hopefully, map the VAN IDs so that the text messages are recorded in VAN (talk to your VAN Admin about setting up these integrations).
One problem that you might face with platforms like Relay and Hustle is that custom fields can have a character limit. There are a few ways to handle this. One way is by recreating the custom messages within the platform themselves. However, I have found this historically somewhat cumbersom. My work around was to split each sentence into it’s own custom field.
We can split the message into the sentences. This will create a list column which we will then unnest (working with list columns by Garret Grolemund).
final_message <- messages %>%
mutate(message = str_split(message, boundary("sentence"))) %>%
unnest() %>%
group_by(van_id) %>%
mutate(message_number = row_number(),
message_number = paste0("message_",message_number)) %>%
ungroup() %>%
spread(message_number, message)
## Warning: `cols` is now required.
## Please use `cols = c(message)`
select(final_message, contains("message_"))
## # A tibble: 6,004 x 4
## message_1 message_2 message_3 message_4
## <chr> <chr> <chr> <chr>
## 1 "Hi Timika this is La… "The election i… We need all the help … <NA>
## 2 "Hi Johanna this is Z… "The election i… We need all the help … <NA>
## 3 "Hi Hoang this is Zam… "The election i… We need all the help … <NA>
## 4 "Hi Rishith this is L… "The election i… We need all the help … <NA>
## 5 "Hi Maximilian this i… "The election i… We need all the help … <NA>
## 6 "Hi Deeksha this is L… "The election i… We need all the help … <NA>
## 7 "Hi Therron this is L… "The election i… We need all the help … <NA>
## 8 "Hi Lonney this is La… "The election i… We need all the help … <NA>
## 9 "Hi Robby this is The… "The election i… We need all the help … <NA>
## 10 "Hi Siah this is Rosa… "The election i… We need all the help … <NA>
## # … with 5,994 more rows
Final step: upload to relay / hustle / whatever platform you use. Blast ’em.