Visualizando data a partir de atributos temporales y espaciales

-Datos sobre la seguridad descargados de data.cityofnewyork.us-

library(lubridate)
## Warning: package 'lubridate' was built under R version 3.6.1
## 
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
## 
##     date
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 3.6.1
## -- Attaching packages ------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.2.0     v purrr   0.3.2
## v tibble  2.1.3     v dplyr   0.8.3
## v tidyr   0.8.3     v stringr 1.4.0
## v readr   1.3.1     v forcats 0.4.0
## Warning: package 'tibble' was built under R version 3.6.1
## Warning: package 'tidyr' was built under R version 3.6.1
## Warning: package 'readr' was built under R version 3.6.1
## Warning: package 'purrr' was built under R version 3.6.1
## Warning: package 'dplyr' was built under R version 3.6.1
## Warning: package 'stringr' was built under R version 3.6.1
## Warning: package 'forcats' was built under R version 3.6.1
## -- Conflicts ---------------------------------------------------------- tidyverse_conflicts() --
## x lubridate::as.difftime() masks base::as.difftime()
## x lubridate::date()        masks base::date()
## x dplyr::filter()          masks stats::filter()
## x lubridate::intersect()   masks base::intersect()
## x dplyr::lag()             masks stats::lag()
## x lubridate::setdiff()     masks base::setdiff()
## x lubridate::union()       masks base::union()
library(sf)
## Warning: package 'sf' was built under R version 3.6.1
## Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(ggmap)
## Warning: package 'ggmap' was built under R version 3.6.1
## Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
## Please cite ggmap if you use it! See citation("ggmap") for details.
library(osmdata)
## Warning: package 'osmdata' was built under R version 3.6.1
## Data (c) OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright

Vamos a analizar la ocurrencia de los delitos en Manhattan, contando con los datos abiertos de la Ciudad de Nueva York, a partir de sus atributos de tiempo y espacio.

NYCPD <- read.csv("NYCPD.csv", encoding = "UTF-8")
NYPD_MAN <- NYCPD %>%
  filter(BORO_NM == "MANHATTAN")

summary(NYPD_MAN)
##    CMPLNT_NUM         ADDR_PCT_CD             BORO_NM     
##  Min.   :100001492   Min.   :  1.0                :    0  
##  1st Qu.:326521944   1st Qu.: 13.0   BRONX        :    0  
##  Median :552272376   Median : 18.0   BROOKLYN     :    0  
##  Mean   :551785160   Mean   : 18.5   MANHATTAN    :55666  
##  3rd Qu.:777966794   3rd Qu.: 25.0   QUEENS       :    0  
##  Max.   :999983972   Max.   :115.0   STATEN ISLAND:    0  
##                                                           
##      CMPLNT_FR_DT     CMPLNT_FR_TM       CMPLNT_TO_DT     CMPLNT_TO_TM  
##  01/01/2019:  442   12:00:00: 1476             : 7087           : 7066  
##  05/21/2019:  377   15:00:00: 1162   06/11/2019:  340   12:00:00:  842  
##  04/24/2019:  375   16:00:00: 1046   05/23/2019:  330   15:00:00:  783  
##  06/12/2019:  375   17:00:00: 1006   02/14/2019:  326   16:00:00:  703  
##  06/21/2019:  375   18:00:00:  982   05/21/2019:  324   17:00:00:  641  
##  04/19/2019:  362   14:00:00:  910   05/31/2019:  322   18:00:00:  631  
##  (Other)   :53360   (Other) :49084   (Other)   :46937   (Other) :45000  
##   CRM_ATPT_CPTD_CD    HADEVELOPT     HOUSING_PSA    JURISDICTION_CODE
##  ATTEMPTED: 1070           :53322   Min.   :  544   Min.   : 0.000   
##  COMPLETED:54596   GRANT   :  185   1st Qu.:  613   1st Qu.: 0.000   
##                    BARUCH  :  161   Median :  670   Median : 0.000   
##                    WALD    :  128   Mean   : 7687   Mean   : 1.311   
##                    SMITH   :  124   3rd Qu.:  752   3rd Qu.: 0.000   
##                    DOUGLASS:  123   Max.   :48889   Max.   :97.000   
##                    (Other) : 1623   NA's   :50529                    
##                JURIS_DESC        KY_CD             LAW_CAT_CD   
##  N.Y. POLICE DEPT   :46324   Min.   :102.0   FELONY     :16876  
##  N.Y. HOUSING POLICE: 5107   1st Qu.:116.0   MISDEMEANOR:31359  
##  N.Y. TRANSIT POLICE: 2984   Median :341.0   VIOLATION  : 7431  
##  PORT AUTHORITY     :  517   Mean   :300.4                      
##  OTHER              :  494   3rd Qu.:351.0                      
##  NYC PARKS          :   59   Max.   :678.0                      
##  (Other)            :  181                                      
##    LOC_OF_OCCUR_DESC                          OFNS_DESC    
##             : 9988   PETIT LARCENY                 :13459  
##  FRONT OF   : 9657   GRAND LARCENY                 : 7453  
##  INSIDE     :34716   HARRASSMENT 2                 : 7328  
##  OPPOSITE OF:  875   ASSAULT 3 & RELATED OFFENSES  : 5535  
##  OUTSIDE    :    0   CRIMINAL MISCHIEF & RELATED OF: 4654  
##  REAR OF    :  430   OFF. AGNST PUB ORD SENSBLTY & : 2360  
##                      (Other)                       :14877  
##                    PARKS_NM                       PATROL_BORO   
##                        :55108   PATROL BORO MAN SOUTH   :28061  
##  CENTRAL PARK          :   79   PATROL BORO MAN NORTH   :27552  
##  WASHINGTON SQUARE PARK:   46   PATROL BORO QUEENS NORTH:   50  
##  MARCUS GARVEY PARK    :   38   PATROL BORO QUEENS SOUTH:    2  
##  UNION SQUARE PARK     :   37   PATROL BORO BRONX       :    1  
##  SARA D. ROOSEVELT PARK:   33                           :    0  
##  (Other)               :  325   (Other)                 :    0  
##      PD_CD                                 PD_DESC     
##  Min.   :101.0   LARCENY,PETIT FROM STORE-SHOPL: 6909  
##  1st Qu.:259.0   HARASSMENT,SUBD 3,4,5         : 4820  
##  Median :339.0   ASSAULT 3                     : 4701  
##  Mean   :401.8   LARCENY,PETIT FROM BUILDING,UN: 3170  
##  3rd Qu.:637.0   HARASSMENT,SUBD 1,CIVILIAN    : 2508  
##  Max.   :922.0   AGGRAVATED HARASSMENT 2       : 2160  
##                  (Other)                       :31398  
##                     PREM_TYP_DESC          RPT_DT     
##  STREET                    :12137   05/28/2019:  420  
##  RESIDENCE - APT. HOUSE    :11278   04/23/2019:  417  
##  RESIDENCE - PUBLIC HOUSING: 5104   06/20/2019:  399  
##  CHAIN STORE               : 3347   05/21/2019:  397  
##  TRANSIT - NYC SUBWAY      : 2956   06/05/2019:  393  
##  COMMERCIAL BUILDING       : 2282   06/11/2019:  393  
##  (Other)                   :18562   (Other)   :53247  
##                          STATION_NAME   SUSP_AGE_GROUP 
##                                :52674   UNKNOWN:18302  
##  125 STREET                    :  272   25-44  :13979  
##  14 STREET                     :  159          :11680  
##  34 ST.-PENN STATION           :  130   45-64  : 5590  
##  42 ST.-PORT AUTHORITY BUS TERM:  125   18-24  : 4219  
##  116 STREET                    :  119   <18    : 1392  
##  (Other)                       : 2187   (Other):  504  
##           SUSP_RACE     SUSP_SEX  TRANSIT_DISTRICT VIC_AGE_GROUP  
##  BLACK         :16337    :11680   Min.   :1.00     UNKNOWN:20800  
##                :11680   F: 7407   1st Qu.:2.00     25-44  :17392  
##  UNKNOWN       :11677   M:26566   Median :2.00     45-64  : 8558  
##  WHITE HISPANIC: 7021   U:10013   Mean   :2.56     18-24  : 4927  
##  WHITE         : 5102             3rd Qu.:4.00     65+    : 2195  
##  BLACK HISPANIC: 2570             Max.   :4.00     <18    : 1788  
##  (Other)       : 1279             NA's   :52674    (Other):    6  
##                            VIC_RACE     VIC_SEX     X_COORD_CD     
##  AMERICAN INDIAN/ALASKAN NATIVE:  315   D:12625   Min.   : 979277  
##  ASIAN / PACIFIC ISLANDER      : 3384   E: 6622   1st Qu.: 987073  
##  BLACK                         : 9799   F:19139   Median : 991136  
##  BLACK HISPANIC                : 2046   M:17280   Mean   : 992598  
##  UNKNOWN                       :21682             3rd Qu.: 998954  
##  WHITE                         :10847             Max.   :1041879  
##  WHITE HISPANIC                : 7593             NA's   :1        
##    Y_COORD_CD        Latitude       Longitude     
##  Min.   :187930   Min.   :40.68   Min.   :-74.02  
##  1st Qu.:208911   1st Qu.:40.74   1st Qu.:-73.99  
##  Median :217993   Median :40.77   Median :-73.98  
##  Mean   :220743   Mean   :40.77   Mean   :-73.97  
##  3rd Qu.:231883   3rd Qu.:40.80   3rd Qu.:-73.95  
##  Max.   :261034   Max.   :40.88   Max.   :-73.79  
##  NA's   :1        NA's   :1       NA's   :1       
##                           Lat_Lon     
##  (40.750430768, -73.989282176):  728  
##  (40.787874599, -73.928181646):  301  
##  (40.756266207, -73.990501248):  261  
##  (40.795581553, -73.932297815):  256  
##  (40.808374136, -73.946885823):  217  
##  (40.804384046, -73.937421669):  184  
##  (Other)                      :53719

A los paquetes con los que venimos trabajando, le sumamos lubridate para trabajar con los atributos de tiempo.

NYPD_MAN <- NYPD_MAN %>%
  mutate(CMPLNT_FR_DT = mdy(CMPLNT_FR_DT)) %>%
  mutate(CMPLNT_FR_DT = ymd(CMPLNT_FR_DT))
set.seed("99")

muestra_fechasMan <- NYPD_MAN %>% 
    sample_n(5) %>% 
    pull(CMPLNT_FR_DT)

muestra_fechasMan
## [1] "2019-01-02" "2019-02-11" "2019-03-21" "2019-06-12" "2019-06-07"

Análisis temporal

options(scipen = 20)

NYPD_MAN19 <- NYPD_MAN %>% 
    filter(year(CMPLNT_FR_DT) == 2019)

ggplot(NYPD_MAN19) + 
    geom_bar(data = NYPD_MAN19, aes(x = month(CMPLNT_FR_DT)), fill = "lightcyan4") +
  labs(title = "Cantidad de delitos por mes",
       subtitle = "Registro año 2019 - Manhattan",
       caption = "Fuente: https://data.cityofnewyork.us",
       x = "Mes",
       y = "Cantidad") +
  theme_minimal()

Hacemos una comparación con la cantidad de delitos ocurridos durante el primer semestre de 2018.

NYPD_MAN18 <- NYPD_MAN %>% 
  filter(year(CMPLNT_FR_DT)==2018, month(CMPLNT_FR_DT)==1:6)
## Warning in month(CMPLNT_FR_DT) == 1:6: longitud de objeto mayor no es
## múltiplo de la longitud de uno menor
ggplot(NYPD_MAN18) + 
    geom_bar(data = NYPD_MAN18, aes(x = month(CMPLNT_FR_DT)), fill = "lightcyan4") +
  labs(title = "Cantidad de delitos por mes",
       subtitle = "Registro año 2018 - Manhattan",
       caption = "Fuente: https://data.cityofnewyork.us",
       x = "Mes",
       y = "Cantidad") +
  theme_minimal()

Podemos ver que de un año a otro la cantidad de delitos registrados en el dataset es mucho mayor, contando el año 2018 con un registro de 50 denuncias y el año 2019 superando las 5000. En este caso podemos intuir que quizás se dió prioridad a pasar datos del año en curso o que hay una carga directa al portal de datos abiertos, pero para nada inferir que la cantidad de delitos denunciados durante el primer semestre del 2018 fue 50. Por ello no podemos hacer comparación y adecuado análisis entre años, por lo que vamos a trabajar con los registros del año 2019.

Veamos los delitos más frecuentes y graficamos en barras por separado un top 5 para observar su distribución en los meses.

NYPD_MAN19 %>% 
    count(OFNS_DESC) %>% 
    top_n(5) %>% 
    arrange(desc(n))
## Selecting by n
## # A tibble: 5 x 2
##   OFNS_DESC                          n
##   <fct>                          <int>
## 1 PETIT LARCENY                  13136
## 2 HARRASSMENT 2                   7080
## 3 GRAND LARCENY                   6909
## 4 ASSAULT 3 & RELATED OFFENSES    5465
## 5 CRIMINAL MISCHIEF & RELATED OF  4479

Guardamos la lista.

NYPD_MANfrec <- NYPD_MAN19 %>% 
    count(OFNS_DESC) %>% 
    top_n(5) %>% 
    pull(OFNS_DESC)
## Selecting by n
NYPD_MAN19 %>% filter(year(CMPLNT_FR_DT) == 2019,
      OFNS_DESC %in% NYPD_MANfrec) %>% 
  
  ggplot() +
  geom_bar(aes(x = month(CMPLNT_FR_DT, label = TRUE), fill = OFNS_DESC), 
           position = "dodge") +
labs(title = "Delitos más frecuentes por mes", 
     subtitle = "Año 2019", 
     caption = "Fuente:https://data.cityofnewyork.us", 
     x = "Mes", 
     y = "Cantidad", 
     fill = "Tipo de delito") +
  theme_minimal()

Se observa que la mayor cantidad de denuncias es el robo o hurto menor, con su mayor registro en el mes de mayo, seguido en los dos primeros meses del año por el robo agravado, siendo este último superado en los siguientes meses por el acoso y abuso sexual.

Ahora vemos la frecuencia por semana en un gráfico de líneas.

conteoMan <-  NYPD_MAN %>% 
    filter(year(CMPLNT_FR_DT) == 2019,
           OFNS_DESC %in% NYPD_MANfrec) %>% 
    count(OFNS_DESC, diasemana = wday(CMPLNT_FR_DT, label = TRUE))
   

ggplot(conteoMan) +
    geom_line(aes(x = diasemana, y = n, group = OFNS_DESC, color = OFNS_DESC), size=1) + 
  geom_point(aes(x = diasemana, y = n, group = OFNS_DESC, color = OFNS_DESC)) +
    scale_color_brewer(palette = "Set1") +
  labs(title = "Cantidad de delitos por día", 
     subtitle = "Año 2019 - Delitos más frecuentes", 
     caption = "Fuente: https://data.cityofnewyork.us", 
     x = "Día/semana", 
     y = "Cantidad", 
     color = "Tipo de delito") +
   theme_minimal()

En concordancia con los gráficos anteriores, el tipo de delito más recurrente en la semana es el hurto o robo menor, seguido por asalto agravado y abuso sexual de manera alternada. Asimismo, podemos ver que los domingos en el día con menor registro para cada uno de estos delitos.

Ahora buscaremos visualizar el registro semanal de manera porcentual.

conteoMan_pct <-  conteoMan  %>% 
    group_by(OFNS_DESC) %>% 
    mutate(pct = n / sum(n) * 100)

ggplot(conteoMan_pct) +
    geom_line(aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC), size=1) + 
  geom_point(aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC)) +
    scale_color_brewer(palette = "Set1") +
  labs(title = "Cantidad de delitos por día porcentual", 
     subtitle = "Año 2019 - Delitos más frecuentes", 
     caption = "Fuente: https://data.cityofnewyork.us", 
     x = "Día/semana", 
     y = "Cantidad %", 
     color = "Tipo de delito") +
   theme_minimal()

En promedio semanal vemos a simple vista que resalta el tipo de asalto/agresión menor y delitos o infracciones conexas (línea roja), siendo los fines de semanas los registros más altos, en contraposición con los otros tipos que bajan su frecuencia en esos días. Es decir, la tendencia de este tipo de delito va a la inversa de los demás tipos de delitos.

Vemos la relación con el tipo de delito de crimen sexual.

conteo_Mancrimen <-  NYPD_MAN %>% 
    filter(year(CMPLNT_FR_DT) == 2019, 
           OFNS_DESC == "SEX CRIMES") %>% 
    count(OFNS_DESC, diasemana = wday(CMPLNT_FR_DT, label = TRUE)) %>%
    group_by(OFNS_DESC) %>% 
    mutate(pct = n / sum(n) *100)
ggplot(conteoMan_pct) +
    geom_line(aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC), size=1) + 
  geom_point(aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC)) +
  geom_line(data = conteo_Mancrimen, aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC), size=1) +
  geom_point(data = conteo_Mancrimen, aes(x = diasemana, y = pct, group = OFNS_DESC, color = OFNS_DESC), size=1) +
    scale_color_brewer(palette = "Set1") +
  labs(title = "Cantidad de delitos por día porcentual", 
     subtitle = "Año 2019 - Delitos más frecuentes", 
     caption = "Fuente: https://data.cityofnewyork.us", 
     x = "Día/semana", 
     y = "Cantidad %", 
     color = "Tipo de delito") +
   theme_minimal()

Agregando el delito del tipo crimen sexual vemos que su registro sigue un patrón similar al de los principales delitos, con un pico los días jueves.

Distribución espacial

Vamos a mostrar la distribución espacial de los delitos. Para ello traemos los mapas necesarios para su localización.

bbox_Man <- getbb("Manhattan New York")
bbox_Man 
##         min       max
## x -74.04722 -73.90616
## y  40.68394  40.88045
mapa_NYC <- get_stamenmap(bbox = bbox_Man,
                           maptype = "toner-lite",
                           zoom = 13)
## Source : http://tile.stamen.com/toner-lite/13/2411/3074.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3074.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3074.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3074.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3075.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3075.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3075.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3075.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3076.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3076.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3076.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3076.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3077.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3077.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3077.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3077.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3078.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3078.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3078.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3078.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3079.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3079.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3079.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3079.png
## Source : http://tile.stamen.com/toner-lite/13/2411/3080.png
## Source : http://tile.stamen.com/toner-lite/13/2412/3080.png
## Source : http://tile.stamen.com/toner-lite/13/2413/3080.png
## Source : http://tile.stamen.com/toner-lite/13/2414/3080.png
NYPD_MANmap <- NYPD_MAN19 %>% 
  filter(!is.na(Latitude), !is.na(Longitude))

nrow(NYPD_MANmap)
## [1] 53490

Vemos su distribución.

ggmap(mapa_NYC) +
  geom_bin2d(data = NYPD_MANmap, aes(x=Longitude, y=Latitude), bins = 100) + 
      labs(title = "Distribución espacial de delitos en Manhattan",
           subtitle = "Año 2019",
         caption = "Fuente: https://data.cityofnewyork.us", 
         fill = "Cantidad") +
  scale_fill_viridis_c() +
  theme_void()
## Warning: Removed 50 rows containing non-finite values (stat_bin2d).

Se pueden ver mayores focos delictivos al sur del Central Park, por la zona de Midtown. Mediante un gráfico del estilo kernel density buscaremos constatar ello.

ggmap(mapa_NYC) +
  geom_density2d(data = NYPD_MANmap, aes(x=Longitude, y=Latitude, color = stat(level))) +
      labs(title = "Distribución espacial de delitos en Manhattan",
           subtitle = "Año 2019",
         caption = "Fuente: https://data.cityofnewyork.us", 
         color = "Cantidad") +
  scale_color_viridis_c() +
  theme_void()
## Warning: Removed 50 rows containing non-finite values (stat_density2d).

Con un facetado veremos la distribución espacial de los delitos frecuentes.

ggmap(mapa_NYC) +
    geom_point(data = filter(NYPD_MAN19, OFNS_DESC %in% NYPD_MANfrec), 
               aes(x = Longitude, y = Latitude, color = OFNS_DESC),
               size = 0.1, alpha = 0.1) +
  guides(color = guide_legend(override.aes = list(size=1, alpha = 1))) +
  labs(title = "Distribución espacial de 5 delitos frecuentes en Manhattan",
           subtitle = "Año 2019",
         caption = "Fuente: https://data.cityofnewyork.us", 
         color = "Tipo de delito") +
    scale_color_brewer(palette = "Set1") +
    facet_wrap(~OFNS_DESC) +
    theme_void()
## Warning: Removed 4 rows containing missing values (geom_point).

ggmap(mapa_NYC) +
  geom_density2d(data = filter(NYPD_MAN19, OFNS_DESC %in% NYPD_MANfrec), aes(x=Longitude, y=Latitude, color = stat(level))) +
      labs(title = "Distribución espacial de 5 delitos frecuentes en Manhattan",
           subtitle = "Año 2019",
         caption = "Fuente: https://data.cityofnewyork.us", 
         color = "Cantidad") +
  scale_color_viridis_c() +
  facet_wrap(~OFNS_DESC) +
  theme_void()
## Warning: Removed 4 rows containing non-finite values (stat_density2d).

En concordancia con las primeras visualizaciones, podemos ver que la espacialidad de los 5 tipos de delitos más frecuentes se localiza al sur del Central Park, por la zona de Midtown, con mayor ocurrencia del robo menoy y agravado.

Ahoramos cruzaremos con la información temporal.

NYPD_MANmap1 <- NYPD_MANmap %>% 
    mutate(dia_semana = wday(CMPLNT_FR_DT, label = TRUE))

ggmap(mapa_NYC) +
  geom_density2d(data = filter(NYPD_MANmap1, OFNS_DESC %in% NYPD_MANfrec), aes(x = Longitude, y = Latitude, color = stat(level))) +
      labs(title = "Distribución espacial de 5 delitos frecuentes en Manhattan",
           subtitle = "Año 2019 - Según día de la semana",
         caption = "Fuente: https://data.cityofnewyork.us", 
         color = "Cantidad") +
  scale_color_viridis_c() +
  facet_wrap(~dia_semana, nrow = 2) +
  theme_void()
## Warning: Removed 4 rows containing non-finite values (stat_density2d).

Al realizar el análisis de la distribución espacial durante la semana, se puede ver el mismo punto caliente en lo que sería el Midtown. Esta mancha se expande más hacia el sur de Manhattan el día sabado.