Philly 311 Call Data Analysis
Set Up
Datasets used to analyze Philly 311 calls include:
Philly 311 Dataset, ranged from 12/08/2014 to 09/28/2016;
Philadelphia 2015 crime dataset, data source: OpenDataPhilly;
Philadelphia neighborhood boundary, data source: OpenDataPhilly;
2015 ACS 5-year Estimates, data source: U.S. Census Bureau
# Install packages
library(tidyverse)
library(dplyr)
library(ggplot2)
library(scales)
library(stringr)
library(tidycensus)
library(sf)
library(tigris)
library(viridis)
library(GISTools)
library(leaflet)
options(tigris_class = "sf")
options(tigris_use_cache = TRUE)
# ggplot theme
theme.graph <- function(base_size = 11, base_family = "Arial", lines_lwd = 0.50, plot_grid = TRUE, axis_font = base_family, title_size = base_size*1.5, legend_size = base_size*0.8,
bg_col = "white",title_font = base_family , base_col = "black", axis_lines = TRUE,
minor_grid = ifelse(plot_grid, TRUE, FALSE), vert_grid = ifelse(plot_grid, TRUE, FALSE), ticks_type = "outer", horz_grid = ifelse(plot_grid, TRUE, FALSE), alpha_leg = 0.1, bord_size = 0,
legend_bg = "white", strip_bg = "white", grid_thick = 1,
grid_type = "solid", ticks_xy = "xy", grid_cols = c("grey50", "grey70")){
theme_bw()+
ggplot2::theme(
plot.margin = grid::unit(c(1, 1, .5, .7), "cm"),
text = ggplot2::element_text(family = base_family, size = base_size),
axis.line = element_line(size = ifelse(axis_lines, grid::unit(lines_lwd, "mm"),0), color = "black"),
axis.ticks.length = grid::unit(ifelse(ticks_type == "outer", 0.15, -0.15), "cm"),
axis.ticks.x = element_line(size = ifelse(stringr::str_detect(ticks_xy, "x"), grid::unit(lines_lwd, "cm"),0), color = "black"),
axis.ticks.y = element_line(size = ifelse(stringr::str_detect(ticks_xy, "y"), grid::unit(lines_lwd, "cm") ,0), color = "black"),
axis.text.x = ggplot2::element_text(angle=45,hjust= .5, vjust= .5, size = base_size, colour = base_col , family = axis_font,margin=margin(ifelse(ticks_type == "inner", 11, 5),5,10,5,"pt")),
axis.text.y = ggplot2::element_text(size = base_size, colour = base_col , family = axis_font, margin=margin(5,ifelse(ticks_type == "inner", 11, 5),10,5,"pt")),
axis.title.y = ggplot2::element_text(angle=0,vjust = .5, margin = margin(r = 15),size = base_size, colour = base_col , family = axis_font),
axis.title.x = ggplot2::element_text(size = base_size,colour = base_col ,vjust = -.5, family = axis_font),
panel.background = ggplot2::element_rect(fill = bg_col),
plot.background = ggplot2::element_rect(fill = bg_col),
panel.border = ggplot2::element_rect(colour = "black", fill=NA, size = bord_size),
panel.grid.major.x = ggplot2::element_line(linetype = grid_type,colour = ifelse(vert_grid, grid_cols[1],bg_col), size = ifelse(vert_grid,0.25 * grid_thick, 0)),
panel.grid.minor.x = ggplot2::element_line(linetype = grid_type,colour = ifelse(vert_grid, ifelse(minor_grid, grid_cols[2 - (length(grid_cols) == 1) ],bg_col),bg_col), size = ifelse(vert_grid,0.15* grid_thick, 0)),
panel.grid.major.y = ggplot2::element_line(linetype = grid_type,colour = ifelse(horz_grid, grid_cols[1],bg_col), size = ifelse(horz_grid,0.25* grid_thick, 0)),
panel.grid.minor.y = ggplot2::element_line(linetype = grid_type,colour = ifelse(horz_grid, ifelse(minor_grid, grid_cols[2 - (length(grid_cols) == 1) ],bg_col),bg_col), size = ifelse(horz_grid,0.15* grid_thick, 0)),
plot.title = ggplot2::element_text(face="bold",vjust = 2, colour = base_col , size = title_size, family = title_font),
legend.background = ggplot2::element_rect(fill = scales::alpha(legend_bg, alpha_leg)), legend.key = ggplot2::element_blank(),
legend.text = ggplot2::element_text(size = legend_size, family = base_family),
legend.title = element_blank(),
strip.background = ggplot2::element_rect(colour = strip_bg, fill = strip_bg),
strip.text.x = ggplot2::element_text(size = base_size + 1),
strip.text.y = ggplot2::element_text(size = base_size + 1)
)
}
theme.widegraph<-theme.graph()+
theme(legend.position = c(.9,.55)) +
theme(axis.text.x = element_text(angle = 0, hjust = 1, vjust =.4))
theme.map <-theme(text = element_text(family = "Arial"))+
theme(plot.title =element_text(size=11*1.5),
plot.subtitle = element_text(size=11),
plot.caption = element_text(size = 8),
axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_line(colour = 'transparent'),
panel.grid.minor=element_blank(),
legend.direction = "vertical",
legend.position = "right",
plot.margin = margin(1, 1, 1, 1, 'cm'),
legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))
Characteristics of Philly 311 Calls
Request Time
The Analysis of Philly 311 calls shows that most of service requests are from 9 am to 3 pm during the weekdays, especially on Monday and Tuesday between 9 am and 10 am. Potential Reasons include 311 Call center is closed during weekends and request sent during weekends through mobile app will be transferred to the department together on Monday.
phlcalls$request_date<-substr(phlcalls$requesteddatetime,1,10)
phlcalls$request_date<-as.Date(phlcalls$request_date,"%m/%d/%Y")
phlcalls$request_weekday<-weekdays(phlcalls$request_date)
phlcalls$request_time<-substr(phlcalls$requesteddatetime,12,22)
phlcalls$request_time<-strptime(phlcalls$request_time, format="%I:%M:%S %p")
phlcalls$request_time<-substr(phlcalls$request_time,12,19)
phlcalls$request_year<-substr(phlcalls$request_date,1,4)
daily_counts <- phlcalls%>%
group_by(phlcalls$request_time)%>%
summarise(dailyCounts=n())
daily_counts<-data.frame(daily_counts)
daily_counts<-daily_counts%>%
rename(request_time=phlcalls.request_time,Counts=dailyCounts)%>%
mutate(time=substr(request_time,1,5))
daily_counts2<-daily_counts%>%
group_by(time)%>%
summarise(Tot.counts=sum(Counts))
daily_counts2$time<-strptime(daily_counts2$time,format="%H:%M")
daily_counts2$time<-format(daily_counts2$time, format="%H:%M")
daily_counts2$timelabel<-paste(substr(daily_counts2$time,1,2),":00")
summary<-quantile(daily_counts2$Tot.counts) #3rd Quantile: 324.5
daily_counts2$proxy.count<-daily_counts2$Tot.counts>summary[4]
ggplot(data=daily_counts2,aes(x=time,y=Tot.counts))+geom_col(aes(fill = proxy.count))+
scale_fill_manual(labels=c("Rank Below 75% of Number of 311 Calls","Rank Above 75% of Number of 311 calls "),values = c("#424242","dark red" ))+
labs(x="Time of Day",y="Number of 311 Calls")+
scale_x_discrete(breaks=c("00:00","01:01","02:00",'03:00',"04:00",'05:00','06:00',
'07:00','08:00','09:00','10:00','11:00','12:00','13:00',
'14:00','15:00','16:00','17:00','18:00','19:00','20:00','21:00',
'22:00','23:00'),
labels=unique(daily_counts2$timelabel))+
ggtitle('Philadelphia Number of 311 Calls by Time of Day ')+
theme.graph()
weekly_counts<-phlcalls%>%
group_by(request_weekday,request_time)%>%
summarise(counts=n())
weekly_counts$request_time<-substr(weekly_counts$request_time,1,2)
weekly_counts<-weekly_counts%>%
group_by(request_weekday,request_time)%>%
summarise(counts=sum(counts))
weekly_counts$request_weekday<-factor(weekly_counts$request_weekday,levels=c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"),ordered=TRUE)
weekly_counts$request_time<-paste(weekly_counts$request_time,": 00")
ggplot(data = weekly_counts, mapping = aes(x =request_time, y =request_weekday, fill =counts))+
geom_tile(colour="Black") +
scale_fill_gradientn(breaks=c(38.5,142.5,2628.2,5164),colors=c('#f8edf1','#cf0029','#7b000b','#49000e'))+
labs(x="Time of Day",y="Day of Week")+
ggtitle('Philadelphia Number of 311 Calls by Day of Week ')+
guides(fill=guide_legend(title="Number of 311 Calls"))+
theme.graph()
Services and Responsible Agencies
Considering the reasons why people call 311, Maintenance Residential or Commercial is the most common request except for information request; Of the top 20 service requests, most are related to housing and transportation, which leads to the imbalanced responsible agencies showing in the right graph. Of all the agencies, Street Department and License & Inspections respond to the majority of 311 calls.
Service_counts<-phlcalls%>%
group_by(servicename)%>%
summarise(Counts=n())
Service_counts<-data.frame(Service_counts)
ggplot(Service_counts[-20,],aes(x=reorder(servicename,Counts),y=Counts))+
geom_bar(stat="identity",fill = "dark red")+
labs(x="Names of Requested Service",y="Number of requests")+
ggtitle('Requested Services via 311 Calls in Philadelphia')+
geom_text(aes(label = Counts), hjust = 1.1, color = "#FFFFFF")+
theme.widegraph+
coord_flip() #without information request
Agency_counts<-phlcalls%>%
group_by(agencyresponsible)%>%
summarise(Counts=n())
Agency_counts<-data.frame(Agency_counts)
Agency_counts$agencyresponsible[1]<-"No Information"
ggplot(Agency_counts[Agency_counts$Counts>1670,],aes(x=reorder(agencyresponsible,Counts),y=Counts))+
geom_bar(stat="identity",fill = "dark red")+
labs(x="Names of Responsible Agencies",y="Number of requests")+
ggtitle('Responsible Agencies in Philadelphia (Top 20 service requests)')+
geom_text(aes(label = Counts), hjust = 1.1, color = "#FFFFFF")+
theme.widegraph+
coord_flip() #without information request
Geographical Patterns
Considering the Geographical patterns of 311, the top six census tracts with over 100 service requests per 1000 people is located dispersedly across the Philadelphia, however, there are three high request services cluster areas, which are neighborhoods along Delaware Ave, like Port Richmond; the border area of Center City and South Philadelphia, and West Philadelphia.
census_api_key("2c3e9f9d2d65abab5f7b81fe418054415d363a43",overwrite=TRUE)
acs_variable_list.2015 <- load_variables(2015, #year
"acs5", #five year ACS estimates
cache = TRUE)
acs_vars<-c("B01001_001E" # ACS total Pop estimates
)
acsTracts.2015<-get_acs(geography = "tract",
year=2015,
variables=acs_vars,
geometry=TRUE,
state="PA",
county="Philadelphia",
output= "wide")%>%
dplyr::select(GEOID,NAME,acs_vars)%>%
rename(total.pop.2015 = B01001_001E )%>%
st_as_sf()%>%
st_transform(crs=4326)
philadelphia<-counties('PA')%>%
st_as_sf()%>%
st_transform(crs=4326)%>%
filter(NAME == 'Philadelphia')
geophlcalls<-phlcalls%>%
subset(is.na(censustract)==FALSE)%>%
dplyr::select(servicerequestid,servicename,agencyresponsible,
latitude,longitude,censustract,request_date,
request_weekday,request_time,request_year)%>%
st_as_sf(coords = c("longitude", "latitude"), crs = 4326, agr = "constant")
Tracts.phlcalls <-st_intersection(geophlcalls,acsTracts.2015)
calls.CTs<-Tracts.phlcalls%>%
group_by(GEOID)%>%
summarise(counts=n())%>%
data.frame()%>%
dplyr::select(GEOID,counts)
acsTracts.2015<-left_join(acsTracts.2015,calls.CTs,by='GEOID')
acsTracts.2015$counts.person<-(acsTracts.2015$counts/acsTracts.2015$total.pop.2015)*1000
acsTracts.2015_2<-filter(acsTracts.2015,total.pop.2015>counts) #exclude outliers
acsTracts.2015_2$counts.person.pct<-ecdf(acsTracts.2015_2$counts.person)(acsTracts.2015_2$counts.person)
acsTracts.2015_2$counts.person.pct<-acsTracts.2015_2$counts.person.pct*100
acsTracts.2015_2<-acsTracts.2015_2%>%
mutate(lon=map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]])) #create centroid
ggplot()+
geom_sf(data=acsTracts.2015_2,
aes(fill=counts.person.pct),
color="transparent")+
scale_fill_continuous('Cumulated Percentile of service requests',
low='light pink',
high= 'dark red',
na.value='grey50')+
geom_sf(data=philadelphia,
color='black',
fill='transparent',
size=0.5)+
geom_text(data=acsTracts.2015_2,aes(x=lon,y=lat,label=round(counts.person)),alpha=0.75,size=2,color='white')+
labs(
title = "Numbers of Requested Services via 311 Calls in Philadelphia by Census Tracts"
)+
theme.map
Crime and 311 Calls
Examine 311 data is essential, because it has significant correlation with the crime rate, which means the higher service requests, the higher crime rates in this area. Therefore, one efficient method to reduce the crime rate is to improve city services and 311 helps the city gather information on lacked city services from the public.
geocalls.2015<-phlcalls%>%
filter(is.na(censustract)==FALSE & request_year==2015)%>%
dplyr::select(servicerequestid,servicename,agencyresponsible,
latitude,longitude,censustract,request_date,
request_weekday,request_time,request_year)%>%
st_as_sf(coords = c("longitude", "latitude"), crs = 4326, agr = "constant")
Tracts.phlcalls.2015 <-st_intersection(geocalls.2015,acsTracts.2015)
calls.CTs.2015<-Tracts.phlcalls.2015%>%
group_by(GEOID)%>%
summarise(counts.2015=n())%>%
data.frame()%>%
dplyr::select(GEOID,counts.2015)
acsTracts.2015<-left_join(acsTracts.2015,calls.CTs.2015,by='GEOID')
acsTracts.2015$counts.person.2015<-(acsTracts.2015$counts.2015/acsTracts.2015$total.pop.2015)*1000
acsTracts.2015_2.2015<-filter(acsTracts.2015,total.pop.2015>counts.2015)
acsTracts.2015_2.2015$counts.person.pct.2015<-ecdf(acsTracts.2015_2.2015$counts.person.2015)(acsTracts.2015_2.2015$counts.person.2015)
acsTracts.2015_2.2015$counts.person.pct.2015<-acsTracts.2015_2.2015$counts.person.pct.2015*100
acsTracts.2015_2.2015<-acsTracts.2015_2.2015%>%
mutate(lon=map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]])) #create centroid
ggplot()+
geom_sf(data=acsTracts.2015_2.2015,
aes(fill=counts.person.pct.2015),
color="transparent")+
scale_fill_continuous('Cumulated Percentile of service requests',
low='light pink',
high= 'dark red',
na.value='grey50')+
geom_sf(data=philadelphia,
color='black',
fill='transparent',
size=0.5)+
geom_text(data=acsTracts.2015_2.2015,aes(x=lon,y=lat,label=round(counts.person.2015)),alpha=0.75,size=2,color='white')+
labs(
title = "Numbers of Requested Services via 311 Calls in Philadelphia by Census Tracts",
subtitle = "2015 Only"
)+
theme.map
geocrime<-crime%>%
dplyr::select(ucr_general,text_general_code,lat,lng)%>%
st_as_sf(coords = c("lng", "lat"), crs = 4326, agr = "constant")
Tracts.crimes.2015 <- st_intersection(geocrime, acsTracts.2015)
crimes.CTs.2015<-Tracts.crimes.2015%>%
group_by(GEOID)%>%
summarise(crime.counts=n())%>%
data.frame()%>%
dplyr::select(GEOID,crime.counts)
acsTracts.2015<-left_join(acsTracts.2015,crimes.CTs.2015,by='GEOID')
acsTracts.2015$crime.person<-(acsTracts.2015$crime.counts/acsTracts.2015$total.pop.2015)*1000
acsTracts.2015.crime<-acsTracts.2015[c(-55,-377,-378,-379,-380,-381,-382,-383),]
acsTracts.2015.crime$crime.person.pct<-ecdf(acsTracts.2015.crime$crime.person)(acsTracts.2015.crime$crime.person)
acsTracts.2015.crime$crime.person.pct<-acsTracts.2015.crime$crime.person.pct*100
acsTracts.2015.crime<-acsTracts.2015.crime%>%
mutate(lon=map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]])) #create centroid
ggplot()+
geom_sf(data=acsTracts.2015.crime,
aes(fill=crime.person.pct),
color="transparent")+
scale_fill_continuous('Cumulated Percentile of crimes',
low='light pink',
high= 'dark red',
na.value='grey50')+
geom_sf(data=philadelphia,
color='black',
fill='transparent',
size=0.5)+
geom_text(data=acsTracts.2015.crime,aes(x=lon,y=lat,label=round(crime.person)),alpha=0.75,size=2,color='white')+
labs(
title = "Numbers of Crimes in Philadelphia by Census Tracts",
subtitle = "2015 Only"
)+
theme.map
ggplot()+
geom_point(data=acsTracts.2015.crime,aes(x=counts.person.2015,y=crime.person))+
geom_smooth(data=acsTracts.2015.crime,aes(x=counts.person.2015,y=crime.person),method='lm')+
scale_x_continuous(limits = c(0, 50))+
scale_y_continuous(limits = c(0, 500),expand=c(0,0))+
labs(x="Number of 311 Calls per 1000 people",
y="Number of Crimes per 1000 people",
caption = "Data Source: OpenDataPhilly")+
theme.graph()+
coord_flip()
cor.test(x=acsTracts.2015.crime[acsTracts.2015.crime$counts.person.2015<=50 & acsTracts.2015.crime$crime.person<=500,]$counts.person.2015,
y=acsTracts.2015.crime[acsTracts.2015.crime$counts.person.2015<=50 & acsTracts.2015.crime$crime.person<=500,]$crime.person,method='pearson')
##
## Pearson's product-moment correlation
##
## data: acsTracts.2015.crime[acsTracts.2015.crime$counts.person.2015 <= and acsTracts.2015.crime[acsTracts.2015.crime$counts.person.2015 <= 50 & acsTracts.2015.crime$crime.person <= 500, ]$counts.person.2015 and 50 & acsTracts.2015.crime$crime.person <= 500, ]$crime.person
## t = 12.367, df = 358, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.4703570 0.6156648
## sample estimates:
## cor
## 0.5471196
Focus Area: University City
University City neighborhood is a focus area across the city, where University of Pennsylvania and Drexel are located, and thousands of students and staffs are living and working here. The safety is the main concern for the students and residents.
acsTracts.2015<-acsTracts.2015%>%
mutate(lon=map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]]))
acsTracts.2015$CT<-substr(as.character(acsTracts.2015$GEOID),6,9)
UniCity.tracts<-acsTracts.2015%>%
subset(CT=='9800' | CT=='0369' | CT=='0090' | CT=='0091' | GEOID =='42101008801' | GEOID=='42101008802' | GEOID=='42101007700')%>%
dplyr::select(-CT)
UniCity.boundary<-Neighborhoods%>%
filter(LISTNAME=='University City')%>%
st_transform(crs = 4326)
ggplot()+
geom_sf(data=Neighborhoods,color='black',fill='transparent',size=0.5)+
geom_sf(data=UniCity.boundary,fill='dark red')+
theme.map
UniCity.tracts<-st_intersection(UniCity.boundary,UniCity.tracts)
UniCity.tracts$CTs<-c('77','88.01','88.02','90','91','369','9800')
UniCity.tracts<-UniCity.tracts%>%
mutate(lon=map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]]))
ggplot()+
geom_sf(data=UniCity.tracts,aes(fill=GEOID))+
geom_text(data=UniCity.tracts,aes(x=lon,y=lat,label=CTs))+
theme.map+
theme(legend.position = 'none')
Services and Agencies
The analysis shows that service requests are mainly related to traffic and streets, with Traffic Signal Emergency and Street Defect standing out as two major requests in the University City. Therefore, Streets Department responds to the vast majority of 311 calls in the University City.
UniCity.phlcalls<-st_intersection(geophlcalls,UniCity.tracts)
UniCity.phlcalls.summary<-UniCity.phlcalls%>%
group_by(servicename,agencyresponsible,GEOID)%>%
summarise(service.counts=n())
UniCity.phlcalls.summary<-data.frame(UniCity.phlcalls.summary)
UniCity.service.summary<-UniCity.phlcalls.summary%>%
group_by(servicename)%>%
summarise(Tot.service.counts=sum(service.counts))
UniCity.service.summary<-data.frame(UniCity.service.summary)
ggplot(UniCity.service.summary,aes(x=reorder(servicename,Tot.service.counts),y=Tot.service.counts))+
geom_bar(stat="identity",fill = "dark red")+
labs(x="Names of Requested Service",y="Number of requests")+
ggtitle('Requested Services via 311 Calls in University City')+
geom_text(aes(label = Tot.service.counts), hjust = 1.1, color = "#FFFFFF")+
theme.widegraph+
coord_flip()
UniCity.agency.summary<-UniCity.phlcalls.summary%>%
group_by(agencyresponsible)%>%
summarise(Tot.agency.counts=sum(service.counts))
UniCity.agency.summary<-data.frame(UniCity.agency.summary)
ggplot(UniCity.agency.summary,aes(x=reorder(agencyresponsible,Tot.agency.counts),y=Tot.agency.counts))+
geom_bar(stat="identity",fill = "dark red")+
labs(x="Names of Responsible Agency",y="Number of requests")+
ggtitle('Responsible Agency in University City')+
geom_text(aes(label = Tot.agency.counts), hjust = 1.1, color = "#FFFFFF")+
theme.widegraph+
coord_flip()
Street Scope Complaints
This map shows the location of different service requests related to street department. From the map, we could find road improvements are needed on Market St, Chestnut St, Walnut St., and Baltimore Ave, especially on Chestnut Street where street light outage is a severe problem for students who walk from or to the school at night and improvement are also needed on intersections along N 38 th street.
UniCity.street<-UniCity.phlcalls%>%
filter(agencyresponsible=='Streets Department')%>%
dplyr::select(servicerequestid,servicename,agencyresponsible,GEOID)%>%
st_drop_geometry()
UniCity.street<-left_join(UniCity.street,phlcalls,by='servicerequestid')
geo_UniCity.street<-SpatialPointsDataFrame(UniCity.street[,c("longitude", "latitude")], UniCity.street)
geo_UniCity.street$servicename.x<-factor(geo_UniCity.street$servicename.x)
domain<-levels(geo_UniCity.street$servicename.x)
pal<-colorFactor(c('antiquewhite1','chartreuse4','chocolate1','chocolate4','blue','cornflowerblue',
'darkgoldenrod1','darkorange2','darkolivegreen1','darkorchid2','darkseagreen3',
'deeppink','gray47','hotpink4','lightpink','lightskyblue4','red'),domain=domain)
leaflet()%>%
addProviderTiles("Stamen.TonerLite") %>%
addCircleMarkers(data=geo_UniCity.street,
lng=~longitude,
lat=~latitude,
stroke=FALSE,
fillColor =~pal(servicename.x),
fillOpacity = 0.8,
popup = ~servicename.x)%>%
leaflet::addLegend("bottomright",
pal=pal,
values=geo_UniCity.street$servicename.x,
labels = levels(geo_UniCity.street$servicename.x),
opacity = 1)
Policy Recommendation
Internal 311 System:
1. Allocate more workforce from 9 am to 3 pm during the weekdays than the rest of time;
2. Train Philly 311 agents with more knowledge related to Streets Department, such as the schedule of trash and recycling collection;
Planning Process:
1. Incorporate Philly311 dataset to citywide planning to help identify the priority areas to be improved;
2. Incorporate Philly 311 dataset to transportation planning to help identify the infrastructures to be improved