Narrative Summary

In this project, I analyze pest sightings in the City of Seattle for the year 2023 using network analysis techniques. The data, provided by a small pest control company in Seattle where I work, includes services for various pests at different locations across the city. Because this data is proprietary, a link to the data will not be provided, but through the analysis, I will show brief segments of the data to ensure the process is clear.

The primary objective of this project is to create a bipartite graph that visualizes the relationships between pest species and neighborhoods, providing insights into pest distribution patterns throughout Seattle. Additional objectives of this project are to analyze seasonal variations in pest sightings and to develop skills in network analysis and visualization techniques. The project seeks to answer research questions such as: What are the most common pest species in Seattle? How do pest sightings vary across different neighborhoods and seasons? Can network visualization techniques effectively highlight patterns in pest distribution?

Because this is a two-mode network analysis, the nodes in the network represent two distinct types: pest species and locations. Each edge in the network represents a sighting of a pest species in a particular neighborhood. It is important to note that a sighting does not necessarily mean a pest was physically seen; rather, it indicates that pest professionals were hired to address the pest issue, whether it was seen, believed to be an issue, or part of a preventative service.

By focusing on visualization strategies for large networks, this project leverages the bipartiteD3 package in R to create interactive and informative visualizations. The analysis includes grouping pests and neighborhoods into broader categories to simplify the visualization and emphasize general trends.

Overall, this project demonstrates the application of network analysis techniques to real-world data, offering a practical perspective on pest management in the City of Seattle.

Setup Process

Packages and Data

# Install necessary packages (uncomment if not already installed)
# install.packages(c("bipartiteD3", "bipartite", "readxl", "r2d3", "tidyverse", "RColorBrewer", "sf", "rnaturalearth", "rnaturalearthdata", "patchwork", "xlsx", "tidygeocoder", "lubridate", "printr"))

# loading the necessary libraries
library(bipartiteD3)        # for creating interactive bipartite graphs
library(bipartite)          # for analyzing bipartite graphs
library(readxl)             # for reading excel files
library(r2d3)               # for creating d3 visualizations
library(tidyverse)          # includes dplyr, ggplot2, and other packages for data manipulation and visualization
library(RColorBrewer)       # for creating custom color palettes
library(sf)                 # for handling spatial data
library(rnaturalearth)      # for downloading natural earth map data
library(rnaturalearthdata)  # companion package for rnaturalearth
library(xlsx)               # for writing data to excel files
library(tidygeocoder)       # for geocoding addresses to obtain latitude and longitude
library(lubridate)          # for handling date and time data
library(printr)             # for printing data frames in a nice format

Data Description

The dataset contains pest sightings for the year 2023 in the City of Seattle. It includes information on various pest species at different locations throughout the city.

Columns seen throughout the analysis process include:

  • Date: Date of the sighting
  • Season: Season when the sighting occurred
  • Address: Location of the sighting
  • Target: Pest species
  • Latitude and Longitude: Geographic coordinates of the location
  • Neighborhood: Grouped neighborhoods in Seattle based on geographic boundaries
  • Shape_area and Shape_len: Area and length of the neighborhood shape, used to create the neighborhood groupings
  • geometry: Created by the sf package to handle spatial data
  • OBJECTID: Unique identifier for each neighborhood (provided by the shapefile data)
  • L_HOOD: Original neighborhood names (provided by the shapefile data)
  • S_HOOD: Sub-neighborhood names (provided by the shapefile data)
  • S_HOOD_ALT: Alternative sub-neighborhood names (provided by the shapefile data)

The original data set contained over 30,000 entries, each representing a service performed throughout the year. Each entry included detailed information such as the name of the individual or business, address, materials used, payment information, specific pest species targeted, as well as other potentially identifying information. For privacy reasons, all personally identifiable information, excluding addressees, were removed.

Scope:

  • Initially, this project covered the greater Seattle area but was ultimately paired down to only include Seattle proper.

Nodes:

  • Various pest species (e.g., ants, rodents, cockroaches, spiders)
  • Locations (initially addresses, later grouped into neighborhoods)

Edges:

  • Service performed for a specific pest species at a particular location (sighting)

Data Import and Preparation

Reading in our Data

The data is first imported from an Excel file and filtered to only include sightings from Seattle addresses.

# setting our working directory and reading the data
setwd("C:/Users/sarah/OneDrive/School/_Network Analysis & Visualization/Final_Project/Seattle Pest Data 2023")
sea_pests <- read_excel("sea_pests.xlsx")

# filtering to only include Seattle addresses
sea_pests <- sea_pests %>%
  filter(grepl("Seattle", Address, ignore.case = TRUE))
# showing the number of rows after filtering (original 30,409 entries)
nrow(sea_pests)
## [1] 12113

Geocoding Addresses

In this step we geocode the addresses to obtain latitude and longitude coordinates for each sighting location. This will allow us to map the sightings more accurately and group them into neighborhoods.

We will use the tidygeocoder package for this task.

# creating a new variable called 'coordinates' that takes the addresses and
# uses tidygeocoder to convert them to lat/long coordinates

# (I am commenting out the below code because it takes a long time to run, and I don't
# want to run it every time I knit the document. I will uncomment it when I need to run it.)
# sea_pests$coordinates <- tidygeocoder::geo(sea_pests$Address)

# renaming the column 'coordinates$lat' to latitude and 'coordinates$long' to longitude
# and only keeping the necessary columns
sea_pests <- sea_pests %>%
  # mutate(Latitude = coordinates$lat,
         # Longitude = coordinates$long) %>%
  select(Date, Target, Latitude, Longitude)
# show all column names to make sure the new columns were added and named correctly
colnames(sea_pests)
## [1] "Date"      "Target"    "Latitude"  "Longitude"

Initial Cleaning and Preparation

In this step, we will do some of the initial work to clean and prepare our data. This includes converting the Date column to a date format, creating a new column for the season based on the date, and checking for missing values.

#  converting the data in our 'Date' column to Date format using the 'lubridate' package
sea_pests <- sea_pests %>%
  mutate(Date = mdy(Date))

# adding a 'Season' column based on the Month
sea_pests <- sea_pests %>%
  mutate(Season = case_when(
    month(Date) %in% c(12, 1, 2) ~ "Winter",
    month(Date) %in% c(3, 4, 5) ~ "Spring",
    month(Date) %in% c(6, 7, 8) ~ "Summer",
    month(Date) %in% c(9, 10, 11) ~ "Fall",
    TRUE ~ NA_character_
  ))
# checking for any NA values in the dataframe
sum(is.na(sea_pests))
## [1] 0
# checking that our date is formatted and our season column was created
sea_pests %>% slice_head(n = 1)
Date Target Latitude Longitude Season
2023-01-02 Rodents 47.69742145 -122.347594768961 Winter

Pest Grouping

In this section, we will group the pest species into broader categories to simplify the analysis and visualization process. This will help us focus on the general trends and patterns of pest populations in Seattle.

We will use the case_when function from the dplyr package to create new categories based on the original noted pest species in the data.

# the names used below to create the broader groups are based on the original pest types that are manually logged when completing a service. These are documented by technicians while they are on site and are, unfortunately, not standardized.
sea_pests <- sea_pests %>%
  mutate(Target = case_when(
    Target %in% c("Ants - Carpenter", "Ants - Nuisance", "Ants - Pavement", 
                  "Ants", "Ants - Crawling Insects", "Ants - Odorous", 
                  "Ants - Moisture", "Ants _ Ants", "Ants - General Ants") ~ "Ants",
    Target %in% c("Rodents - Mice", "Rodents - Rats", "Rodents", "Vertebrate", 
                  "Vertebrate - Large", "Vertebrate - Small", "Vertebrate - General",
                  "Rodents, Rodents - Mice, Rodents - Rats", "Rodents, Rodents - Mice", 
                  "Rodents - Mice, Rodents - Rats", "Rodents - General") ~ "Rodents",
    Target %in% c("Flies - House", "Flying Insects", 
                  "Flying Insects, General insect control") ~ "Flying Insects",
    Target %in% c("Biting Insects - Bed Bugs", "Biting Insects - Fleas", 
                  "Biting Insects - Mites", 
                  "Biting Insects, Biting Insects - Bed Bugs", "Biting Insects, Biting Insects - Fleas", 
                  "Biting Insects - Bed Bugs, General insect control", 
                  "Biting Insects - Fleas, General insect control", 
                  "Biting Insects - Fleas, General insect control, Bed Bugs") ~ "Biting Insects",
    Target %in% c("Stinging Insects", "Wasps", "General insect control, Stinging Insects", 
                  "Stinging Insects, General insect control", "Hornets", 
                  "Hornet - Stinging Insect") ~ "Stinging Insects",
    Target %in% c("Fabric Pests", "Food Storage Pests", "WDO - Beetles", 
                  "WDO - Termites", "Fabric Pests, General insect control", 
                  "Food Storage Pests, General insect control") ~ "Storage and Structure Pests",
    Target == "Cockroaches" ~ "Cockroaches",
    Target == "Spiders" ~ "Spiders",
    TRUE ~ Target  # Do not change other entries
  ))
# creating a table to show the counts of each pest category
sea_pests %>%
  group_by(Target) %>%
  summarise(Count = n(), .groups = 'drop') %>%
  arrange(desc(Count))
Target Count
Rodents 5666
Ants 3783
Cockroaches 1907
Biting Insects 385
Storage and Structure Pests 93
Stinging Insects 89
Spiders 86
Flying Insects 67
Misc Crawling Insects 37

Pest Grouping Summary

Below is a breakdown of the pest categories we have created based on the original pest species in the data, in a way that is easier to read:

Category Types
Ants Carpenter, Nuisance, Pavement, Odorous House, Moisture, and other Misc/General Ants
Rodents Mice, Rats, Misc. Vertebrates
Cockroaches General, German, American, and Oriental Cockroaches
Biting Insects Bed Bugs, Fleas, Mites
Storage and Structure Pests Fabric Pests (moths, carpet beetles), Food Storage Pests (non wood-destroying storage beetles, weevils), Wood-Destroying Beetles, Termites
Stinging Insects Wasps, Hornets
Spiders General Spiders
Flying Insects House, Fruit, and Drain Flies. Other Misc general non-stinging Flying Insects
Misc Crawling Insects Silverfish, Stinkbugs, Other Crawling Insects

Neighborhood Grouping

Now, we group the pest sightings by neighborhoods using the geographic coordinates and a shapefile for neighborhood boundaries in Seattle. This process involves reading the shapefile, transforming the coordinates, and joining the pest data with the neighborhood data.

The packages we use in this section are sf for handling spatial data, rnaturalearth for downloading the shapefile, rnaturalearthdata for accessing the shapefile data, and dplyr for data manipulation.

# reading in the shapefile to define neighborhood boundaries based on coordinates
neighborhoods <- sf::st_read("Neighborhood_Map_Atlas_Neighborhoods.shp")

# checking the coordinate reference systems (CRS) of both datasets
st_crs(sea_pests)  # 4326
st_crs(neighborhoods)  # 2926

# because the CRS of the shapefile is 2926, we need to transform it to match the pest data
neighborhoods <- st_transform(neighborhoods, crs = 4326)

# converting pest data to spatial data frame
pest_sf <- st_as_sf(sea_pests, coords = c("Longitude", "Latitude"), crs = 4326)

# joining the pest data with the neighborhood data
pest_neighborhoods <- st_join(pest_sf, neighborhoods, join = st_within)
# displaying the first few rows to verify the join
head(pest_neighborhoods)
Date Target Season geometry OBJECTID L_HOOD S_HOOD S_HOOD_ALT Shape__Are Shape__Len
2023-01-02 Rodents Winter POINT (-122.3476 47.69742) 118 Northwest Greenwood NA 42044564 26501.53
2023-01-02 Rodents Winter POINT (-122.3476 47.69742) 118 Northwest Greenwood NA 42044564 26501.53
2023-01-03 Rodents Winter POINT (-122.3659 47.56881) 90 Delridge North Delridge Avalon, Luna Park, Pigeon Point 29454657 23549.93
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95

The sub-neighborhood data and the map (Figure 1) used for this step of our analysis can be explored through the Seattle City GIS open data portal.

Seattle Sub-Neighborhoods Map

Figure 1: Seattle Sub-Neighborhoods Map.

Source: Seattle City GIS open data portal.

# segmenting the myriad sub-neighborhoods into 7 distinct neighborhoods

# define a mapping function for neighborhood groupings
get_custom_segment <- function(neighborhood) {
  if (neighborhood %in% c("Loyal Heights", "Ballard", "Whittier Heights", 
                          "West Woodland", "Phinney Ridge", "Wallingford", 
                          "Fremont", "Green Lake", "Broadview", "Bitter Lake", 
                          "Haller Lake", "North Beach/Blue Ridge", "Licton Springs", 
                          "Crown Hill", "Greenwood", "Sunset Hill")) {
    return("NW Seattle")
  } else if (neighborhood %in% c("View Ridge", "Ravenna", "Sand Point", "Bryant", 
                                 "Windermere", "Laurelhurst", "Roosevelt", 
                                 "University of Washington", "Wedgwood", 
                                 "Victory Heights", "Matthews Beach", "Meadowbrook", 
                                 "Olympic Hills", "Cedar Park", "Pinehurst", 
                                 "Maple Leaf", "University District", 
                                 "University Heights")) {
    return("NE Seattle")
  } else if (neighborhood %in% c("East Queen Anne", "West Queen Anne", 
                                 "Lower Queen Anne", "North Queen Anne", 
                                 "Westlake", "Lawton Park", "Briarcliff", 
                                 "Southeast Magnolia", "Interbay")) {
    return("Magnolia/Queen Anne")
  } else if (neighborhood %in% c("Eastlake", "Madrona", "Harrison/Denny-Blaine", 
                                 "Minor", "Leschi", "Mann", "First Hill", 
                                 "Yesler Terrace", "Portage Bay", "Montlake", 
                                 "Madison Park", "Broadway", "Stevens")) {
    return("Central Seattle")
  } else if (neighborhood %in% c("South Lake Union", "Pike-Market", "Belltown", 
                                 "International District", "Central Business District", 
                                 "Pioneer Square", "Denny Triangle")) {
    return("Downtown Seattle")
  } else if (neighborhood %in% c("South Park", "Seaview", "Gatewood", 
                                 "Arbor Heights", "Alki", "North Admiral", 
                                 "Fairmount Park", "Genesee", "Fauntleroy", 
                                 "Highland Park", "North Delridge", "Riverview", 
                                 "High Point", "South Delridge", "Roxhill")) {
    return("West Seattle")
  } else if (neighborhood %in% c("Atlantic", "SODO", "Georgetown", 
                                 "North Beacon Hill", "Mid-Beacon Hill", 
                                 "South Beacon Hill", "Holly Park", "Brighton", 
                                 "Dunlap", "Rainier Beach", "Rainier View", 
                                 "Mount Baker", "Columbia City", "Seward Park", 
                                 "Industrial District", "Harbor Island")) {
    return("South/SE Seattle")
  } else {
    return(NA)
  }
}

# apply the mapping function to create a new column for custom neighborhoods
pest_neighborhoods <- pest_neighborhoods %>%
  mutate(Neighborhood = sapply(S_HOOD, get_custom_segment))
# displaying the first few rows to verify the new column
head(pest_neighborhoods)
Date Target Season geometry OBJECTID L_HOOD S_HOOD S_HOOD_ALT Shape__Are Shape__Len Neighborhood
2023-01-02 Rodents Winter POINT (-122.3476 47.69742) 118 Northwest Greenwood NA 42044564 26501.53 NW Seattle
2023-01-02 Rodents Winter POINT (-122.3476 47.69742) 118 Northwest Greenwood NA 42044564 26501.53 NW Seattle
2023-01-03 Rodents Winter POINT (-122.3659 47.56881) 90 Delridge North Delridge Avalon, Luna Park, Pigeon Point 29454657 23549.93 West Seattle
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95 Central Seattle
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95 Central Seattle
2023-01-03 Cockroaches Winter POINT (-122.3289 47.61368) 100 Capitol Hill Broadway Pike/Pine 30239241 26851.95 Central Seattle

Neighborhood Grouping Summary

The neighborhoods in Seattle have been divided into seven distinct regions based on geographic boundaries and proximity. This division helps in simplifying the analysis and visualization of pest sightings across the city.

The following table summarizes the neighborhoods and their corresponding regions:

Neighborhood Sub-Neighborhoods
NW Seattle Loyal Heights, Ballard, Whittier Heights, West Woodland, Phinney Ridge, Wallingford, Fremont, Green Lake, Broadview, Bitter Lake, Haller Lake, North Beach/Blue Ridge, Licton Springs, Crown Hill, Greenwood, Sunset Hill
NE Seattle View Ridge, Ravenna, Sand Point, Bryant, Windermere, Laurelhurst, Roosevelt, University of Washington, Wedgwood, Victory Heights, Matthews Beach, Meadowbrook, Olympic Hills, Cedar Park, Pinehurst, Maple Leaf, University District, University Heights
Magnolia/Queen Anne East Queen Anne, West Queen Anne, Lower Queen Anne, North Queen Anne, Westlake, Lawton Park, Briarcliff, Southeast Magnolia, Interbay
Central Seattle Eastlake, Madrona, Harrison/Denny-Blaine, Minor, Leschi, Mann, First Hill, Yesler Terrace, Portage Bay, Montlake, Madison Park, Broadway, Stevens
Downtown Seattle South Lake Union, Pike-Market, Belltown, International District, Central Business District, Pioneer Square, Denny Triangle
West Seattle South Park, Seaview, Gatewood, Arbor Heights, Alki, North Admiral, Fairmount Park, Genesee, Fauntleroy, Highland Park, North Delridge, Riverview, High Point, South Delridge, Roxhill
South/Southeast Seattle Atlantic, SODO, Georgetown, North Beacon Hill, Mid-Beacon Hill, South Beacon Hill, Holly Park, Brighton, Dunlap, Rainier Beach, Rainier View, Mount Baker, Columbia City, Seward Park, Industrial District, Harbor Island

Bipartite Graphs and Analysis

We will now create our bipartite graphs to visualize the relationships between pest species and neighborhoods. We will analyze the bipartite network structure and explore the patterns of pest sightings across different regions of Seattle.

We will be using the bipartiteD3 package to create interactive bipartite graphs, the bipartite package for analyzing bipartite networks, and RColorBrewer for creating a custom color palette.

Converting our sf object to a df

# first we have to convert our data, which is currently still an sf object, back to a regular data frame
# in this step we can also use the select function to get rid of the columns we no longer need
sea_pests_df <- as.data.frame(st_drop_geometry(pest_neighborhoods)) %>%
  select(Date, Season, Target, Neighborhood)

Color Customization

# custom colors using RColorBrewer's Paired palette
paired_colors <- brewer.pal(12, "Paired")

# manually selecting colors
custom_colors <- c(
  "Ants" = paired_colors[1],
  "Rodents" = paired_colors[3],
  "Cockroaches" = paired_colors[5],
  "Biting Insects" = paired_colors[4],
  "Spiders" = paired_colors[10],
  "Flying Insects" = paired_colors[9],
  "Storage and Structure Pests" = paired_colors[7],
  "Stinging Insects" = paired_colors[8],
  "Misc Crawling Insects" = paired_colors[6]
)

Bipartite Graph Function

# function to create bipartite graph for a given season using bipartiteD3 with sorting and coloring
create_bipartiteD3_graph <- function(data, season = NULL) {
  if (!is.null(season)) {
    # Filter data for the given season
    data <- data %>% filter(Season == season)
  }
  
  # creating a matrix of sightings
  sightings_matrix <- data %>%
    group_by(Target, Neighborhood) %>%
    summarise(Sightings = n(), .groups = 'drop') %>%
    pivot_wider(names_from = Neighborhood, values_from = Sightings, values_fill = 0) %>%
    column_to_rownames("Target") %>%
    as.matrix()
  
  # sorting primary nodes
  sort_primary <- data %>%
    group_by(Target) %>%
    summarise(Total = n(), .groups = 'drop') %>%
    arrange(desc(Total)) %>%
    pull(Target)
  
  # sorting secondary nodes
  sort_secondary <- data %>%
    group_by(Neighborhood) %>%
    summarise(Total = n(), .groups = 'drop') %>%
    arrange(desc(Total)) %>%
    pull(Neighborhood)
  
  # creating bipartite graph using bipartiteD3 with custom colors
  bipartite_D3(
    sightings_matrix, 
    PrimaryLab = "Pest Type", 
    SecondaryLab = "Neighborhood", 
    SiteNames = if (is.null(season)) "Bipartite Graph of Seattle Pest Sightings in 2023" else paste("Bipartite Graph of Seattle Pest Sightings in", season, "2023"),
    SortPrimary = sort_primary,
    SortSecondary = sort_secondary,
    colouroption = "manual",
    NamedColourVector = custom_colors,
    ColourBy = 1,
    PercentageDecimals = 2,
    MainFigSize = c(800, 475),      # main figure size
    IndivFigSize = c(200, 400),     # individual figure size
    BoxLabPos = c(35, 35),          # label positions (excluding SiteNames, PrimaryLab, and SecondaryLab)
    PercPos = c(240, 210),          # percentage positions
    MinWidth = 0,                   # min width of bars
    Pad = 8                         # padding between bars
  )
}

Bipartite Graph for Entire 2023 Year

Now that we have our Bipartite Function made we will start by creating a bipartite graph for the entire year 2023 to visualize the overall patterns of pest sightings in Seattle.

The graph will show the relationships between pest species and neighborhoods, highlighting the distribution of sightings across different regions of the city.

The percentages on the graph represent the proportion of sightings for each pest species in a particular neighborhood. You can hover over a bar to see a better view of a specific distribution (on either the pest or neighborhood side).

The Pest Types are color-coded for easy identification, and both node types are sorted based on the total distribution percentages (descending).

Bipartite Graph for the Entire Year 2023

# creating a bipartite graph for the entire year
create_bipartiteD3_graph(sea_pests_df)

Key Observations for 2023 Graph:

  • Rodents were the most frequently reported pest type, accounting for 46.78% of all sightings. They were predominantly found in the South/SE Seattle and the Central Seattle neighborhoods.
  • Ants were the second most common pest type, comprising 31.23% of sightings, with a significant presence in West Seattle.
  • Cockroaches made up 15.74% of sightings, with notable concentrations in Downtown Seattle and South/SE Seattle..
  • Biting Insects, Storage and Structure Pests, Stinging Insects, Spiders, Flying Insects, and Misc Crawling Insects were less frequently reported, collectively accounting for less than 10% of all sightings.

Seasonal Bipartite Graphs

We will now create bipartite graphs for each season to explore the seasonal variations in pest sightings across Seattle. The graphs will show the distribution of pest species in different neighborhoods during each season.

Winter Bipartiate Graph

# creating bipartite graphs for each season
create_bipartiteD3_graph(sea_pests_df, season = "Winter")

Key Observations for Winter Graph:

  • Rodents were the most frequently reported pest type, accounting for 54.58% of all sightings.
    • Predominantly found in South/SE Seattle (27.71%) and Central Seattle (15.62%).
  • Ants made up 21.80% of sightings.
    • Significant presence in West Seattle (40.75%) and S/SE Seattle (17.18%).
  • Cockroaches comprised 18.59% of sightings.
    • Concentrated in Downtown Seattle (28.98%) and S/SE Seattle (23.22%).
  • Biting Insects, Storage and Structure Pests, Spiders, Flying Insects, and Misc Crawling Insects were less frequently reported, collectively accounting for less than 10% of all sightings.

Spring Bipartiate Graph

# creating bipartite graphs for each season
create_bipartiteD3_graph(sea_pests_df, season = "Spring")

Key Observations for Spring Graph:

  • Rodents were the most frequently reported pest type, accounting for 46.52% of all sightings.
    • Predominantly found in South/SE Seattle (24.73%) and West Seattle (17.14%).
  • Ants made up 35.79% of sightings.
    • Significant presence in West Seattle (34.02%) and South Seattle (20.58%).
  • Cockroaches comprised 13.93% of sightings.
    • Concentrated in Downtown Seattle (28.99%) and Central Seattle (17.63%).
  • Biting Insects, Storage and Structure Pests, Spiders, Flying Insects, and Misc Crawling Insects were less frequently reported, collectively accounting for less than 10% of all sightings.

Summer Bipartite Graph

# creating bipartite graphs for each season
create_bipartiteD3_graph(sea_pests_df, season = "Summer")

Key Observations for Summer Graph:

  • Ants were the most frequently reported pest type, accounting for 40.45% of all sightings.
    • Predominantly found in West Seattle (44.67%).
  • Rodents made up 39.80% of sightings.
    • Significant presence in South/SE Seattle (27.54%).
  • Cockroaches comprised 13.23% of sightings.
    • Concentrated in Downtown Seattle (32.39%).
  • Biting Insects, Storage and Structure Pests, Spiders, Flying Insects, and Misc Crawling Insects were less frequently reported, collectively accounting for less than 10% of all sightings.

Fall Bipartite Graph

# creating bipartite graphs for each season
create_bipartiteD3_graph(sea_pests_df, season = "Fall")

Key Observations for Fall Graph:

  • Rodents were the most frequently reported pest type, accounting for 47.21% of all sightings.
    • Predominantly found in South/SE Seattle (30.46%).
  • Ants made up 25.83% of sightings. Down from their Summer high.
    • Significant presence in West Seattle (45.84%).
  • Cockroaches comprised 17.52% of sightings.
    • Concentrated in Downtown Seattle (30.54%) and South/SE Seattle (21.98%).
  • Biting Insects, Storage and Structure Pests, Spiders, Flying Insects, and Misc Crawling Insects were less frequently reported, collectively accounting for less than 10% of all sightings.

Network Analysis: A Focus on Visualization

I found the Bipartite graphs to be particularly useful for representing my two-mode network, where I had two distinct sets of nodes and my edges connected these nodes from one set to the other. This visualization technique allowed me to clearly see the relationships between pest species and neighborhoods, highlighting the distribution patterns and prevalence of different pests across Seattle.

Methodology and Tools Used

To simplify the visualization and make the graphs more readable, we grouped the pest species and neighborhoods into broader categories. This aggregation helps in focusing on general trends and patterns rather than getting lost in the details of individual pest species or sub-neighborhoods.

  • Pest Grouping: We categorized the pests into broader groups such as Rodents, Ants, Cockroaches, Biting Insects, and others. This was done using the case_when function from the dplyr package.
  • Neighborhood Grouping: Similarly, we grouped the neighborhoods into seven distinct regions based on geographic boundaries and proximity, such as NW Seattle, NE Seattle, Magnolia/Queen Anne, Central Seattle, Downtown Seattle, West Seattle, and South/SE Seattle.

Visualization Package

I used the bipartiteD3 package in R to create interactive bipartite graphs. This package allows for dynamic and visually appealing network visualizations, which can help in better understanding the relationships and patterns within the data.

I found some very useful information from this site to help me get my bipartite plots formatted and working.

Strengths:
  • Interactive Visualization: The bipartiteD3 package allows for dynamic interaction with the graph, making it easier to explore and understand the data.
  • Color-Coding: The ability to color-code the nodes based on categories helps in distinguishing between different pest species and neighborhoods.
  • Sorting: The option to sort the nodes based on the total distribution percentages provides a clearer view of the most prevalent pests and neighborhoods.
Limitations:
  • Title and Main Labels: The bipartiteD3 package has some limitations when it comes to customizing the title and main labels (“Pest Type” and “Neighborhood”). These elements are not as clear as they could be due to restrictions in the package’s settings.
  • Graph Width: Adjusting the graph width to space out the nodes further apart caused issues with the alignment of the title and main labels. Without editing the underlying CSS and HTML files, which requires additional expertise/time, these adjustments were not feasible within the scope of this project.
  • Customization Complexity: Just in general, more time would have been needed in order to fully customize the graph(s) to my liking. I would have liked to have more control over the overall appearance of the graph(s), but given that I have little experience with CSS and HTML, I was limited in what I could do. Luckily, the base formatting of the bipartiteD3 package is still quite nice and I was still able to make some adjustments.

Conclusion

This project has given me great insights into the complexities of data cleaning, preparation, and visualization. I discovered that the majority of my time, roughly 90%, was spent on cleaning and preparing the data for analysis. I encountered several roadblocks during this process, requiring creative workarounds to ensure the data was accurate and usable.

Initially, I experimented with other visualization methods such as igraph and ggplot2 for the bipartite graph. However, I found that bipartiteD3 provided the most effective way to visualize the relationships between pest species and neighborhoods dynamically. The interactivity and customization options offered by bipartiteD3 made it the ideal choice for this project.

Despite my efforts, I ran out of time to incorporate a more detailed geospatial analysis/visualization beyond the neighborhood classifications. I also intended to apply some of the network measures learned in class to this data, but time constraints prevented me from doing so. Still, I believe the visualizations created here provided valuable information and a strong foundation for future work.

Working with real-world data from my own job was an incredible opportunity. It allowed me to apply theoretical concepts to practical problems, enhancing my understanding and skills in data analysis and visualization. Although this project is being submitted now, I plan to continue refining and expanding upon it.

Future work will include improving the formatting and content of this RMarkdown document, which currently feels rushed and cluttered in parts. I aim to reduce redundancy and enhance its clarity. Beyond these initial changes, I am excited to continue exploring network measures and I will attempt to apply them to this dataset, hopefully, gaining additional information that I will find useful. Incorporating more detailed geospatial analysis/visualization aspects is also a goal of mine, as I think it would be interesting to get an alternative view/perspective of the spatial distribution of pests in Seattle.

Ultimately, while I encountered several limitations primarily related to time, I believe that with patience and a willingness to continue learning, these barriers will not severely restrict the future developments of my work with this data. What I learned from this project will guide me in my continued exploration and application of network analysis and visualization techniques.

This project was really fun, and I am excited to continue working on it. Thank you for another great quarter!