Fundraising Visualized: Using Interactive Maps to Surprise, Communicate and Impress

How can we use data visualization to better understand our organizations and our fundraising work? We have the data — wealth screenings, addresses, giving histories, interactions, etc. — but how we look at it can make all the difference.

Data visualizations can help us:

  • Surprise ourselves (think lightbulb, not shock)
  • Communicate to others
  • Impress our boards, bosses and constituents

In this article, I’ll provide three abbreviated examples of how I’ve used interactive maps in our fundraising work at Colby College. I will also demonstrate how you can create your own interactive maps to explore and answer your own spatial research questions. To follow along, you’ll need the R software, which is a free software package for data analysis, as well as RStudio, which provides an easy-to-use interface for programming in R. Tutorials for installing R and RStudio are widely available on the web, including in Modern Dive: An Introduction to Statistical and Data Science via R. Once you have these installed, read ahead for some general tips about making maps in R.

Map-Making in R

All of the code examples in this article are meant to be reproducible. However, you will need to install some free add-on packages using R’s install.packages() function. By installing the tidyverse, leaflet, leaflet.extras and rgdal packages, you should be able to make every map in this article. If you are interested in learning more about interactive graphics in R, I’ll be speaking on this topic at both the OverDRIVE and DRIVE Conferences in March 2019.

The tidyverse and leaflet packages in R make it possible to stack a series of simple steps to build an impressive and complex data visualization. Like Legos, each piece in the map making process easily fits together, and the builder can stack as many pieces as they wish to achieve the desired result.

Throughout the article, we’ll use an example data set that contains information on alumni and parents of a fictional university. A screenshot of the data file is below. Additionally, clicking this link will download an Excel version of the file for you examine, and the script used to create the file is available here.

Taking a look at our example data file, suppose you’d like to map the prospects in the portfolio of Ziyaad el-Salehi one of the hypothetical fundraisers in our example data file. We could combine a number of simple steps to make an interactive map in R:

  1. Load the tidyverse1 and leaflet2 packages
  2. Read in the example data using the read_csv function3
  3. Filter the data to only the prospects assigned to the gift officer
  4. Create our map by calling leaflet()
  5. Add the default base map by calling addTiles()4
  6. Add a circle to the map to indicate the location of each prospect

Each of these steps on their own are relatively straightforward, but when stacked together they create the following:

# Step 1: load packages
# to install these packages you can run install.packages(c("tidyverse", "leaflet"))
library(tidyverse)
library(leaflet)

# Step 2: Read in example data

prospects <-
read_csv
("https://raw.githubusercontent.com/majerus/NEDRA2018/master/example.csv")
prospects %>%
# Step 3: filter data to include only Ziyaad's prospects
filter(officer == "el-Salehi, Ziyaad") %>%
# Step 4: create the map
leaflet() %>%
# Step 5: add the base map

Once you’ve installed the tidyverse and leaflet packages, you can copy and paste the above code directly into a script in RStudio to reproduce the above map. This process is depicted in the following video:

From our map, it looks like Ziyaad’s portfolio may benefit from some geographically focused prospect management work. Want to create these maps for your team? You can get started by swapping out the .csv file with your institution’s data.

The Surprising Map

Data science is not often characterized as surprising. However, data visualizations have a powerful ability to surprise by presenting information in novel, interesting and insightful ways.

When I arrived at Colby four years ago, one of our first projects to leverage interactive maps was to determine where we should be hosting alumni events. It wasn’t surprising that we could have even more events in Boston, but it was surprising that the top location (outside of Boston and NYC) to host an event was Amherst, Massachusetts.

We found this out by mapping every constituent who had engaged with the college in some capacity (e.g., making a gift, attending an event on campus, etc.), but had not attended an alumni event in their area. Our alumni relations team was then able to make a data-informed decision to host an event in Amherst, and it was successful and well attended. Since many of our constituents in this area live in towns surrounding Amherst, we would not have noticed this fact with simple city or zip code level counts.

Let’s look at a similar question using our example data: “Where should we host an event focused on fundraising for our new Math/Science Center?” Exploring the following interactive heat map reveals “hotter” areas (i.e., those appearing in warmer colors) for such an event.

# Step 1: load packages
library(tidyverse)
library(leaflet)
library(leaflet.extras)

# Step 2: Read in example data
prospects <-
read_csv
("https://raw.githubusercontent.com/majerus/NEDRA2018/master/example.csv")

prospects %>%
# Step 3: filter data to include only prospects interested in the Math/Science Center
filter(priority == "Math/Science Center") %>%
# Step 4: Create the map
leaflet() %>%
# Step 5: Add the base map
addTiles() %>%
# Step 6: Add a heatmap with circles with an 8 pixel radius
addHeatmap(lng = ~longitude, lat = ~latitude, radius = 8)

Heatmaps can help us to identify clusters of constituents. If we were to map each constituent as a dot, higher-density areas may not be as obvious, as constituents would overlap. For example, our heatmap reveals clusters of prospects for the new Math/Science Center in several areas beyond the eastern corridor, including Los Angeles, Houston and Cincinnati.

Communicating With Maps

Maps can also help us communicate spatial data more effectively. We all likely work with spatial data — addresses, states, countries and zip codes — on a daily basis. For example, our office used to have a list of the wealthiest zip codes, which was provided to gift officers with the names of prospects in relatively wealthy areas for fundraising outreach.

However, simply maintaining a list of zip codes or delivering a list of prospects in high-income zip codes obscures data from those making data-based decisions. For example, where are these zip codes? Are they close to one another? Are there clusters of prospects in adjacent high-income zip codes?

All these questions are more quickly and effectively answered with an interactive map. Furthermore, that map can be shared to efficiently communicate with the team.

Here is the list of zip codes:

34103, 60558, 07028, 07704, 11530, 78116, 60022, 92091, 12866, 10012, 10028, 10128, 10007, 77056, 10282, 07935, 10003, 19041, 94111, 06840, 20016, 90211, 06853, 02110, 10023, 10019, 08738, 18913, 74103, 33037, 11509, 10528, 10065, 46290, 02199, 02210, 19066, 07632, 48304, 10005, 33480, 29401, 60602, 60603, 60604, 33109, 33131, 33146, 83001, 34102, 34108, 34228, 98004, 38139, 98040, 98164, 44040, 33957, 75225, 94939, 76102, 90265, 92662, 77019, 94027, 94062, 94065, 77401, 60044, 90401, 63124, 33496, 60606, 01984, 55402, 19035, 02111, 75201, 07311, 31561, 72712, 20818, 10075, 73151, 10010, 77010, 10036, 60601, 02116, 33477, 60611, 78701, 07458, 10280, 33786, 34242, 02109, 02108, 78072, 04110, 35223, 06830, 06820, 06870, 33408, 06883, 10069, 08534, 07423, 77005, 15142, 19087, 78205, 94941, 06897, 02025, 34134, 22101, 95014, 07930, 10514, 11557, 75209, 10803, 77382, 32082, 33432, 90024, 90067, 01742, 02492, 37350, 95030, 11771, 94583, 91008, 10577, 10022, 45174, 07043, 10016, 07021, 07901, 02482, 95746, 01890, 34786, 94506, 94104, 91105, 02445, 10510, 63005, 07481, 60305, 33715, 60614, 19807, 10538, 94925, 85262, 06896, 10549, 11545, 10024, 02421, 63131, 45243, 60091, 10964, 01776, 02465, 20007, 95032, 02030, 48302, 78746, 37215, 22066, 10018, 06831, 06877, 33767, 94563, 92660, 90077, 11791, 19312, 07417, 07627, 11568, 10546, 22182, 33139, 10804, 07670, 07924, 11050, 07928, 60184, 11797, 10014, 11753, 91361, 02468, 78738, 20817, 11559, 11576, 55356, 01741, 01772, 01778, 06793, 06824, 11023, 11024, 11030, 11976, 02090, 02420, 02493, 11765, 07090, 19010, 07401, 20816, 19034, 55391, 19085, 20854, 30305, 07733, 07722, 55436, 48009, 60035, 60010, 07931, 07932, 07945, 60043, 13152, 08550, 10504, 10506, 10004, 10013, 10987, 55331, 33149, 22102, 22124, 32789, 20117, 33301, 37205, 33455, 60523, 33469, 20184, 33483, 33140, 63141, 83014, 34145, 34201, 68520, 34236, 38120, 98075, 34996, 58847, 66208, 66211, 98112, 66224, 29482, 28207, 28211, 90049, 89109, 94904, 90210, 76034, 90266, 90272, 90274, 76092, 90402, 92014, 92625, 92651, 92210, 92861, 92130, 92661, 85253, 77024, 91011, 77027, 75230, 91108, 94010, 75205, 94024, 94025, 94028, 94070, 94123, 94129, 94306, 94507, 94611, 94618, 94556, 94303, 33154, 78257, 07046, 07078, 03750, 07039, 07059, 92657, 10576, 78703, 78730, 78733, 85255, 90212, 01773, 02459, 02052, 01944, 10708, 11560, 10017, 10533, 23103, 95120, 89413, 33156, 10536, 02481, 35213, 78231, 10583, 94118, 91302, 94708, 95070, 20814, 91436, 07069, 06073, 06880, 93108, 34105, 32963, 06807, 10580, 08750, 94946, 02467, 94114, 06785, 48301, 77002, 63105, 60521, 07920, 07450, 97034, 98039, 90010, 33316, 55439, 92037, 93953, 08558, 11724, 60093, 11780, 07760, 10011, 60015, 20015, 01770, 60045, 06903, 94549, 94402, 53217, 11733, 02043, 19477, 07934, 06890, 18938, 94304, 94105, 06878, 44022, 07976, 20815, 85266, 11747, 07677, 19333, 89451, 94920, 55424, 08540, 10021, 10597, 01921, 92603, 94301, 19096, 30327, 22207, 10162, 94022, 94115, 95606, 90064.

That’s not very helpful on its own, but let’s take a look at that data on a map.

# Step 1: load packages
library(rgdal) # for working with geojson data files
library(tidyverse)
library(leaflet)

# Step 2: load GeoJSON file for United States zipcodes with mean income >= $200k.
wealthy_zips <-
readOGR("https://raw.githubusercontent.com/majerus/NEDRA2018/master/wealthiest_100_zipcodes_geojson.GeoJSON", verbose = FALSE)

# Step 4: Create the map
prospects %>%
 filter(officer == "el-Salehi, Ziyaad") %>%
leaflet() %>%
# Step 5: Add the base map (using a greyscale map to make zips pop)
addProviderTiles("CartoDB") %>%
# Step 6: plot zip codes on the map
addPolygons(data = wealthy_zips, color = "green")

We can even add another layer to our map to include Ziyaad’s prospects that we looked at earlier.

# Step 1: load packages
library(rgdal) # for working with geojson data files
library(tidyverse)
library(leaflet)

# Step 2: load GeoJSON file for United States zipcodes with mean income >= $200k and prospect data.
wealthy_zips <- readOGR("https://raw.githubusercontent.com/majerus/NEDRA2018/master/wealthiest_100_zipcodes_geojson.GeoJSON", verbose = FALSE)

prospects <-
read_csv("https://raw.githubusercontent.com/majerus/NEDRA2018/master/example.csv")


prospects %>%
# Step 3: filter data to include only Ziyaad's prospects
filter(officer == "el-Salehi, Ziyaad") %>%
# Step 4: create the map
leaflet() %>%
# Step 5: add the base map (using a greyscale map to make zips pop)
addProviderTiles("CartoDB") %>%
# Step 6: add a circle to represent each prospect's location
addCircles(lng = ~longitude, lat = ~latitude, popup = ~name, group = "Prospects") %>%
# Step 7: plot wealth zip codes on the map
addPolygons(data = wealthy_zips, color = "green", group = "Zips") %>%
# Step 8: add a menu to allow users to toggle layers on and off
addLayersControl(overlayGroups = c("Prospects", "Zips"))

Now we have an interactive map that we can use with our gift officer to show not only where prospects are distributed, but also which prospects live in the highest-income zip codes. The menu in the upper right-hand corner can be used to toggle the layers (polygons and circles) of the map on and off. You could expand on this approach in several ways, including adding all of your managed prospects to the map and color coding them by prospect manager. However, to get started, all you need to do is install the required packages and copy and paste the the above code into RStudio. This process is depicted in the following video:

Map to Impress

People tend to be impressed with interactive data visualizations. I suspect that it has something do with a mix of their relative newness, mass customization or the ability to ask your own question of the graphic rather than following along with the creator’s thinking. It may also have something to do with our conditioning to engage with moving objects on screens.

Regardless of the reason, I’ve found that my arguments have been made stronger over the years by using interactive graphics in presentations and reports. Sometimes the graphics make the reports better, but every time, individuals are impressed with graphics. This impression is often generalized to the entirety of the report or argument. I’m not advocating for using interactive graphics to mislead, but rather have observed that putting the extra time into creating such graphics to support my argument, like those included above, is certainly worth the additional time investment.

Get Up and Running in 5 Minutes

So you’ve made it to the end of this article and may be wondering, “Now what?” If you have five more minutes, give the following a shot:

  • Create an account on RStudio Cloud1
  • Create a new R project in RStudio Cloud
  • Copy and paste the code below into an R script (Note: Installing the packages may take a minute or two, but you only need to do this once.)
  • Highlight all of the code and press run
  • Enjoy exploring your first interactive map
# Step 0: install packages (only need to run this once)install.packages(c("tidyverse", "leaflet"))

# Step 1: load packages
library(tidyverse)library(leaflet)

# Step 2: Read in example data
prospects <-
read_csv("https://raw.githubusercontent.com/majerus/NEDRA2018/master/example.csv")

prospects %>%
# Step 3: select the first 500 rows of the data
slice(1:500) %>%
# Step 4: create the map
leaflet() %>%
# Step 5: add the base map
addTiles() %>%
# Step 6: add a circle to represent each prospect's location
addCircles(lng = ~longitude, lat = ~latitude, popup = ~name)

Here’s the whole process with a minute to spare:

If you have any questions or have any issues getting up and running, please contact me at rich.majerus@gmail.com.


 

1 New to the tidyverse? Check out the resources listed on tidyverse.org.

2 New to leaflet? See RStudio’s leaflet website. Looking to learn more about using leaflet in R? See my DataCamp course and project.

3 The example data is randomly generated using this R script. If your data is not geocoded (i.e., does not include latitude and longitude) you can use the geocode function from the ggmap package to geocode addresses or locations. An alternative for geocoding a large number of American addresses is to join on the lat/lon for the zip code centroid which is available in the zipcode package. The center of a person’s zip code is often times “close enough” to their actual location for our purposes.

4 A base map is composed of tiles that are fetched as users zoom and pan your interactive map (e.g., Google Maps). The base map can be changed to highlight different features of the map using the addProviderTiles function from the leaflet package.

5 RStudio Cloud is a service provided by RStudio and can be used for free. RStudio Cloud was used to make the videos in this article. I would recommend using RStudio Cloud if you are new to R, once you become more familiar it would be helpful to install R and RStudio locally on your computer. Prior to using any of your institutional data on a service like RStudio Cloud it would be important to check with your IT department and to review your institution’s data use policies.

 


Rich Majerus, the assistant vice president of donor relations and advancement strategy at Colby College, oversees the advancement operations, prospect research, and donor relations teams and develops new approaches to modeling and visualizing fundraising data. He regularly presents at national conferences, teaches programming workshops, and blogs about data science at www.richmajerus.com. Additionally, Rich is an instructor on DataCamp and a data science mentor with SpringBoard. Prior to his work at Colby, Rich was a founder of Third Coast Analytics (TCA), a data science consulting firm (TCA’s IP was acquired by Capture Higher Ed in 2016). Rich earned his Bachelor of Arts, summa cum laude, in Sociology and Anthropology at Carleton College and a Master of Arts in Sociology from the University of Notre Dame, where he studied in the Center for Research on educational opportunity.

Want to learn a whole lot more about data science? Attend OverDRIVE/ 2019 on March 11 in Baltimore, Maryland. OverDRIVE/ is a specialized, day-long dive into data science happening the day before the DRIVE/ conference. The event is designed to appeal to a broad range of prospect development professionals through engaging and interactive content covering the world of data science.

Recent Stories
Message from the President: Taking Action With Our Strategic Plan

Rethinking Gift Tables

Religious Roots of Charity