1 Ejemplo guiado


No se trata de una pauta para repetir sino diferentes ejemplos para daros ideas e inspiraros.


1.1 Descripción del origen del conjunto de datos


Se ha seleccionado un conjunto de datos del National Highway Traffic Safety Administration. El sistema de informes de análisis de mortalidad fue creado en los Estados Unidos por la National Highway Traffic Safety Administration para proporcionar una medida global de la seguridad en las carreteras. (Fuente Wikipedia). Los datos pertenecen al año 2020. Se trata de un conjunto de registros de accidentes que recogen datos significativos que los describen. Todos los accidentes tienen alguna víctima mortal como mínimo. El objetivo analítico que tenemos en mente es entender que hace que un accidente sea grave y que quiere decir que sea grave. https://www.nhtsa.gov/crash-data-systems/fatality-analysis-reporting-system

1.2 Análisis exploratorio

Queremos hacer una primera aproximación al conjunto de datos escogido y responder a las preguntas más básicas: ¿Cuánto registros tiene? ¿Cuántas variables? ¿De qué tipología son? ¿Cómo se distribuyen los valores de las variables? ¿Hay problemas con los datos, por ejemplo, campos vacíos? ¿Puedo intuir ya el valor analítico de los datos? ¿Qué primeras conclusiones puedo extraer?

El primer paso para realizar un análisis exploratorio es cargar el fichero de datos

#getwd()  # ruta del directorio actual
#path = file.choose()
path = 'accident.CSV'
accidentData <- read.csv(path, row.names=NULL)

1.2.1 Exploración del conjunto de datos

Verificamos la estructura del juego de datos principal. Vemos el número de columnas que tenemos y ejemplos de los contenidos de las filas.

structure = str(accidentData)
## 'data.frame':    35766 obs. of  81 variables:
##  $ STATE       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ STATENAME   : chr  "Alabama" "Alabama" "Alabama" "Alabama" ...
##  $ ST_CASE     : int  10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 ...
##  $ VE_TOTAL    : int  1 4 2 1 1 2 1 2 2 2 ...
##  $ VE_FORMS    : int  1 4 2 1 1 2 1 2 2 2 ...
##  $ PVH_INVL    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ PEDS        : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ PERSONS     : int  4 6 2 5 1 3 1 2 4 3 ...
##  $ PERMVIT     : int  4 6 2 5 1 3 1 2 4 3 ...
##  $ PERNOTMVIT  : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ COUNTY      : int  51 73 117 15 37 103 73 25 45 95 ...
##  $ COUNTYNAME  : chr  "ELMORE (51)" "JEFFERSON (73)" "SHELBY (117)" "CALHOUN (15)" ...
##  $ CITY        : int  0 350 0 0 0 0 330 0 0 1500 ...
##  $ CITYNAME    : chr  "NOT APPLICABLE" "BIRMINGHAM" "NOT APPLICABLE" "NOT APPLICABLE" ...
##  $ DAY         : int  1 2 2 3 4 4 7 8 9 10 ...
##  $ DAYNAME     : int  1 2 2 3 4 4 7 8 9 10 ...
##  $ MONTH       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ MONTHNAME   : chr  "January" "January" "January" "January" ...
##  $ YEAR        : int  2020 2020 2020 2020 2020 2020 2020 2020 2020 2020 ...
##  $ DAY_WEEK    : int  4 5 5 6 7 7 3 4 5 6 ...
##  $ DAY_WEEKNAME: chr  "Wednesday" "Thursday" "Thursday" "Friday" ...
##  $ HOUR        : int  2 17 14 15 0 16 19 7 20 10 ...
##  $ HOURNAME    : chr  "2:00am-2:59am" "5:00pm-5:59pm" "2:00pm-2:59pm" "3:00pm-3:59pm" ...
##  $ MINUTE      : int  58 18 55 20 45 55 23 15 0 2 ...
##  $ MINUTENAME  : chr  "58" "18" "55" "20" ...
##  $ NHS         : int  0 0 0 0 0 0 0 0 0 1 ...
##  $ NHSNAME     : chr  "This section IS NOT on the NHS" "This section IS NOT on the NHS" "This section IS NOT on the NHS" "This section IS NOT on the NHS" ...
##  $ ROUTE       : int  4 6 3 4 4 3 4 4 4 2 ...
##  $ ROUTENAME   : chr  "County Road" "Local Street - Municipality" "State Highway" "County Road" ...
##  $ TWAY_ID     : chr  "cr-4" "martin luther king jr dr" "sr-76" "CR-ALEXANDRIA WELLINGTON RD" ...
##  $ TWAY_ID2    : chr  "" "" "us-280" "" ...
##  $ RUR_URB     : int  1 2 1 1 1 1 2 1 1 1 ...
##  $ RUR_URBNAME : chr  "Rural" "Urban" "Rural" "Rural" ...
##  $ FUNC_SYS    : int  5 4 4 7 5 4 4 5 5 3 ...
##  $ FUNC_SYSNAME: chr  "Major Collector" "Minor Arterial" "Minor Arterial" "Local" ...
##  $ RD_OWNER    : int  2 4 1 2 2 1 4 2 2 1 ...
##  $ RD_OWNERNAME: chr  "County Highway Agency" "City or Municipal Highway Agency" "State Highway Agency" "County Highway Agency" ...
##  $ MILEPT      : int  0 0 49 0 0 390 0 0 0 3019 ...
##  $ MILEPTNAME  : chr  "None" "None" "49" "None" ...
##  $ LATITUDE    : num  32.4 33.5 33.3 33.8 32.8 ...
##  $ LATITUDENAME: chr  "32.43313333" "33.48465833" "33.29994167" "33.79507222" ...
##  $ LONGITUD    : num  -86.1 -86.8 -86.4 -85.9 -86.1 ...
##  $ LONGITUDNAME: chr  "-86.09485" "-86.83954444" "-86.36964167" "-85.88348611" ...
##  $ SP_JUR      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SP_JURNAME  : chr  "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" ...
##  $ HARM_EV     : int  42 12 34 42 42 12 8 12 12 12 ...
##  $ HARM_EVNAME : chr  "Tree (Standing Only)" "Motor Vehicle In-Transport" "Ditch" "Tree (Standing Only)" ...
##  $ MAN_COLL    : int  0 6 0 0 0 2 0 1 1 2 ...
##  $ MAN_COLLNAME: chr  "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" "Angle" "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" ...
##  $ RELJCT1     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ RELJCT1NAME : chr  "No" "No" "No" "No" ...
##  $ RELJCT2     : int  1 1 3 1 1 1 3 1 8 1 ...
##  $ RELJCT2NAME : chr  "Non-Junction" "Non-Junction" "Intersection-Related" "Non-Junction" ...
##  $ TYP_INT     : int  1 1 3 1 1 1 2 1 1 1 ...
##  $ TYP_INTNAME : chr  "Not an Intersection" "Not an Intersection" "T-Intersection" "Not an Intersection" ...
##  $ WRK_ZONE    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ WRK_ZONENAME: chr  "None" "None" "None" "None" ...
##  $ REL_ROAD    : int  4 1 4 4 4 1 1 1 1 1 ...
##  $ REL_ROADNAME: chr  "On Roadside" "On Roadway" "On Roadside" "On Roadside" ...
##  $ LGT_COND    : int  2 3 1 1 2 2 3 1 2 1 ...
##  $ LGT_CONDNAME: chr  "Dark - Not Lighted" "Dark - Lighted" "Daylight" "Daylight" ...
##  $ WEATHER     : int  1 2 2 10 2 1 1 1 10 10 ...
##  $ WEATHERNAME : chr  "Clear" "Rain" "Rain" "Cloudy" ...
##  $ SCH_BUS     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SCH_BUSNAME : chr  "No" "No" "No" "No" ...
##  $ RAIL        : chr  "0000000" "0000000" "0000000" "0000000" ...
##  $ RAILNAME    : chr  "Not Applicable" "Not Applicable" "Not Applicable" "Not Applicable" ...
##  $ NOT_HOUR    : int  99 17 14 99 0 17 19 7 20 10 ...
##  $ NOT_HOURNAME: chr  "Unknown" "5:00pm-5:59pm" "2:00pm-2:59pm" "Unknown" ...
##  $ NOT_MIN     : int  99 18 58 99 45 0 23 21 0 3 ...
##  $ NOT_MINNAME : chr  "Unknown" "18" "58" "Unknown" ...
##  $ ARR_HOUR    : int  3 17 15 99 0 17 19 7 20 10 ...
##  $ ARR_HOURNAME: chr  "3:00am-3:59am" "5:00pm-5:59pm" "3:00pm-3:59pm" "Unknown EMS Scene Arrival Hour" ...
##  $ ARR_MIN     : int  10 26 15 99 55 19 29 28 10 7 ...
##  $ ARR_MINNAME : chr  "10" "26" "15" "Unknown EMS Scene Arrival Minutes" ...
##  $ HOSP_HR     : int  99 99 99 99 88 18 88 88 99 10 ...
##  $ HOSP_HRNAME : chr  "Unknown" "Unknown" "Unknown" "Unknown" ...
##  $ HOSP_MN     : int  99 99 99 99 88 51 88 88 99 29 ...
##  $ HOSP_MNNAME : chr  "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" ...
##  $ FATALS      : int  3 1 1 1 1 1 1 1 1 1 ...
##  $ DRUNK_DR    : int  1 0 0 0 0 0 0 0 0 0 ...

Vemos que tenemos 81 variables y 35766 registros

Revisamos la descripción de las variables contenidas en el fichero y si los tipos de variables se corresponden con las que hemos cargado. Las organizamos lógicamente para darles sentido y construimos un pequeño diccionario de datos utilizando la documentación auxiliar.

  • ST_CASE identificador de accidente

HECHOS A ESTUDIAR

  • FATAL muertes
  • DRUNK_DR conductores bebidos
  • VE_TOTAL número de vehículos implicados en total
  • VE_FORMS número de vehículos en movimiento implicados
  • PVH_INVL número de vehículos estacionados implicados
  • PEDS número de peatones implicados
  • PERSONS número de ocupantes de vehículo implicados
  • PERMVIT número conductores y ocupantes implicados
  • PERNOTMVIT número peatones, ciclistas, a caballo… Cualquier cosa menos vehículo motorizado

DIMENSIÓN GEOGRÁFICA

  • STATE codificación de estado
  • STATENAME nombre de estado
  • COUNTY identificador de contado
  • COUNTYNAME condado
  • CITY identificador de ciudad
  • CITYNAME ciudad
  • NHS 1 ha pasado a autopista del NHS 0 no
  • NHSNAME TBD
  • ROUTE identificador de ruta
  • ROUTENAME ruta
  • TWAY_ID vía de tránsito (1982)
  • TWAY_ID2 vía de tránsito (2004)
  • RUR_URB identificador de segmento rural o urbano
  • RUR_URBNAME segmento rural o urbano
  • FUNC_SYS clasificación funcional segmento
  • FUNC_SYSNAME TBD
  • RD_OWNER identificador propietario del segmento
  • RD_OWNERNAME propietario del segmento
  • MILEPT milla int
  • MILEPTNAME milla chr
  • LATITUDE latitud int
  • LATITUDENAME latitud chr
  • LONGITUD longitud int
  • LONGITUDNAME longitud chr
  • SP_JUR código jurisdicción
  • SP_JURNAME jurisdicción

DIMENSIÓN TEMPORAL

  • DAY día
  • DAYNAME día repetido
  • MONTH mes
  • MONTHNAME nombre de mes
  • YEAR año
  • DAY_WEEK día de la semana
  • DAY_WEEKNAME nombre de día de la semana
  • HOUR hora
  • HOURNAME franja hora
  • MINUTE minuto int
  • MINUTENAME minuto chr

DIMENSIÓN CONDICICIONES ACCIDENTE

  • HARM_EV código primer acontecimiento del accidente que produzca daños o lesiones
  • HARM_EVNAME primer acontecimiento del accidente que produzca daños o lesiones
  • MAN_COLL código de posición de los vehículos
  • MAN_COLLNAME posición de los vehículos
  • RELJCT1 código si hay área de intercambio
  • RELJCT1NAME si hay área de intercambio
  • RELJCT2 código proximidad cruce
  • RELJCT2NAME proximidad cruce
  • TYP_INT código tipo de intersección
  • TYP_INTNAME tipo de intersección
  • WRK_ZONE código tipología de obras
  • WRK_ZONENAME tipología de obras
  • RAIL_ROAD código ubicación vehículo a la vía
  • RAIL_ROADNAME ubicación vehículo a la vía
  • LGT_COND código condición lumínica
  • LGT_CONDNAME condición lumínica

DIMENSIÓN METEOROLOGIA

  • WEATHER código tiempo
  • WEATHERNAME tiempo

OTROS

  • SCH_BUSS código si vehículo escolar implicado
  • SCH_BUSNAME vehículo escolar implicado
  • RAIL código si dentro o cerca paso ferroviario
  • RAILNAME si dentro o cerca paso ferroviario

DIMENSIÓN SERVICIO EMERGENCIAS

  • NOT_HOUR hora notificación a emergencias int
  • NOT_HOURNAME hora notificación a emergencias franja
  • NOT_MIN minuto notificación a emergencias int
  • NOT_MINNAME minuto notificación a emergencias chr
  • ARR_HOUR hora llegada emergencias int
  • ARR_HOURNAME hora llegada emergencias franja
  • ARR_MIN minuto llegada emergencias int
  • ARR_MINNAME minuto llegada emergencias franja
  • HOSP_HR hora llegada hospital int
  • HOSP_HRNAME hora llegada hospital franja
  • HOSP_MN minuto llegada hospital int
  • HOSP_MNNAME minuto llegada hospital franja

DIMENSIÓN FACTORES RELACIONADOS ACCIDENTE

  • CF1 código factores relacionados con el accidente 1
  • CF1NAME factores relacionados con el accidente 1
  • CF2 código factores relacionados con el accidente 2
  • CF2NAME factores relacionados con el accidente 2
  • CF3 código factores relacionados con el accidente 3

1.3 Preprocesamiento y gestión de características

1.3.1 Limpieza

El siguiente paso será la limpieza de datos, mirando si hay valores vacíos o nulos.

print('NA')
## [1] "NA"
colSums(is.na(accidentData))
##        STATE    STATENAME      ST_CASE     VE_TOTAL     VE_FORMS     PVH_INVL 
##            0            0            0            0            0            0 
##         PEDS      PERSONS      PERMVIT   PERNOTMVIT       COUNTY   COUNTYNAME 
##            0            0            0            0            0            0 
##         CITY     CITYNAME          DAY      DAYNAME        MONTH    MONTHNAME 
##            0            0            0            0            0            0 
##         YEAR     DAY_WEEK DAY_WEEKNAME         HOUR     HOURNAME       MINUTE 
##            0            0            0            0            0            0 
##   MINUTENAME          NHS      NHSNAME        ROUTE    ROUTENAME      TWAY_ID 
##            0            0            0            0            0            0 
##     TWAY_ID2      RUR_URB  RUR_URBNAME     FUNC_SYS FUNC_SYSNAME     RD_OWNER 
##            0            0            0            0            0            0 
## RD_OWNERNAME       MILEPT   MILEPTNAME     LATITUDE LATITUDENAME     LONGITUD 
##            0            0            0            0            0            0 
## LONGITUDNAME       SP_JUR   SP_JURNAME      HARM_EV  HARM_EVNAME     MAN_COLL 
##            0            0            0            0            0            0 
## MAN_COLLNAME      RELJCT1  RELJCT1NAME      RELJCT2  RELJCT2NAME      TYP_INT 
##            0            0            0            0            0            0 
##  TYP_INTNAME     WRK_ZONE WRK_ZONENAME     REL_ROAD REL_ROADNAME     LGT_COND 
##            0            0            0            0            0            0 
## LGT_CONDNAME      WEATHER  WEATHERNAME      SCH_BUS  SCH_BUSNAME         RAIL 
##            0            0            0            0            0            0 
##     RAILNAME     NOT_HOUR NOT_HOURNAME      NOT_MIN  NOT_MINNAME     ARR_HOUR 
##            0            0            0            0            0            0 
## ARR_HOURNAME      ARR_MIN  ARR_MINNAME      HOSP_HR  HOSP_HRNAME      HOSP_MN 
##            0            0            0            0            0            0 
##  HOSP_MNNAME       FATALS     DRUNK_DR 
##            0            0            0
print('Blancos')
## [1] "Blancos"
colSums(accidentData=="")
##        STATE    STATENAME      ST_CASE     VE_TOTAL     VE_FORMS     PVH_INVL 
##            0            0            0            0            0            0 
##         PEDS      PERSONS      PERMVIT   PERNOTMVIT       COUNTY   COUNTYNAME 
##            0            0            0            0            0            0 
##         CITY     CITYNAME          DAY      DAYNAME        MONTH    MONTHNAME 
##            0            0            0            0            0            0 
##         YEAR     DAY_WEEK DAY_WEEKNAME         HOUR     HOURNAME       MINUTE 
##            0            0            0            0            0            0 
##   MINUTENAME          NHS      NHSNAME        ROUTE    ROUTENAME      TWAY_ID 
##            0            0            0            0            0            0 
##     TWAY_ID2      RUR_URB  RUR_URBNAME     FUNC_SYS FUNC_SYSNAME     RD_OWNER 
##        26997            0            0            0            0            0 
## RD_OWNERNAME       MILEPT   MILEPTNAME     LATITUDE LATITUDENAME     LONGITUD 
##            0            0            0            0            0            0 
## LONGITUDNAME       SP_JUR   SP_JURNAME      HARM_EV  HARM_EVNAME     MAN_COLL 
##            0            0            0            0            0            0 
## MAN_COLLNAME      RELJCT1  RELJCT1NAME      RELJCT2  RELJCT2NAME      TYP_INT 
##            0            0            0            0            0            0 
##  TYP_INTNAME     WRK_ZONE WRK_ZONENAME     REL_ROAD REL_ROADNAME     LGT_COND 
##            0            0            0            0            0            0 
## LGT_CONDNAME      WEATHER  WEATHERNAME      SCH_BUS  SCH_BUSNAME         RAIL 
##            0            0            0            0            0            0 
##     RAILNAME     NOT_HOUR NOT_HOURNAME      NOT_MIN  NOT_MINNAME     ARR_HOUR 
##            0            0            0            0            0            0 
## ARR_HOURNAME      ARR_MIN  ARR_MINNAME      HOSP_HR  HOSP_HRNAME      HOSP_MN 
##            0            0            0            0            0            0 
##  HOSP_MNNAME       FATALS     DRUNK_DR 
##            0            0            0

Vemos que no hay valores nulos en los datos. También verificamos si existen campos llenos de espacios en blanco. En este caso sí encontramos el campo TWAY_ID2 con 26997 valores en blanco. Valoramos no hacer ninguna acción de eliminar registros puesto que este campo no lo utilizaremos.

Vamos a crear histogramas y describir los valores para ver los datos en general de estos atributos para hacer una primera aproximación a los datos:

if (!require('ggplot2')) install.packages('ggplot2'); library('ggplot2')
if(!require('Rmisc')) install.packages('Rmisc'); library('Rmisc')
if(!require('dplyr')) install.packages('dplyr'); library('dplyr')
if(!require('xfun')) install.packages('xfun'); library('xfun')

summary(accidentData[c("FATALS","DRUNK_DR")])
##      FATALS         DRUNK_DR     
##  Min.   :1.000   Min.   :0.0000  
##  1st Qu.:1.000   1st Qu.:0.0000  
##  Median :1.000   Median :0.0000  
##  Mean   :1.085   Mean   :0.2664  
##  3rd Qu.:1.000   3rd Qu.:1.0000  
##  Max.   :8.000   Max.   :4.0000
histList<- list()

n = c("FATALS","DRUNK_DR")
accidentDataAux= accidentData %>% select(all_of(n))
for(y in 1:ncol(accidentDataAux)){
col <- names(accidentDataAux)[y]
ggp <- ggplot(accidentDataAux, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "cornflowerblue", color = "black",ggtittle = "Contador de ocurrencias por variable")
histList[[y]] <- ggp # añadimos cada plot a la lista vacía
}
multiplot(plotlist = histList, coles = 1)

## [1] 1

Observaciones:

Número de muertes: Todos los accidentes recogidos en estos datos reportan una muerte como mínimo. Siendo el accidente más grave con ocho víctimas y vemos que la distribución se acumula de forma muy evidente en una muerte por accidente.

Conductores bebidos involucrados en el accidente: Analizaremos con más detalle este dato más adelante para derivar un nuevo dato: Accidentes donde el alcohol está presente o no. De entrada, la media es de 0.26% de accidentes donde interviene un conductor bebido. La franja de conductores bebidos por accidente va de un conductor como mínimo a cuatro como máximo.

summary(accidentData[c("VE_TOTAL","VE_FORMS","PVH_INVL")])
##     VE_TOTAL        VE_FORMS         PVH_INVL       
##  Min.   : 1.00   Min.   : 1.000   Min.   : 0.00000  
##  1st Qu.: 1.00   1st Qu.: 1.000   1st Qu.: 0.00000  
##  Median : 1.00   Median : 1.000   Median : 0.00000  
##  Mean   : 1.56   Mean   : 1.517   Mean   : 0.04269  
##  3rd Qu.: 2.00   3rd Qu.: 2.000   3rd Qu.: 0.00000  
##  Max.   :15.00   Max.   :15.000   Max.   :10.00000
#Crearemos una lista para mostrar los atributos que interesan.
histList<- list()
n = c("VE_TOTAL","VE_FORMS","PVH_INVL")
accidentDataAux= accidentData %>% select(all_of(n))
for(y in 1:ncol(accidentDataAux)){
col <- names(accidentDataAux)[y]
ggp <- ggplot(accidentDataAux, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "cornflowerblue", color = "black")
histList[[y]] <- ggp # añadimos cada plot a la lista vacía
}
multiplot(plotlist = histList, coles = 1)

## [1] 1

Observaciones en cuanto a los vehículos implicados.

Número de vehículos implicados (VE_TOTAL) en total está en la franja de 1 hasta 59 siendo este el valor máximo y una media de 1.5. Número de vehículos en movimiento implicados (VE_FORMS), el valor más habitual es 1 con un valor máximo también de 59. Prevemos que hay un valor extremo que habrá que tratar para poder sacar más información de esta variable. Número de vehículos estacionados implicados (PVH_INVL): Por lo que respecta a esta variable lo habitual es que no haya vehículos estacionados en los incidentes recogidos en estos datos. Con todo aparecen casos aislados donde incluso había 10 coches estacionados.

summary(accidentData[c("PEDS","PERSONS","PERMVIT","PERNOTMVIT")])
##       PEDS           PERSONS          PERMVIT         PERNOTMVIT    
##  Min.   :0.0000   Min.   : 0.000   Min.   : 0.000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.: 1.000   1st Qu.: 1.000   1st Qu.:0.0000  
##  Median :0.0000   Median : 2.000   Median : 2.000   Median :0.0000  
##  Mean   :0.2285   Mean   : 2.173   Mean   : 2.163   Mean   :0.2387  
##  3rd Qu.:0.0000   3rd Qu.: 3.000   3rd Qu.: 3.000   3rd Qu.:0.0000  
##  Max.   :8.0000   Max.   :61.000   Max.   :61.000   Max.   :9.0000
#Crearemos una lista para mostrar los atributos que interesan.
histList<- list()
n = c("PEDS","PERSONS","PERMVIT","PERNOTMVIT")
accidentDataAux= accidentData %>% select(all_of(n))
for(y in 1:ncol(accidentDataAux)){
col <- names(accidentDataAux)[y]
ggp <- ggplot(accidentDataAux, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "cornflowerblue", color = "black")
histList[[y]] <- ggp # añadimos cada plot a la lista vacía
}
multiplot(plotlist = histList, coles = 1)

## [1] 1

Observaciones en cuanto a las personas implicadas en un accidente.

El número de peatones implicados (PEDS) es muy bajo siendo coherente con el tipo de vía que se estudia y dónde no es habitual que haya gente andando. Con todo el valor como media de 0.22 y máximo de 8 obliga a investigar más este dato. (PERSONS) El número de ocupantes de vehículo implicados se sitúa como media en 2.1 (PERMVIT) El número conductores y ocupantes de vehículos en circulación implicados tiene un valor de media de 2.1. Estas dos variables recogen la misma información, pero la cuantifican de diferente manera. El accidente con el mayor número de ocupantes es de 61 personas. Por lo que respecta al número peatones, ciclistas, personas en vehículos aparcados y otros (PERNOTMVIT) vemos que aumenta un poco la media respecto a peatón puesto que entendemos que se incluyen más casos.

Vamos a profundizar un poco en el tema de la relación del alcohol en los conductores y el número de accidentes.

accidentData$alcohol <- ifelse(accidentData$DRUNK_DR %in% c(0), 0, 1)
counts <- table(accidentData$alcohol)
barplot(prop.table(counts),
        col=c("green","red"), 
        main="Accidentes con conductor bebido", 
        xlab ="Presencia Alcohol", 
        ylab = "Porcentaje",
        ylim=c(0,0.8),
        names.arg = c("No Alcohol", "Sí Alcohol"))

legend("topright", legend = c("No Alcohol", "Sí Alcohol"), fill = c("green", "red"))

Vemos que porcentualmente, en la gran mayoría de accidentes, alrededor del 75% no hay presencia de alcohol en el conductor. Los conductores que dan positivo están alrededor de un 22%. Hemos buscado contrastar el dato con otros países y estarían en un valor central donde los valores extremos máximo por país superan el 50% y los mínimos están sobre el 10%

Observamos ahora como se distribuyen las muertes por accidente.

df1 <- accidentData %>%
group_by(accidentData$FATALS) %>%
dplyr::summarise(counts = n())
df1
## # A tibble: 8 × 2
##   `accidentData$FATALS` counts
##                   <int>  <int>
## 1                     1  33226
## 2                     2   2154
## 3                     3    289
## 4                     4     71
## 5                     5     20
## 6                     6      4
## 7                     7      1
## 8                     8      1
counts <- table(accidentData$FATALS)
barplot(prop.table(counts),
        col=("red"),ylim=c(0,0.99),
        main="Distribución de la mortalidad a los accidentes",
        xlab ="Número de muertos", 
        ylab = "Porcentaje")

Vemos que la mayoría de los accidentes tienen como mínimo un muerto. Vamos ahora a relacionar mortalidad y alcohol.

counts <- table(accidentData$alcohol, accidentData$FATALS)
colors <- c("green", "red")
barplot(prop.table(counts), 
        beside = TRUE, 
        col = colors,
        ylim = c(0, 1), 
        axes = TRUE,
        xlab = "Número de muertos",
        ylab = "Porcentaje",
        main = "Accidentes por muertes y conductores positivos en alcohol")

legend("topright", legend = c("No Alcohol", "Alcohol"), col = colors, pch = 15)

Probaremos ahora si hay relación entre el estado donde ha pasado el accidente y el número de conductores bebidos. Filtramos los cinco estados donde hay más accidentes.

accidentDataST5=subset(accidentData, accidentData$STATENAME == "California" | accidentData$STATENAME == "Texas" | accidentData$STATENAME == "Florida" | accidentData$STATENAME == "Georgia" | accidentData$STATENAME == "North Carolina")

files=dim(accidentDataST5)[1]
ggplot(data=accidentDataST5[1:files,],aes(x=DRUNK_DR,fill=STATENAME))+geom_bar()+ggtitle("Relación entre conductor bebido y Estado")+labs(x="Número de conductores bebidos implicados")

Como reflexión este gráfico tiene que pasar por el filtro de percentuar el número de accidentes por estado y la población del estado para no sacar conclusiones apresuradas.

Veamos ahora como en un mismo gráfico de frecuencias podemos trabajar con 3 variables: FATALS, STATENAME y WEATHERNAME.

ggplot(data = accidentDataST5[1:files,],aes(x=FATALS,fill=STATENAME))+geom_bar(position="fill")+facet_wrap(~WEATHERNAME)+ggtitle("Número de muertes en accidente por Estado y clima")+labs(x="Número de muertes")

Esta gráfica está bien como mecánica de construcción, pero los resultados los ponemos en entredicho. Está bien como paso inicial, pero hay que profundizar mucho más.

Vamos a buscar las correlaciones en función de las muertes y unas variables elegidas que creemos que pueden ayudar a explicar el aumento de muertes por accidente:

DRUNK_DR conductores bebidos VE_TOTAL número de vehículos implicados en total VE_FORMS número de vehículos en movimiento implicados PVH_INVL número de vehículos estacionados implicados PEDS número de peatón implicados PERSONS número de ocupante de vehículo implicados PERMVIT número conductores y ocupantes implicados PERNOTMVIT número peatones, ciclistas… Cualquier cosa menos vehículo motorizado

# Utilizamos esta librería para usar la funcio multiplot()
if(!require('Rmisc')) install.packages('Rmisc'); library('Rmisc')

n = c("DRUNK_DR","VE_TOTAL","VE_FORMS","PVH_INVL","PEDS","PERSONS","PERMVIT","PERNOTMVIT") 
accidentDataAux= accidentData %>% select(all_of(n))
histList2<- vector('list', ncol(accidentDataAux))
for(i in seq_along(accidentDataAux)){
  message(i)
histList2[[i]]<-local({
  i<-i
  col <-log(accidentDataAux[[i]])
  ggp<- ggplot(data = accidentDataAux, aes(x = accidentData$FATALS, y=col)) + 
    geom_point(color = "gray30") + geom_smooth(method = lm,color = "firebrick") + 
    theme_bw() + xlab("Muertes") + ylab(names(accidentDataAux)[i])
  })

}
multiplot(plotlist = histList2, cols = 3)

Podemos ver que:

  • De forma general cualquier aumento en las variables elegidas implica un aumento de las muertes en el accidente.

  • El factor que hace aumentar más el número de víctimas son las variables relacionadas con los peatones y pasajeros de los coches involucrados en el accidente.

Utilizamos las columnas que nos interesa para hacer la matriz y la visualizaremos utilizando la función corrplot.

# https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html
if(!require("corrplot")) install.packages("corrplot"); library("corrplot")

n = c("FATALS","DRUNK_DR","VE_TOTAL","VE_FORMS","PVH_INVL","PEDS","PERSONS","PERMVIT","PERNOTMVIT")
factores= accidentData %>% select(all_of(n))
res<-cor(factores)
corrplot(res,method="color",tl.col="black", tl.srt=30, order = "AOE",
number.cex=0.75,sig.level = 0.01, addCoef.col = "black")

No vemos que haya una correlación negativa significativa entre dos variables y sí una muy buena correlación ya previsible entre los peatones implicados y personas involucradas en el accidente que no van en coche (PEDS y PERNOTMVIT) Lo mismo podemos observar en cuanto al número de conductores y ocupantes implicados (PERMVIT) y el número de vehículos implicados en movimiento (VE_FORMS) o el total de vehículos (VE_TOTAL).

Vamos a probar si hay una correlación entre personas implicadas en el accidente y el número de muertes.

if (!require("tidyverse")) {
  install.packages("tidyverse")
  library(tidyverse)
} else {
  library(tidyverse)
}
## Cargando paquete requerido: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.4     ✔ tidyr     1.3.1
## ✔ readr     2.1.5     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::arrange()    masks plyr::arrange()
## ✖ purrr::compact()    masks plyr::compact()
## ✖ dplyr::count()      masks plyr::count()
## ✖ dplyr::desc()       masks plyr::desc()
## ✖ dplyr::failwith()   masks plyr::failwith()
## ✖ dplyr::filter()     masks stats::filter()
## ✖ dplyr::id()         masks plyr::id()
## ✖ dplyr::lag()        masks stats::lag()
## ✖ dplyr::mutate()     masks plyr::mutate()
## ✖ dplyr::rename()     masks plyr::rename()
## ✖ stringr::str_wrap() masks xfun::str_wrap()
## ✖ dplyr::summarise()  masks plyr::summarise()
## ✖ dplyr::summarize()  masks plyr::summarize()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
cor.test(x = accidentData$PERSONS, y = accidentData$FATALS, method = "kendall")
## 
##  Kendall's rank correlation tau
## 
## data:  accidentData$PERSONS and accidentData$FATALS
## z = 53.008, p-value < 2.2e-16
## alternative hypothesis: true tau is not equal to 0
## sample estimates:
##       tau 
## 0.2558174
ggplot(data = accidentData, aes(x = PERSONS, y = log(FATALS))) + 
  geom_point(color = "gray30") + 
  geom_smooth(color = "firebrick") + 
  theme_bw() +
  ggtitle("Correlación entre personas implicadas en el accidente y número de muertes")
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

De la observación de este gráfico podemos concluir que efectivamente el número de muertes aumenta en función de las personas implicadas en un accidente pero que la correlación no es tan elevada ni continúa como se podía prever.

1.4 Construcción de conjunto de datos final

Si dos variables están altamente correlacionadas obviamente darán casi exactamente la misma información en un modelo de regresión, por ejemplo. Pero, al incluir las dos variables, en realidad estamos debilitando el modelo. No estamos añadiendo información incremental. En lugar de esto, estamos haciendo un modelo ruidoso. No es una buena idea.

Cómo hemos visto antes tenemos una correlación muy grande entre PEDS y PERNOTMVIT, por lo tanto, podríamos eliminar la columna de peatones (PEDS) y dejar el total de peatones y otros reflejado a PERNOTMVIT.

# accidentData$PEDS<- NULL
str(accidentData)
## 'data.frame':    35766 obs. of  82 variables:
##  $ STATE       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ STATENAME   : chr  "Alabama" "Alabama" "Alabama" "Alabama" ...
##  $ ST_CASE     : int  10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 ...
##  $ VE_TOTAL    : int  1 4 2 1 1 2 1 2 2 2 ...
##  $ VE_FORMS    : int  1 4 2 1 1 2 1 2 2 2 ...
##  $ PVH_INVL    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ PEDS        : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ PERSONS     : int  4 6 2 5 1 3 1 2 4 3 ...
##  $ PERMVIT     : int  4 6 2 5 1 3 1 2 4 3 ...
##  $ PERNOTMVIT  : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ COUNTY      : int  51 73 117 15 37 103 73 25 45 95 ...
##  $ COUNTYNAME  : chr  "ELMORE (51)" "JEFFERSON (73)" "SHELBY (117)" "CALHOUN (15)" ...
##  $ CITY        : int  0 350 0 0 0 0 330 0 0 1500 ...
##  $ CITYNAME    : chr  "NOT APPLICABLE" "BIRMINGHAM" "NOT APPLICABLE" "NOT APPLICABLE" ...
##  $ DAY         : int  1 2 2 3 4 4 7 8 9 10 ...
##  $ DAYNAME     : int  1 2 2 3 4 4 7 8 9 10 ...
##  $ MONTH       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ MONTHNAME   : chr  "January" "January" "January" "January" ...
##  $ YEAR        : int  2020 2020 2020 2020 2020 2020 2020 2020 2020 2020 ...
##  $ DAY_WEEK    : int  4 5 5 6 7 7 3 4 5 6 ...
##  $ DAY_WEEKNAME: chr  "Wednesday" "Thursday" "Thursday" "Friday" ...
##  $ HOUR        : int  2 17 14 15 0 16 19 7 20 10 ...
##  $ HOURNAME    : chr  "2:00am-2:59am" "5:00pm-5:59pm" "2:00pm-2:59pm" "3:00pm-3:59pm" ...
##  $ MINUTE      : int  58 18 55 20 45 55 23 15 0 2 ...
##  $ MINUTENAME  : chr  "58" "18" "55" "20" ...
##  $ NHS         : int  0 0 0 0 0 0 0 0 0 1 ...
##  $ NHSNAME     : chr  "This section IS NOT on the NHS" "This section IS NOT on the NHS" "This section IS NOT on the NHS" "This section IS NOT on the NHS" ...
##  $ ROUTE       : int  4 6 3 4 4 3 4 4 4 2 ...
##  $ ROUTENAME   : chr  "County Road" "Local Street - Municipality" "State Highway" "County Road" ...
##  $ TWAY_ID     : chr  "cr-4" "martin luther king jr dr" "sr-76" "CR-ALEXANDRIA WELLINGTON RD" ...
##  $ TWAY_ID2    : chr  "" "" "us-280" "" ...
##  $ RUR_URB     : int  1 2 1 1 1 1 2 1 1 1 ...
##  $ RUR_URBNAME : chr  "Rural" "Urban" "Rural" "Rural" ...
##  $ FUNC_SYS    : int  5 4 4 7 5 4 4 5 5 3 ...
##  $ FUNC_SYSNAME: chr  "Major Collector" "Minor Arterial" "Minor Arterial" "Local" ...
##  $ RD_OWNER    : int  2 4 1 2 2 1 4 2 2 1 ...
##  $ RD_OWNERNAME: chr  "County Highway Agency" "City or Municipal Highway Agency" "State Highway Agency" "County Highway Agency" ...
##  $ MILEPT      : int  0 0 49 0 0 390 0 0 0 3019 ...
##  $ MILEPTNAME  : chr  "None" "None" "49" "None" ...
##  $ LATITUDE    : num  32.4 33.5 33.3 33.8 32.8 ...
##  $ LATITUDENAME: chr  "32.43313333" "33.48465833" "33.29994167" "33.79507222" ...
##  $ LONGITUD    : num  -86.1 -86.8 -86.4 -85.9 -86.1 ...
##  $ LONGITUDNAME: chr  "-86.09485" "-86.83954444" "-86.36964167" "-85.88348611" ...
##  $ SP_JUR      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SP_JURNAME  : chr  "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" ...
##  $ HARM_EV     : int  42 12 34 42 42 12 8 12 12 12 ...
##  $ HARM_EVNAME : chr  "Tree (Standing Only)" "Motor Vehicle In-Transport" "Ditch" "Tree (Standing Only)" ...
##  $ MAN_COLL    : int  0 6 0 0 0 2 0 1 1 2 ...
##  $ MAN_COLLNAME: chr  "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" "Angle" "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" "The First Harmful Event was Not a Collision with a Motor Vehicle in Transport" ...
##  $ RELJCT1     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ RELJCT1NAME : chr  "No" "No" "No" "No" ...
##  $ RELJCT2     : int  1 1 3 1 1 1 3 1 8 1 ...
##  $ RELJCT2NAME : chr  "Non-Junction" "Non-Junction" "Intersection-Related" "Non-Junction" ...
##  $ TYP_INT     : int  1 1 3 1 1 1 2 1 1 1 ...
##  $ TYP_INTNAME : chr  "Not an Intersection" "Not an Intersection" "T-Intersection" "Not an Intersection" ...
##  $ WRK_ZONE    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ WRK_ZONENAME: chr  "None" "None" "None" "None" ...
##  $ REL_ROAD    : int  4 1 4 4 4 1 1 1 1 1 ...
##  $ REL_ROADNAME: chr  "On Roadside" "On Roadway" "On Roadside" "On Roadside" ...
##  $ LGT_COND    : int  2 3 1 1 2 2 3 1 2 1 ...
##  $ LGT_CONDNAME: chr  "Dark - Not Lighted" "Dark - Lighted" "Daylight" "Daylight" ...
##  $ WEATHER     : int  1 2 2 10 2 1 1 1 10 10 ...
##  $ WEATHERNAME : chr  "Clear" "Rain" "Rain" "Cloudy" ...
##  $ SCH_BUS     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SCH_BUSNAME : chr  "No" "No" "No" "No" ...
##  $ RAIL        : chr  "0000000" "0000000" "0000000" "0000000" ...
##  $ RAILNAME    : chr  "Not Applicable" "Not Applicable" "Not Applicable" "Not Applicable" ...
##  $ NOT_HOUR    : int  99 17 14 99 0 17 19 7 20 10 ...
##  $ NOT_HOURNAME: chr  "Unknown" "5:00pm-5:59pm" "2:00pm-2:59pm" "Unknown" ...
##  $ NOT_MIN     : int  99 18 58 99 45 0 23 21 0 3 ...
##  $ NOT_MINNAME : chr  "Unknown" "18" "58" "Unknown" ...
##  $ ARR_HOUR    : int  3 17 15 99 0 17 19 7 20 10 ...
##  $ ARR_HOURNAME: chr  "3:00am-3:59am" "5:00pm-5:59pm" "3:00pm-3:59pm" "Unknown EMS Scene Arrival Hour" ...
##  $ ARR_MIN     : int  10 26 15 99 55 19 29 28 10 7 ...
##  $ ARR_MINNAME : chr  "10" "26" "15" "Unknown EMS Scene Arrival Minutes" ...
##  $ HOSP_HR     : int  99 99 99 99 88 18 88 88 99 10 ...
##  $ HOSP_HRNAME : chr  "Unknown" "Unknown" "Unknown" "Unknown" ...
##  $ HOSP_MN     : int  99 99 99 99 88 51 88 88 99 29 ...
##  $ HOSP_MNNAME : chr  "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" "Unknown EMS Hospital Arrival Time" ...
##  $ FATALS      : int  3 1 1 1 1 1 1 1 1 1 ...
##  $ DRUNK_DR    : int  1 0 0 0 0 0 0 0 0 0 ...
##  $ alcohol     : num  1 0 0 0 0 0 0 0 0 0 ...

1.4.1 Codificación

Seguidamente vayamos a asignar un 1 por accidentes que se producen de madrugada (01h a 06h en invierno) y un 0 para el resto de franja horaria, es decir, vamos a categorizar la variable HOUR y así tendremos una variable numérica que nos permitirá trabajar mejor en el futuro. La denominaremos madrugada. Después la utilizaremos para ver cómo se distribuyen los accidentes en las dos franjas horarias. Debemos tener en cuenta que la hora incluye el código 99 qué quiere decir que la hora no está informada. Miraremos de filtrar los registros con este valor para excluirlos.

accidentDataAux=subset(accidentData, accidentData$HOUR <= 24)

accidentData$madrugada <- NA
accidentData$madrugada[accidentDataAux$HOUR >=1 & accidentDataAux$HOUR <= 6] <- 1
accidentData$madrugada[accidentDataAux$HOUR ==0 | accidentDataAux$HOUR >6 ] <- 0

counts <- table(accidentData$madrugada)
barplot(prop.table(counts),
        col=c("green","red"),
        ylim=c(0,1), 
        main="Distribución de accidentes la madrugada y resto del día",
        xlab="0 Resto del día 1 Madrugada",
        ylab="Porcentaje" )

legend("topright", legend = c("Resto del día", "Madrugada"), fill = c("green", "red"))

1.4.2 Discretización

Ahora añadiremos un campo nuevo a los datos. Este campo contendrá el valor de la hora del accidente discretizada con un método simple de intervalos de igual amplitud.

summary(accidentDataAux[,"HOUR"])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00    7.00   15.00   13.19   19.00   23.00

Discretizamos con intervalos. Los criterios de corte están cogidos de la Web del Parlamento de Cataluña.

accidentDataAux["segmento_horario"] <- cut(accidentDataAux$HOUR, breaks = c(0,4,11,14,18,22), labels = c("Madrugada", "Mañana", "Mediodía", "Anochecer","Noche"))

Observamos los datos discretizados y construimos un gráfico para analizar cómo se agrupan los accidentes.

head(accidentDataAux$segmento_horario)
## [1] Madrugada Anochecer Mediodía  Anochecer <NA>      Anochecer
## Levels: Madrugada Mañana Mediodía Anochecer Noche
plot(accidentDataAux$segmento_horario,main="Número de accidentes por segmento horario",xlab="Segmento horario", ylab="Cantidad",col = "ivory")

Ahora vamos a discretizar la variable que contiene el número de vehículos implicados en un accidente (VE_TOTALS) puesto que era una de las variables en que las distancias entre sus valores eran muy grandes:

# Utilizaremos la función discretize de arules: This function implements several basic unsupervised methods to convert a continuous variable into a categorical variable (factor) using different binning strategies. 
# https://cran.r-project.org/web/packages/arules/index.html
if (!require('arules')) install.packages('arules'); library('arules')
## Cargando paquete requerido: arules
## Cargando paquete requerido: Matrix
## 
## Adjuntando el paquete: 'Matrix'
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## Adjuntando el paquete: 'arules'
## The following object is masked from 'package:dplyr':
## 
##     recode
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
set.seed(2)
table(discretize(accidentData$VE_TOTAL, "cluster" ))
## 
##    [1,1.57) [1.57,3.38)   [3.38,15] 
##       19972       14972         822
hist(accidentData$VE_TOTAL, main="Número de accidentes por vehículos implicados con kmeans",xlab="Vehículos implicados", ylab="Cantidad",col = "ivory")
abline(v=discretize(accidentData$VE_TOTAL, method="cluster", onlycuts=TRUE),col="red")

Podemos observar que sin pasar ningún argumento y permitiendo que el algoritmo elija el conjunto de particiones se muestran tres clústeres que agrupan los vehículos implicados en las franjas mencionadas. Podemos asignar el propio clúster como una variable más al dataset para trabajar después.

accidentData$VE_TOTAL_KM<- (discretize(accidentData$VE_TOTAL, "cluster" ))
head(accidentData$VE_TOTAL_KM)
## [1] [1,1.5)    [2.73,15]  [1.5,2.73) [1,1.5)    [1,1.5)    [1.5,2.73)
## Levels: [1,1.5) [1.5,2.73) [2.73,15]

1.4.3 Normalización

Ahora normalizaremos el número de muertes por el máximo añadiendo un nuevo campo a los datos que contendrá el valor.

accidentData$FATALS_NM<- (accidentData$FATALS/max(accidentData[,"FATALS"]))
head(accidentData$FATALS_NM)
## [1] 0.375 0.125 0.125 0.125 0.125 0.125

Supongamos que queremos normalizar por la diferencia para ubicar entre 0 y 1 la variable del número de muertes del accidente dado que el algoritmo de minería que utilizaremos así lo requiere. Observamos la distribución de la variable original y las generadas.

accidentData$FATALS_ND = (accidentData$FATALS-min(accidentData$FATALS))/(max(accidentData$FATALS)-min(accidentData$FATALS))

max(accidentData$FATALS)
## [1] 8
min(accidentData$FATALS)
## [1] 1
hist(accidentData$FATALS,xlab="Muertos", col="ivory",ylab="Cantidad", main="Número de muertes en accidente")

hist(accidentData$FATALS_NM,xlab="Muertos",ylab="Cantidad",col="ivory", main="Muertos normalizado por el máximo")

hist(accidentData$FATALS_ND,xlab="Muertos",ylab="Cantidad", col="ivory", main="Muertos normalizado por la diferencia")

A continuación, vamos a normalizar las otras columnas para asegurarnos que cada variable contribuye por igual en nuestro análisis.

# Definimos la función de normalización
nor <-function(x) { (x -min(x))/(max(x)-min(x))}
# Guardamos un nuevo dataset normalizado

accidentData$type<- NULL
n = c("FATALS","DRUNK_DR","VE_TOTAL","VE_FORMS","PVH_INVL","PEDS","PERSONS","PERMVIT","PERNOTMVIT")
accidentData<- accidentData %>% select(all_of(n))
accidentData_nor <- as.data.frame(lapply(accidentData, nor))

head(accidentData_nor)
##      FATALS DRUNK_DR   VE_TOTAL   VE_FORMS PVH_INVL PEDS    PERSONS    PERMVIT
## 1 0.2857143     0.25 0.00000000 0.00000000        0    0 0.06557377 0.06557377
## 2 0.0000000     0.00 0.21428571 0.21428571        0    0 0.09836066 0.09836066
## 3 0.0000000     0.00 0.07142857 0.07142857        0    0 0.03278689 0.03278689
## 4 0.0000000     0.00 0.00000000 0.00000000        0    0 0.08196721 0.08196721
## 5 0.0000000     0.00 0.00000000 0.00000000        0    0 0.01639344 0.01639344
## 6 0.0000000     0.00 0.07142857 0.07142857        0    0 0.04918033 0.04918033
##   PERNOTMVIT
## 1          0
## 2          0
## 3          0
## 4          0
## 5          0
## 6          0

1.5 Proceso de PCA

Tanto el análisis de componentes principales, principal componente analysis (PCA) en inglés, como la descomposición de valores singulares, singular value decomposition (SVD) en inglés, son técnicas que nos permitan trabajar con nuevas características llamadas componentes, que ciertamente son independientes entre sí. En realidad, estas dos técnicas nos permiten representar el juego de datos en un nuevo sistema de coordenadas que denominamos componentes principales. Este sistema está mejor adaptado a la distribución del juego de datos, de forma que recoge mejor su variabilidad.

Aplicamos el análisis de componentes principales al dataset. Empezamos ejecutando la función prcomp().

pca.acc <- prcomp(accidentData_nor)
summary(pca.acc)
## Importance of components:
##                           PC1     PC2    PC3    PC4     PC5     PC6     PC7
## Standard deviation     0.1168 0.08755 0.0690 0.0484 0.03269 0.02437 0.01072
## Proportion of Variance 0.4520 0.25389 0.1577 0.0776 0.03539 0.01967 0.00381
## Cumulative Proportion  0.4520 0.70584 0.8635 0.9411 0.97652 0.99619 1.00000
##                              PC8       PC9
## Standard deviation     1.143e-14 2.491e-15
## Proportion of Variance 0.000e+00 0.000e+00
## Cumulative Proportion  1.000e+00 1.000e+00

Como se puede observar la función summary, nos devuelve la proporción de varianza aplicada al conjunto total de cada atributo. Gracias a esto, el atributo 1 explica el 0.452 de variabilidad del total de datos; en cambio, el atributo 7 explica solo el 0.000381.

A continuación, se muestra un histograma para ver el peso de cada atributo sobre el conjunto total de datos:

if (!require('factoextra')) install.packages('factoextra'); library('factoextra')
## Cargando paquete requerido: factoextra
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
#Los valores propios corresponden a la cantidad de variación explicada por cada componente principal (PC).
ev= get_eig(pca.acc)
ev
##         eigenvalue variance.percent cumulative.variance.percent
## Dim.1 1.364366e-02     4.519469e+01                    45.19469
## Dim.2 7.664656e-03     2.538921e+01                    70.58391
## Dim.3 4.760727e-03     1.576993e+01                    86.35384
## Dim.4 2.342530e-03     7.759642e+00                    94.11348
## Dim.5 1.068327e-03     3.538839e+00                    97.65232
## Dim.6 5.937915e-04     1.966938e+00                    99.61926
## Dim.7 1.149407e-04     3.807416e-01                   100.00000
## Dim.8 1.305543e-28     4.324617e-25                   100.00000
## Dim.9 6.203717e-30     2.054984e-26                   100.00000
fviz_eig(pca.acc)

En este ejercicio se decidió utilizar el método de Káiser para decidir cuales de las variables obtenidas serán escogidas. Este criterio mantendrá todas aquellas variables cuya varianza sea superior a 1.

# Calculamos la varianza de los componentes principales a partir de la desviación estándar

var_acc <- pca.acc$sdev^2

var_acc
## [1] 1.364366e-02 7.664656e-03 4.760727e-03 2.342530e-03 1.068327e-03
## [6] 5.937915e-04 1.149407e-04 1.305543e-28 6.203717e-30

Con los resultados obtenidos es muy complicado decidir cuáles son los componentes principales componentes a escoger. Este hecho podría estar causado por no haber escalado los datos previamente. Por lo tanto, el siguiente paso es escalar los datos y volver a calcular la varianza para ver qué datos selecciona.

# Escalamos los datos
acc_scale <- scale(accidentData_nor)
# Calculamos las componentes principales
pca.acc_scale <- prcomp(acc_scale)
# Mostramos la varianza de dichas variables:
var_acc_scale <- pca.acc_scale$sdev^2
head(var_acc_scale)
## [1] 3.5632826 1.8581532 1.1186798 1.0185157 0.8423082 0.5603141

Después de analizar la varianza y aplicando el criterio de Káiser nos quedaremos con los componentes principales 1,2,3 y 4 que son los superiores a 1. Este criterio tiene el problema de sobreestimar el número de factores, pero a pesar de ello es el que aplicaremos para analizar los resultados.

Mostramos el histograma de porcentaje de varianza explicado con los datos escalados:

fviz_eig(pca.acc_scale)

ev = get_eig(pca.acc_scale)
ev
##         eigenvalue variance.percent cumulative.variance.percent
## Dim.1 3.563283e+00     3.959203e+01                    39.59203
## Dim.2 1.858153e+00     2.064615e+01                    60.23817
## Dim.3 1.118680e+00     1.242978e+01                    72.66795
## Dim.4 1.018516e+00     1.131684e+01                    83.98479
## Dim.5 8.423082e-01     9.358980e+00                    93.34377
## Dim.6 5.603141e-01     6.225712e+00                    99.56948
## Dim.7 3.874645e-02     4.305161e-01                   100.00000
## Dim.8 3.781577e-26     4.201752e-25                   100.00000
## Dim.9 2.016132e-26     2.240146e-25                   100.00000

Los valores propios se pueden utilizar para determinar el número de componentes principales a retener después de la PCA (Kaiser 1961):

  • Un valor propio > 1 indica que los PCs representan más varianza de la que representa una de las variables originales de los datos estandarizados. Esto se utiliza habitualmente como punto de corte para el cual se conservan los PCs. Esto solo es cierto cuando los datos están estandarizados.

  • También podemos limitar el número de componentes a este número que representa una determinada fracción de la varianza total. Por ejemplo, si estamos satisfecho con el 80% de la varianza total explicada, usamos el número de componentes para conseguirlo que son los 4 componentes principales vistos antes.

Continuamos con el análisis de los componentes principales. Después de aplicar el método Káiser se han seleccionado los 4 componentes principales.

var <- get_pca_var(pca.acc_scale)
var
## Principal Component Analysis Results for variables
##  ===================================================
##   Name       Description                                    
## 1 "$coord"   "Coordinates for the variables"                
## 2 "$cor"     "Correlations between variables and dimensions"
## 3 "$cos2"    "Cos2 for the variables"                       
## 4 "$contrib" "contributions of the variables"

Los componentes de get_pca_var() se pueden utilizar en el diagrama de variables de la siguiente manera:

  • var$coord: coordenadas de variables para crear un diagrama de dispersión.
  • var$cos2: representa la calidad de representación de las variables al mapa de factores. Se calcula como las coordenadas al cuadrado: var.cos2 = var.coord * var.coord.
  • var$contrib: contiene las contribuciones (en porcentaje) de las variables a los componentes principales. La contribución de una variable (var) a un determinado componente principal es (en porcentaje): (var.cos2 * 100) / (cos2 total del componente).
#Utilizamos los 4 componentes principales encontrados antes
head(var$coord[,1:4],11)
##                  Dim.1       Dim.2       Dim.3       Dim.4
## FATALS     -0.30725642  0.07606945  0.28817514 -0.73967833
## DRUNK_DR   -0.04921565 -0.38170799 -0.26454706 -0.58497489
## VE_TOTAL   -0.83708697  0.28706095 -0.34390477  0.13727724
## VE_FORMS   -0.86483356  0.18416941 -0.02398033  0.21866655
## PVH_INVL   -0.06062570  0.30358100 -0.85844575 -0.18336810
## PEDS        0.48249027  0.82661079  0.13673775 -0.08353380
## PERSONS    -0.88514448  0.21755791  0.19732665 -0.07582156
## PERMVIT    -0.88736748  0.19749105  0.22352113 -0.06716534
## PERNOTMVIT  0.45872440  0.85355861  0.04773257 -0.10804664

1.5.1 Calidad de representación

La calidad de representación de las variables en el mapa de factores se denomina cos2 (coseno cuadrado, coordenadas cuadradas). Podemos acceder al cos2 de la siguiente manera:

head(var$cos2[,1:4],11)
##                  Dim.1       Dim.2       Dim.3       Dim.4
## FATALS     0.094406509 0.005786562 0.083044911 0.547124037
## DRUNK_DR   0.002422180 0.145700988 0.069985146 0.342195618
## VE_TOTAL   0.700714591 0.082403990 0.118270488 0.018845040
## VE_FORMS   0.747937080 0.033918370 0.000575056 0.047815060
## PVH_INVL   0.003675475 0.092161422 0.736929104 0.033623861
## PEDS       0.232796860 0.683285394 0.018697211 0.006977896
## PERSONS    0.783480750 0.047331446 0.038937806 0.005748910
## PERMVIT    0.787421038 0.039002717 0.049961697 0.004511183
## PERNOTMVIT 0.210428078 0.728562294 0.002278398 0.011674076
corrplot(var$cos2[,1:4], is.corr=FALSE)

También es posible crear un diagrama de barras de variables cos2 mediante la función fviz_cos2():

fviz_cos2(pca.acc_scale, choice = "var", axes = 1:2)

  • Un cos2 elevado indica una buena representación de la variable en el componente principal. En este caso, la variable se coloca cerca de la circunferencia del círculo de correlación.

  • Un cos2 bajo indica que la variable no está perfectamente representada por los PC. En este caso, la variable está cerca del centro del círculo.

Para una variable dada, la suma del cos2 de todos los componentes principales es igual a uno.

Si una variable está perfectamente representada por solo dos componentes principales (Dim.1 y Dim.2), la suma del cos2 en estos dos PCs es igual a uno. En este caso las variables se colocarán en el círculo de correlaciones.

Para algunas de las variables, pueden ser necesarios más de 2 componentes para representar perfectamente los datos. En este caso las variables se sitúan dentro del círculo de correlaciones.

En resumen:

  • Los valores de cos2 se utilizan para estimar la calidad de la representación
  • Cuanto más próxima esté una variable al círculo de correlaciones, mejor será su representación en el mapa de factores (y más importante es interpretar estos componentes)
  • Las variables que están próximas en el centro de la trama son menos importantes para los primeros componentes.
fviz_pca_var(pca.acc_scale,
col.var = "cos2",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE
)

1.5.2 Contribución

Las contribuciones de las variables en la contabilización de la variabilidad de un determinado componente principal se expresan en porcentaje.

Las variables que están correlacionadas con PC1 (es decir, Dim.1) y PC2 (es decir, Dim.2) son las más importantes para explicar la variabilidad en el conjunto de datos.

Las variables que no están correlacionadas con ningún PC o con las últimas dimensiones son variables con una contribución baja y se pueden eliminar para simplificar el análisis global.

La contribución de las variables se puede extraer de la siguiente manera:

head(var$contrib[,1:4],11)
##                  Dim.1      Dim.2       Dim.3      Dim.4
## FATALS      2.64942527  0.3114147  7.42347448 53.7177824
## DRUNK_DR    0.06797608  7.8411720  6.25604798 33.5974816
## VE_TOTAL   19.66486180  4.4347254 10.57232696  1.8502454
## VE_FORMS   20.99011424  1.8253807  0.05140488  4.6945826
## PVH_INVL    0.10314857  4.9598398 65.87489045  3.3012610
## PEDS        6.53321358 36.7722855  1.67136397  0.6851044
## PERSONS    21.98761218  2.5472306  3.48069265  0.5644400
## PERMVIT    22.09819245  2.0990044  4.46613018  0.4429174
## PERNOTMVIT  5.90545583 39.2089469  0.20366845  1.1461852

Cuando más grande sea el valor de la contribución, más contribución habrá al componente.

corrplot(var$contrib[,1:4], is.cor=FALSE)

Las variables más importantes (que más contribuyen) se pueden resaltar a la gráfica de correlación de la siguiente manera:

fviz_pca_var(pca.acc_scale, col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07")
)

Las variables correlacionadas positivas apuntan al mismo lado de la trama. Las variables correlacionadas negativas apuntan a lados opuestos del gráfico. Por ejemplo, vemos que las personas involucradas en un accidente (PVH_INVL) y conductor bebido (DRUNK_DR) apuntan direcciones opuestas por tanto no están nada correlacionas, además lo hemos visto antes puesto que tienen un coeficiente de correlación de -0.01.

Se observa que las variables que más aportan a las componentes principales son PEDS y PERNOTMVIT por un lado y VE_TOTAL, VE_FORMS, PERSONS y PERMVIT por otro. Esto es debido al hecho que están correlacionadas. En concreto por el diagrama de correlación de antes de que PEDS está muy bien correlacionada con PERNOTMVIT. De otra banda VE_TOTAL, VE_FORMS, PERSONS y PERMVIT están también bastante correlacionadas. La correlación de FATALS con este grupo de variables no es elevada, pero apunta en la misma dirección.

Podrían ahora rehacer los componentes excluyendo las variables que no aportan información. Una vez rehechas estas nuevas variables sustituyen a las originales que las forman y se podrían utilizar por ejemplo como un indicador de gravedad de accidente puesto que incluye vehículo en movimiento, parado, peatones, conductores y otros implicados en una sola variable.

1.6 Interpretación de los resultados

Los datos estudiados contemplan accidentes de tráfico con víctimas en las redes de autopistas en los EUUU a largo del 2020. Todos los registros tienen un identificador único de accidente y una serie de hechos principales como número de muertos, número de conductores bebidos, vehículos y personas implicadas. Tenemos que añadir otras variables que los caracterizan agrupadas por ubicación geográfica, temporal, condiciones específicas del accidente, meteorológicas, la intervención del servicio de emergencias y otros factores.

Revisados los datos parecen bien informados. Los datos están bastante limpios y bien documentados. No plantean graves problemas de campos con valores nulos o vacíos y tienen bastante potencial para generar nuevos indicadores a partir de los datos.

Podemos afirmar que a lo largo del 2020 en las autopistas de EE. UU. sucedieron 35766 accidentes en los que perdieron la vida 38824 personas. Pretendiamos extraer relaciones entre la presencia de alcohol en los conductores y el número de accidentes, pero las conclusiones no fueron claras. Las relaciones más obvias comprobadas son el incremento de muertes en función del incremento del número de vehículos, pasajeros y peatones implicados.

Habría que profundizar mucho más. Sí que podemos perfilar cómo son los accidentes típicos en cuanto al número de vehículos y personas, conductores o peatones implicados.

El más habitual es un muerto por accidente incrementándose este valor en función de las variables relacionadas. Los conductores bebidos aparecen en uno de cada cuatro accidentes mortales aproximadamente. Los vehículos implicados en los accidentes son típicamente uno pudiéndose incrementar a dos en los casos más típicos. El número de peatones implicados es relativamente bajo dado el tipo de vía que estamos estudiando.

En cuanto al consumo de alcohol, con el grado de profundidad estudiado, no se observa un estado donde las proporcionalidades del número de conductor con presencia de alcohol sean superiores a otros estados.

Estudiando el número de muertes en accidente en relación con el estado donde ha sucedido y la condición climática, vemos necesario profundizar con técnicas por ejemplo de agregación para ver cómo se agrupan y poder obtener un perfil.

Se ha estudiado la franja horaria de la madrugada para ver si acumula un mayor número de accidentes, no siendo así. Parece que el número de accidentes mantiene también proporcionalidad respecto las franjas horarias. Se ha estudiado el número de accidentes por segmento horario con una discretización fijada en intervalo arbitrarios. La mayor presencia de accidentes en horario por la mañana y anochecer (ir y volver al trabajo) hace pensar en que queda pendiente estudiar dado el tipo de vía la distribución horaria de los accidentes lunes a viernes respecto a los fines de semana y festivos para ver si hay horas donde se acumulan más accidentes mortales.

Finalmente, con la técnica de los componentes principales hemos generado una nueva variable que combina otras variables con una correlación inicial que se podría considerar como índice de gravedad del accidente.


2 Ejercicio 1


A partir de un juego de datos que te parezca interesante, propón un proyecto completo de minería de datos. La organización de la respuesta tiene que coincidir en las fases típicas del ciclo de vida de un proyecto de minería de datos.

No hay que realizar las tareas de la fase, este es un ejercicio teórico.

Se espera por lo tanto que se responda de forma argumentada a las siguientes preguntas (Metodología CRISP-DM):

  1. Comprensión del negocio - ¿Qué necesita el negocio?
  2. Comprensión de los datos - ¿Qué datos tenemos/necesitamos? ¿Están limpios?
  3. Preparación de los datos - ¿Cómo organizamos los datos para el modelado?
  4. Modelado - ¿Qué técnicas de modelado debemos aplicar?
  5. Evaluación - ¿Qué modelo cumple mejor con los objetivos del negocio?
  6. Implementación - ¿Cómo acceden los interesados a los resultados?

Para cada fase indica cuál es el objetivo de la fase y el producto que se obtendrá. Utiliza ejemplos de qué y cómo podrían ser las tareas. Si hay alguna característica que hace diferente el ciclo de vida de un proyecto de minería respecto a otros proyectos indícalo.

Extensión mínima: 400 palabras

2.1 Identificación de las causas de los accidentes mortales en EEUU

2.1.1 I. Comprensión del negocio

El objetivo del negocio es reducir la siniestralidad vial mediante el entendimiento profundo de las principales causas que provocan los accidentes de tráfico mortales. Este conocimiento resulta crucial para que autoridades viales, organismos de seguridad pública y compañías aseguradoras puedan diseñar medidas preventivas más eficaces, optimizar campañas de concienciación y focalizar recursos en puntos críticos.

Para ello, se propone desarrollar un modelo de clasificación capaz de identificar la causa probable más probable de cada accidente a partir de datos históricos recogidos en el archivo accident.csv (año 2020). Las causas se agrupan en cuatro grandes categorías: error humano, condiciones climáticas y factores externos (infraestructura, obras, entorno, vehículo).

El éxito del proyecto se medirá por la precisión del modelo predictivo, su interpretabilidad y su utilidad para apoyar la toma de decisiones estratégicas en seguridad vial.

El resultado será un documento detallado que describirá los objetivos del proyecto desde una perspectiva de negocio.


2.1.2 II. Comprensión de los datos

El conjunto de datos contiene 35.766 registros de accidentes mortales en EEUU, con 81 variables categóricas y numéricas que describen aspectos del contexto geográfico, temporal, climático y estructural del siniestro.

Las variables de interés seleccionadas para el modelado de clasificación para identificar la causa del sinientro en Error humano o Factores externos, en un principio son:

  • DRUNK_DR: [0-Ningún conductor ebrio implicado, 1-Un conductor implicado estaba ebrio, 2-Información no disponible / desconocida, 3-No aplica (p. ej. sin conductor implicado, solo peatón), 4-No reportado / no se pudo determinar]
  • HARM_EVNAME: [Tree (Standing Only), Motor Vehicle In-Transport, Ditch, Pedestrian, Pedalcyclist, Immersion or Partial Immersion, Rollover/Overturn, Jackknife (harmful to this vehicle), Guardrail End, Parked Motor Vehicle, Embankment, Culvert, Utility Pole/Light Support, Guardrail Face, Concrete Traffic Barrier, Boulder, Traffic Sign Support, Fence, Curb, Live Animal, Fire Hydrant, Other Object (not fixed), Wall, Fell/Jumped from Vehicle, Mail Box, Other Fixed Object, Bridge Rail (Includes parapet), Motor Vehicle In-Transport Strikes or is Struck by Cargo, Persons or Objects Set-in-Motion from/by Another Motor Vehicle In Transport, Railway Vehicle, Cable Barrier, Ground, Building, Other Traffic Barrier, Unknown Fixed Object, Working Motor Vehicle, Bridge Pier or Support, Pavement Surface Irregularity (Ruts, Potholes, Grates, etc.), Traffic Signal Support, Shrubbery, Other Non-Collision, Reported as Unknown, Non-Motorist on Personal Conveyance, Impact Attenuator/Crash Cushion, Post, Pole or Other Supports, Road Vehicle on Rails, Cargo/Equipment Loss, Shift, or Damage [harmful], Fire/Explosion, Thrown or Falling Object, Unknown Object Not Fixed, Harmful Event, Details Not Reported, Object That Had Fallen From Motor Vehicle In-Transport, Injured In Vehicle (Non-Collision), Bridge Overhead Structure, Snow Bank, Ridden Animal or Animal Drawn Conveyance]
  • RELJCT1: [No, Yes, Reported as Unknown, Not Reported]
  • RELJCT2NAME: [Non-Junction, Intersection-Related, Driveway Access Related, Intersection, Through Roadway, Entrance/Exit Ramp Related, Driveway Access, Railway Grade Crossing, Entrance/Exit Ramp, Other location within Interchange Area, Crossover-Related, Reported as Unknown, Not Reported, Shared-Use Path Crossing, Acceleration/Deceleration Lane]
  • TYP_INTNAME: [Not an Intersection, T-Intersection, Four-Way Intersection, Y-Intersection, Traffic Circle, Reported as Unknown, Not Reported, Five Point, or More, Roundabout, Other Intersection Type, L-Intersection]
  • MAN_COLLNAME: [The First Harmful Event was Not a Collision with a Motor Vehicle in Transport, Angle, Front-to-Front, Front-to-Rear, Sideswipe - Opposite Direction, Reported as Unknown, Rear-to-Side, Not Reported, Other, Sideswipe - Same Direction, Rear-to-Rea]
  • WEATHERNAME: [Clear, Rain, Cloudy, Fog, Smog, Smoke, Reported as Unknown, Freezing Rain or Drizzle, Snow, Not Reported, Blowing Snow, Sleet or Hail, Severe Crosswinds, Blowing Sand, Soil, Dirt, Other]
  • LGT_CONDNAME: [Dark - Not Lighted, Dark - Lighted, Daylight, Dusk, Dawn, Reported as Unknown, Dark - Unknown Lighting, Not Reported, Other]
  • WRK_ZONENAME: [None, Maintenance, Construction, Work Zone, Type Unknown, Utility]
  • RD_OWNERNAME: [County Highway Agency, City or Municipal Highway Agency, State Highway Agency, Town or Township Highway Agency, Trafficway Not in State Inventory, Not Reported, Unknown, Bureau of Indian Affairs, U.S. Forest Service, Other Federal Agency, State Park, Forest, or Reservation Agency, Other Local Agency, National Park Service, State Toll Road, Local Park, Forest or Reservation Agency, Railroad, Bureau of Fish and Wildlife, Other State Agency, Private (other than Railroad), Indian Tribe Nation, Army, Bureau of Land Management, Local Toll Authority, Corps of Engineers]
  • FUNC_SYSNAME: [Major Collector, Minor Arterial, Local, Principal Arterial - Other, Interstate, Minor Collector, Principal Arterial - Other Freeways and Expressways, Trafficway Not in State Inventory, Unknown, Not Reported]
  • REL_ROADNAME: [On Roadside, On Roadway, Outside Trafficway, On Median, On Shoulder, Gore, Continuous Left - Turn Lane, Reported as Unknown, Not Reported, In Parking Lane/Zone, Pedestrian Refuge Island or Traffic Island, Off Roadway-Location Unknown, Separator]
  • REL_ROADNAME: [On Roadside, On Roadway, Outside Trafficway, On Median, On Shoulder, Gore, Continuous Left - Turn Lane, Reported as Unknown, Not Reported, In Parking Lane/Zone, Pedestrian Refuge Island or Traffic Island, Off Roadway-Location Unknown, Separator]
  • RUR_URBNAME: [Rural, Urban, Trafficway Not in State Inventory, Unknown, Not Reported]
  • ROUTENAME: [County Road, Local Street - Municipality, State Highway, U.S. Highway, Interstate, Other, Unknown, Local Street - Frontage Road, Local Street - Township]
  • PERNOTMVIT: [0, 1, 2, 3, 5, 4, 6, 7, 8, 9] Refleja el número total de personas no motorizadas implicadas en el accidente, incluyendo Peatones, Ciclistas, Personas a caballo, Otras personas no asociadas a vehículos motorizados.

Los datos están bien documentados y no contienen valores nulos o vacíos que sean críticos. Aunque la variables TWAY_ID2 tienen valores ausentes, éstas no afectan directamente al nuestro análisis, lo que asegura la calidad de los datos.

El producto final será un informe exploratorio de los datos donde se incluirá descripción de las variables, estadísticas descriptivas y visualizaciones, detección de valores inconsistentes y por último la identificación de posible variables significativas para nuestro objetivo.


2.1.3 III. Preparación de los datos

Esta fase tiene como objetivo preparar un conjunto de datos para ser modelado. Esta fase es crítica en el ciclo de vida del proyecto, ya que implica convertir datos brutos de accidentes en una forma estructurada, limpia y adecuada para alimentar modelos de clasificación.

Tareas principales que se llevarán a cabo: - Selección de variables significativas para el modelo, directamente relacionadas co n la posibles causas de los accidentes mortales de acuerdo con la categorización mencionada. - Limpieza de registros con incosistencias relevantes. - Creación de variables auxiliares: Se construirá las variables ERR_HUMAN y EXT_FACTORS mediante lógica condicional (case_when() en R) a partir de las variables observadas. - Codificación de las variables categóricas como factores para modelos de aprendezaje supervisado. - Normalización de variables numéricas como PERSONS o VE_TOTAL, se evaluará la normalización si se aplican algoritmos sensibles a escala. - División del conjunto de datos de entrenamiento del 80% y de prueba del 20%, asegurando una distribución proporcional por categoría para evitar sesgo en clases minoritarias.

El producto final de esta fase será Un dataset estructurado y preparado con la variable objetivo bien definida, listo para alimentar distintos modelos de clasificación.


2.1.4 IV. Modelado

El objetrivo de la fase de modelado es construir uno o varios modelos de clasificación capaces de predecir la causa probable de un accidente mortal a partir de los datos preparados.

Las principales tareas son:

  1. Selección de técnicas ded modelado. Se aplicarán distintos enfoques para su comparación: - Árboles de decisión (rpart): Fácilmente interpretable que permite generar reglas lógicas comprensibles por decisores. - Random Forest: Mejora la precisión y mitiga problemas de sobreajuste. - Regresión logística multinomial: Interpreta estadísticamente el peso de cada variable predictiva.

  2. Diseño y pruebas de validación: Se aplicarñá la validación cruzada (5-fold) para la evaluar la consistencia del modelo y evitar sobreajuste. También se aplicará matices de confusión para el modelo.

  3. Entrenamiento del modelo: Se ajustarán los modelos seleccionados con el conjunto de entrenamiento.

  4. Se analizarán las variables más relevantes mediante medidas de importancia en Random Forest o coeficientes en regresión logística.

El producto final de esta fase serán los modelos de clasificación entrenados, validos y listos para su evaluación final.


2.1.5 V. Evaluación

En esta fase se ddeterminará si los modelos construidos cumplen con los objetivos de negocio y se escogerá la mejor solución.

El modelo seleccionado se evaluará en base a: 1.Evaluación del modelo según las métricas sigueintes: - Precisión global - Recall por clase - F1-score - Matriz de confusión Estas métricas permitirán identificar si el modelo clasifica correctamente las distintas causas de accidentes mortales. 2. Evaluación por contexto geográfico: El modelo se testeará también en distintos escenarios (urbano, rural, autopista) usando la variable FUNC_SYSNAME o ROUTENAME 3. Evaluación del proceso: Se revisarán las decisiones tomadas en cada fase para verificar si se cumplió con la lógica del problema, los criterios de calidad y los objetivos del cliente. 4. Selección del modelo que mejor explique la información que deseamos obtener.

El producto final de esta fase será la entrega a negocio del modelo seleccionado y justificado según las evaluaciones y evidencias técnicas.


2.1.6 VI. Implementación

Los resultados del proyecto se desplegará a través de los siguientes archivos que contendrán todo el flujo de trabajo, desde la carga, selección de los datos relevantes, preprocesammiento que incluye limpieza y tratammiento de los datos, transformación, mineria de datos y su interpretación: - Archivo .Rmd - Informe generado con RMarkdown en formato HTML del archivo Rmd.

Se planteará un sistema de mantenimiento que permita actualizar el modelo con nuevos datos. Esto permitirá la mejora continua, actualización de los datos y adaptación a posibles cambios en los patrones de accidentes.



3 Ejercicio 2


A partir del juego de datos utilizado en el ejemplo de la PEC, realiza las tareas previas a la generación de un modelo de minería de datos explicadas en los módulos “El proceso de minería de datos” y “Preprocesado de los datos y gestión de características”.

Puedes utilizar de referencia el ejemplo de la PEC, pero procura cambiar el enfoque y analizar los datos en función de las diferentes dimensiones que presentan los datos. Así, no se puede utilizar la combinación de variables utilizada en el ejemplo: “FATALS”,“DRUNK_DR”,“VE_TOTAL”,“VE_FORMS”,“PVH_INVL”,“PEDS”,“PERSONS”,“PERMVIT”,“PERNOTMVIT”. Se debe analizar cualquier otra combinación que puede incluir (o no) algunas de estas variables con otras nuevas.

Opcionalmente y valorable se pueden añadir al estudio datos de otros años para realizar comparaciones temporales (https://www.nhtsa.gov/file-downloads?p=nhtsa/downloads/FARS/) o añadir otros hechos a estudiar relacionados, por ejemplo, el consumo de drogas en los accidentes (https://static.nhtsa.gov/nhtsa/downloads/FARS/2020/National/FARS2020NationalCSV.zip)

# Limpiar el entorno
rm(list = ls())

3.1 Descripción del origen del conjunto de datos


Datos públicos extraídos de un opendata National Highway Traffic Safety Administration donde se recopilan informes enfocados al análisis de mortalidad en las carreteras. Estos datos pertenecen a los años 2018, 2019 y 2020. Nos muesta información sobre la casuística de accidentes mortales ocurridos. El objetivo de nuestro estudio se fundamenta en clasificar el siniestro a dos posibles causas: + Error Humano + Factores Externos (infraestructuras, condiciones climáticas, mala señalización, fallo vehículo) Para más infornación sobre el conjunto de los datos, podéis visitar https://www.nhtsa.gov/crash-data-systems/fatality-analysis-reporting-system

3.1.1 Análisis exploratorio

Primera aproximación al conjunto de datos para identificar variables relevantes, dimensión del dataset, tipología de los datos, la distribución de las varibles, agregar datos de los diferentes archivos csv y extraer información básica del comportamiento de las variables.

Cargamos los datos y agregamos las variables de drugs.csv a cada uno de los archivos accident.csv correspondientes a cada año de estudio 2018, 2029 y 2020.

Verificamos que las observaciones y dimensiones de los diferentes datasets coinciden.

# Cargamos el juego de datos Drugs.csv del año 2018, 2019 y 2020
path2018 = 'Drugs2018.CSV'
path2019 = 'Drugs2019.CSV'
path2020 = 'Drugs2020.CSV'
drugs2018 <- read.csv(path2018, row.names = NULL)
drugs2019 <- read.csv(path2019, row.names = NULL)
drugs2020 <- read.csv(path2020, row.names = NULL)

# Tamaño del dataset drugs2018
dim(drugs2018)
## [1] 99097     9
# Tamaño del dataset drugs2019
dim(drugs2019)
## [1] 98899     9
# Tamaño del dataset drugs2020
dim(drugs2020)
## [1] 107141      9
file2018 = 'accident2018.CSV'
file2019 = 'accident2019.CSV'
file2020 = 'accident2020.CSV'
# Cargamos el juego de datos accident.csv del año 2018, 2019 y 2020
accident2018 <- read.csv(file2018, row.names = NULL)
accident2019 <- read.csv(file2019, row.names = NULL)
accident2020 <- read.csv(file2020, row.names = NULL)

# Tamaño del dataset accidents2018
dim(accident2018)
## [1] 33919    91
# Tamaño del dataset accidents2019
dim(accident2019)
## [1] 33487    91
# Tamaño del dataset accidents2020
dim(accident2020)
## [1] 35766    81
# Unimos ambos datasets drugs.csv y accident.csv por la columna ST_CASE 
merged2018 <- merge(accident2018, drugs2018, by = "ST_CASE", all = FALSE)
merged2019 <- merge(accident2019, drugs2019, by = "ST_CASE", all = FALSE)
merged2020 <- merge(accident2020, drugs2020, by = "ST_CASE", all = FALSE)

# Dimensiones
dim(merged2018)
## [1] 99097    99
dim(merged2019)
## [1] 98899    99
dim(merged2020)
## [1] 106397     89
# Columnas comunes entre los tres datasets
common_cols <- Reduce(intersect, list(
  colnames(merged2018),
  colnames(merged2019),
  colnames(merged2020)
))

# Filtramos los tres datasets para conservar solo las columnas comunes
merged2018 <- merged2018[, common_cols]
merged2019 <- merged2019[, common_cols]
merged2020 <- merged2020[, common_cols]
# Unir los datasets en uno solo
accident3Y <- rbind(merged2018, merged2019, merged2020)

# Eliminamos duplicados
accident3Y <- accident3Y %>%
  select(-STATENAME.y)
accident3Y <- accident3Y %>%
  select(-STATE.y)
# Renombramos la varible
accident3Y <- accident3Y %>%
  rename(STATENAME = STATENAME.x)
accident3Y <- accident3Y %>%
  rename(STATE = STATE.x)

dim(accident3Y)
## [1] 304393     87
# Primeros registros
head(accident3Y)
##   ST_CASE STATE STATENAME VE_TOTAL VE_FORMS PVH_INVL PEDS PERSONS PERMVIT
## 1   10001     1   Alabama        2        1        1    0       1       1
## 2   10002     1   Alabama        1        1        0    0       2       2
## 3   10002     1   Alabama        1        1        0    0       2       2
## 4   10003     1   Alabama        2        2        0    0       2       2
## 5   10003     1   Alabama        2        2        0    0       2       2
## 6   10003     1   Alabama        2        2        0    0       2       2
##   PERNOTMVIT COUNTY      COUNTYNAME CITY       CITYNAME DAY DAYNAME MONTH
## 1          0    121 TALLADEGA (121) 1870        LINCOLN   5       5     1
## 2          0    127    WALKER (127) 1780         JASPER   8       8     1
## 3          0    127    WALKER (127) 1780         JASPER   8       8     1
## 4          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
## 5          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
## 6          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
##   MONTHNAME YEAR DAY_WEEK DAY_WEEKNAME HOUR        HOURNAME MINUTE MINUTENAME
## 1   January 2018        6       Friday    6   6:00am-6:59am      0          0
## 2   January 2018        2       Monday    0   0:00am-0:59am     48         48
## 3   January 2018        2       Monday    0   0:00am-0:59am     48         48
## 4   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
## 5   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
## 6   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
##   NHS                    NHSNAME ROUTE  ROUTENAME TWAY_ID   TWAY_ID2 RUR_URB
## 1   1 This section IS ON the NHS     1 Interstate    I-20                  1
## 2   1 This section IS ON the NHS     1 Interstate    I-22 CALUMET RD       2
## 3   1 This section IS ON the NHS     1 Interstate    I-22 CALUMET RD       2
## 4   1 This section IS ON the NHS     1 Interstate    I-65                  1
## 5   1 This section IS ON the NHS     1 Interstate    I-65                  1
## 6   1 This section IS ON the NHS     1 Interstate    I-65                  1
##   RUR_URBNAME FUNC_SYS FUNC_SYSNAME RD_OWNER         RD_OWNERNAME MILEPT
## 1       Rural        1   Interstate        1 State Highway Agency   1676
## 2       Urban        1   Interstate        1 State Highway Agency   1621
## 3       Urban        1   Interstate        1 State Highway Agency   1621
## 4       Rural        1   Interstate        1 State Highway Agency   2028
## 5       Rural        1   Interstate        1 State Highway Agency   2028
## 6       Rural        1   Interstate        1 State Highway Agency   2028
##   MILEPTNAME LATITUDE LATITUDENAME  LONGITUD LONGITUDNAME SP_JUR
## 1       1676 33.59133  33.59133056 -86.13187 -86.13186944      0
## 2       1621 33.80919  33.80918611 -87.28985 -87.28984722      0
## 3       1621 33.80919  33.80918611 -87.28985 -87.28984722      0
## 4       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
## 5       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
## 6       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
##                SP_JURNAME HARM_EV          HARM_EVNAME MAN_COLL
## 1 No Special Jurisdiction      14 Parked Motor Vehicle        0
## 2 No Special Jurisdiction      38                Fence        0
## 3 No Special Jurisdiction      38                Fence        0
## 4 No Special Jurisdiction      24       Guardrail Face        0
## 5 No Special Jurisdiction      24       Guardrail Face        0
## 6 No Special Jurisdiction      24       Guardrail Face        0
##                                      MAN_COLLNAME RELJCT1 RELJCT1NAME RELJCT2
## 1 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 2 Not a Collision with Motor Vehicle In-Transport       1         Yes      19
## 3 Not a Collision with Motor Vehicle In-Transport       1         Yes      19
## 4 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 5 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 6 Not a Collision with Motor Vehicle In-Transport       0          No       1
##                              RELJCT2NAME TYP_INT         TYP_INTNAME WRK_ZONE
## 1                           Non-Junction       1 Not an Intersection        0
## 2 Other location within Interchange Area       1 Not an Intersection        0
## 3 Other location within Interchange Area       1 Not an Intersection        0
## 4                           Non-Junction       1 Not an Intersection        1
## 5                           Non-Junction       1 Not an Intersection        1
## 6                           Non-Junction       1 Not an Intersection        1
##   WRK_ZONENAME REL_ROAD REL_ROADNAME LGT_COND       LGT_CONDNAME WEATHER
## 1         None        2  On Shoulder        4               Dawn       1
## 2         None        3    On Median        2 Dark - Not Lighted       2
## 3         None        3    On Median        2 Dark - Not Lighted       2
## 4 Construction        4  On Roadside        2 Dark - Not Lighted      10
## 5 Construction        4  On Roadside        2 Dark - Not Lighted      10
## 6 Construction        4  On Roadside        2 Dark - Not Lighted      10
##   WEATHERNAME SCH_BUS SCH_BUSNAME    RAIL       RAILNAME NOT_HOUR  NOT_HOURNAME
## 1       Clear       0          No 0000000 Not Applicable        6 6:00am-6:59am
## 2        Rain       0          No 0000000 Not Applicable        0 0:00am-0:59am
## 3        Rain       0          No 0000000 Not Applicable        0 0:00am-0:59am
## 4      Cloudy       0          No 0000000 Not Applicable       99       Unknown
## 5      Cloudy       0          No 0000000 Not Applicable       99       Unknown
## 6      Cloudy       0          No 0000000 Not Applicable       99       Unknown
##   NOT_MIN NOT_MINNAME ARR_HOUR    ARR_HOURNAME ARR_MIN ARR_MINNAME HOSP_HR
## 1      99     Unknown        6   6:00am-6:59am      15          15      88
## 2      99     Unknown        0   0:00am-0:59am      59          59      88
## 3      99     Unknown        0   0:00am-0:59am      59          59      88
## 4      99     Unknown       23 11:00pm-11:59pm      10          10      99
## 5      99     Unknown       23 11:00pm-11:59pm      10          10      99
## 6      99     Unknown       23 11:00pm-11:59pm      10          10      99
##                        HOSP_HRNAME HOSP_MN                       HOSP_MNNAME
## 1 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 2 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 3 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 4                          Unknown      99 Unknown EMS Hospital Arrival Time
## 5                          Unknown      99 Unknown EMS Hospital Arrival Time
## 6                          Unknown      99 Unknown EMS Hospital Arrival Time
##   FATALS DRUNK_DR VEH_NO PER_NO DRUGSPEC   DRUGSPECNAME DRUGRES
## 1      1        0      1      1        1    Whole Blood       1
## 2      2        0      1      1        0 Test Not Given       0
## 3      2        0      1      2        0 Test Not Given       0
## 4      1        0      1      1        1    Whole Blood     401
## 5      1        0      1      1        1    Whole Blood     417
## 6      1        0      1      1        1    Whole Blood     316
##                       DRUGRESNAME
## 1 Tested, No Drugs Found/Negative
## 2                  Test Not Given
## 3                  Test Not Given
## 4                     AMPHETAMINE
## 5                 METHAMPHETAMINE
## 6                      Clonazepam
# Información de las variables y sus valores una vez agregados
structure3Y <- str(accident3Y)
## 'data.frame':    304393 obs. of  87 variables:
##  $ ST_CASE     : int  10001 10002 10002 10003 10003 10003 10003 10003 10004 10004 ...
##  $ STATE       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ STATENAME   : chr  "Alabama" "Alabama" "Alabama" "Alabama" ...
##  $ VE_TOTAL    : int  2 1 1 2 2 2 2 2 1 1 ...
##  $ VE_FORMS    : int  1 1 1 2 2 2 2 2 1 1 ...
##  $ PVH_INVL    : int  1 0 0 0 0 0 0 0 0 0 ...
##  $ PEDS        : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ PERSONS     : int  1 2 2 2 2 2 2 2 2 2 ...
##  $ PERMVIT     : int  1 2 2 2 2 2 2 2 2 2 ...
##  $ PERNOTMVIT  : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ COUNTY      : int  121 127 127 21 21 21 21 21 3 3 ...
##  $ COUNTYNAME  : chr  "TALLADEGA (121)" "WALKER (127)" "WALKER (127)" "CHILTON (21)" ...
##  $ CITY        : int  1870 1780 1780 0 0 0 0 0 0 0 ...
##  $ CITYNAME    : chr  "LINCOLN" "JASPER" "JASPER" "NOT APPLICABLE" ...
##  $ DAY         : int  5 8 8 8 8 8 8 8 9 9 ...
##  $ DAYNAME     : int  5 8 8 8 8 8 8 8 9 9 ...
##  $ MONTH       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ MONTHNAME   : chr  "January" "January" "January" "January" ...
##  $ YEAR        : int  2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
##  $ DAY_WEEK    : int  6 2 2 2 2 2 2 2 3 3 ...
##  $ DAY_WEEKNAME: chr  "Friday" "Monday" "Monday" "Monday" ...
##  $ HOUR        : int  6 0 0 22 22 22 22 22 13 13 ...
##  $ HOURNAME    : chr  "6:00am-6:59am" "0:00am-0:59am" "0:00am-0:59am" "10:00pm-10:59pm" ...
##  $ MINUTE      : int  0 48 48 50 50 50 50 50 2 2 ...
##  $ MINUTENAME  : chr  "0" "48" "48" "50" ...
##  $ NHS         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ NHSNAME     : chr  "This section IS ON the NHS" "This section IS ON the NHS" "This section IS ON the NHS" "This section IS ON the NHS" ...
##  $ ROUTE       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ ROUTENAME   : chr  "Interstate" "Interstate" "Interstate" "Interstate" ...
##  $ TWAY_ID     : chr  "I-20" "I-22" "I-22" "I-65" ...
##  $ TWAY_ID2    : chr  "" "CALUMET RD" "CALUMET RD" "" ...
##  $ RUR_URB     : int  1 2 2 1 1 1 1 1 1 1 ...
##  $ RUR_URBNAME : chr  "Rural" "Urban" "Urban" "Rural" ...
##  $ FUNC_SYS    : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ FUNC_SYSNAME: chr  "Interstate" "Interstate" "Interstate" "Interstate" ...
##  $ RD_OWNER    : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ RD_OWNERNAME: chr  "State Highway Agency" "State Highway Agency" "State Highway Agency" "State Highway Agency" ...
##  $ MILEPT      : int  1676 1621 1621 2028 2028 2028 2028 2028 460 460 ...
##  $ MILEPTNAME  : chr  "1676" "1621" "1621" "2028" ...
##  $ LATITUDE    : num  33.6 33.8 33.8 32.8 32.8 ...
##  $ LATITUDENAME: chr  "33.59133056" "33.80918611" "33.80918611" "32.76773611" ...
##  $ LONGITUD    : num  -86.1 -87.3 -87.3 -86.6 -86.6 ...
##  $ LONGITUDNAME: chr  "-86.13186944" "-87.28984722" "-87.28984722" "-86.56403611" ...
##  $ SP_JUR      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SP_JURNAME  : chr  "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" "No Special Jurisdiction" ...
##  $ HARM_EV     : int  14 38 38 24 24 24 24 24 24 24 ...
##  $ HARM_EVNAME : chr  "Parked Motor Vehicle" "Fence" "Fence" "Guardrail Face" ...
##  $ MAN_COLL    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ MAN_COLLNAME: chr  "Not a Collision with Motor Vehicle In-Transport" "Not a Collision with Motor Vehicle In-Transport" "Not a Collision with Motor Vehicle In-Transport" "Not a Collision with Motor Vehicle In-Transport" ...
##  $ RELJCT1     : int  0 1 1 0 0 0 0 0 0 0 ...
##  $ RELJCT1NAME : chr  "No" "Yes" "Yes" "No" ...
##  $ RELJCT2     : int  1 19 19 1 1 1 1 1 1 1 ...
##  $ RELJCT2NAME : chr  "Non-Junction" "Other location within Interchange Area" "Other location within Interchange Area" "Non-Junction" ...
##  $ TYP_INT     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ TYP_INTNAME : chr  "Not an Intersection" "Not an Intersection" "Not an Intersection" "Not an Intersection" ...
##  $ WRK_ZONE    : int  0 0 0 1 1 1 1 1 0 0 ...
##  $ WRK_ZONENAME: chr  "None" "None" "None" "Construction" ...
##  $ REL_ROAD    : int  2 3 3 4 4 4 4 4 4 4 ...
##  $ REL_ROADNAME: chr  "On Shoulder" "On Median" "On Median" "On Roadside" ...
##  $ LGT_COND    : int  4 2 2 2 2 2 2 2 1 1 ...
##  $ LGT_CONDNAME: chr  "Dawn" "Dark - Not Lighted" "Dark - Not Lighted" "Dark - Not Lighted" ...
##  $ WEATHER     : int  1 2 2 10 10 10 10 10 10 10 ...
##  $ WEATHERNAME : chr  "Clear" "Rain" "Rain" "Cloudy" ...
##  $ SCH_BUS     : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ SCH_BUSNAME : chr  "No" "No" "No" "No" ...
##  $ RAIL        : chr  "0000000" "0000000" "0000000" "0000000" ...
##  $ RAILNAME    : chr  "Not Applicable" "Not Applicable" "Not Applicable" "Not Applicable" ...
##  $ NOT_HOUR    : int  6 0 0 99 99 99 99 99 13 13 ...
##  $ NOT_HOURNAME: chr  "6:00am-6:59am" "0:00am-0:59am" "0:00am-0:59am" "Unknown" ...
##  $ NOT_MIN     : int  99 99 99 99 99 99 99 99 99 99 ...
##  $ NOT_MINNAME : chr  "Unknown" "Unknown" "Unknown" "Unknown" ...
##  $ ARR_HOUR    : int  6 0 0 23 23 23 23 23 13 13 ...
##  $ ARR_HOURNAME: chr  "6:00am-6:59am" "0:00am-0:59am" "0:00am-0:59am" "11:00pm-11:59pm" ...
##  $ ARR_MIN     : int  15 59 59 10 10 10 10 10 14 14 ...
##  $ ARR_MINNAME : chr  "15" "59" "59" "10" ...
##  $ HOSP_HR     : int  88 88 88 99 99 99 99 99 88 88 ...
##  $ HOSP_HRNAME : chr  "Not Applicable (Not Transported)" "Not Applicable (Not Transported)" "Not Applicable (Not Transported)" "Unknown" ...
##  $ HOSP_MN     : int  88 88 88 99 99 99 99 99 88 88 ...
##  $ HOSP_MNNAME : chr  "Not Applicable (Not Transported)" "Not Applicable (Not Transported)" "Not Applicable (Not Transported)" "Unknown EMS Hospital Arrival Time" ...
##  $ FATALS      : int  1 2 2 1 1 1 1 1 1 1 ...
##  $ DRUNK_DR    : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ VEH_NO      : int  1 1 1 1 1 1 1 2 1 1 ...
##  $ PER_NO      : int  1 1 2 1 1 1 1 1 1 2 ...
##  $ DRUGSPEC    : int  1 0 0 1 1 1 1 0 1 1 ...
##  $ DRUGSPECNAME: chr  "Whole Blood" "Test Not Given" "Test Not Given" "Whole Blood" ...
##  $ DRUGRES     : int  1 0 0 401 417 316 605 0 1 1 ...
##  $ DRUGRESNAME : chr  "Tested, No Drugs Found/Negative" "Test Not Given" "Test Not Given" "AMPHETAMINE" ...

El dataset en el que basaremos nuestro estudio contiene información de 304.393 observaciones correspondientes a accidentes de circulación mortales ocurridos en 2018, 2019 y 2020 en EEUU. Presenta 89 variables que proporcionan información relacionada con la casuística de los siniestros y se ha ampliado información auxiliar referente a la detección de drogas. Consultar en https://www.nhtsa.gov/research-data/fatality-analysis-reporting-system-fars

Observamos la descripción de las variables del fichero y apreciamos que la variable ST_CASE identificadora del siniestro.

Destacamos que existen variables duplicadas en forma numérica or ejemplo: NHS, ROUTE, FUNC_SYS, WEATHER, etc. y la versión descriptiva correspondiente, como NHSNAME, ROUTENAME, FUNC_SYSNAME, WEATHERNAME, etc. lo que nos permite usar tanto los valores descriptivos NAME para su interpretación o visualización, mientras que los codificados CODE pueden usarse para análisis automatizados.

Además, la mayoría de variables son categóricas, es decir describen tipos de condiciones por ejemplo: LGT_CONDNAME, segmentos: RUR_URBNAME, sistemas viales: FUNC_SYSNAME, etc. lo que implica que muchas técnicas de modelado requerirán transformación de variables categóricas a factores o variables dummy.

3.1.2 Preprocesamiento y gestión de características

En esta etapa incluiremos la limpieza de los datos, transformación de variables, normalización y discretización si fueran necesario.

3.1.2.1 Número total de valores NA

# Verificamos la existencia de valors nulos o vacíos por columnas
colSums(is.na(accident3Y))
##      ST_CASE        STATE    STATENAME     VE_TOTAL     VE_FORMS     PVH_INVL 
##            0            0            0            0            0            0 
##         PEDS      PERSONS      PERMVIT   PERNOTMVIT       COUNTY   COUNTYNAME 
##            0            0            0            0            0            0 
##         CITY     CITYNAME          DAY      DAYNAME        MONTH    MONTHNAME 
##            0            0            0            0            0            0 
##         YEAR     DAY_WEEK DAY_WEEKNAME         HOUR     HOURNAME       MINUTE 
##            0            0            0            0            0            0 
##   MINUTENAME          NHS      NHSNAME        ROUTE    ROUTENAME      TWAY_ID 
##            0            0            0            0            0            0 
##     TWAY_ID2      RUR_URB  RUR_URBNAME     FUNC_SYS FUNC_SYSNAME     RD_OWNER 
##            0            0            0            0            0            0 
## RD_OWNERNAME       MILEPT   MILEPTNAME     LATITUDE LATITUDENAME     LONGITUD 
##            0            0            0            0            0            0 
## LONGITUDNAME       SP_JUR   SP_JURNAME      HARM_EV  HARM_EVNAME     MAN_COLL 
##            0            0            0            0            0            0 
## MAN_COLLNAME      RELJCT1  RELJCT1NAME      RELJCT2  RELJCT2NAME      TYP_INT 
##            0            0            0            0            0            0 
##  TYP_INTNAME     WRK_ZONE WRK_ZONENAME     REL_ROAD REL_ROADNAME     LGT_COND 
##            0            0            0            0            0            0 
## LGT_CONDNAME      WEATHER  WEATHERNAME      SCH_BUS  SCH_BUSNAME         RAIL 
##            0            0            0            0            0            0 
##     RAILNAME     NOT_HOUR NOT_HOURNAME      NOT_MIN  NOT_MINNAME     ARR_HOUR 
##            0            0            0            0            0            0 
## ARR_HOURNAME      ARR_MIN  ARR_MINNAME      HOSP_HR  HOSP_HRNAME      HOSP_MN 
##            0            0            0            0            0            0 
##  HOSP_MNNAME       FATALS     DRUNK_DR       VEH_NO       PER_NO     DRUGSPEC 
##            0            0            0            0            0            0 
## DRUGSPECNAME      DRUGRES  DRUGRESNAME 
##            0            0            0

3.1.2.2 Número total de valores vacíos

colSums(accident3Y=="")
##      ST_CASE        STATE    STATENAME     VE_TOTAL     VE_FORMS     PVH_INVL 
##            0            0            0            0            0            0 
##         PEDS      PERSONS      PERMVIT   PERNOTMVIT       COUNTY   COUNTYNAME 
##            0            0            0            0            0            0 
##         CITY     CITYNAME          DAY      DAYNAME        MONTH    MONTHNAME 
##            0            0            0            0            0            0 
##         YEAR     DAY_WEEK DAY_WEEKNAME         HOUR     HOURNAME       MINUTE 
##            0            0            0            0            0            0 
##   MINUTENAME          NHS      NHSNAME        ROUTE    ROUTENAME      TWAY_ID 
##            0            0            0            0            0            0 
##     TWAY_ID2      RUR_URB  RUR_URBNAME     FUNC_SYS FUNC_SYSNAME     RD_OWNER 
##       218583            0            0            0            0            0 
## RD_OWNERNAME       MILEPT   MILEPTNAME     LATITUDE LATITUDENAME     LONGITUD 
##            0            0            0            0            0            0 
## LONGITUDNAME       SP_JUR   SP_JURNAME      HARM_EV  HARM_EVNAME     MAN_COLL 
##            0            0            0            0            0            0 
## MAN_COLLNAME      RELJCT1  RELJCT1NAME      RELJCT2  RELJCT2NAME      TYP_INT 
##            0            0            0            0            0            0 
##  TYP_INTNAME     WRK_ZONE WRK_ZONENAME     REL_ROAD REL_ROADNAME     LGT_COND 
##            0            0            0            0            0            0 
## LGT_CONDNAME      WEATHER  WEATHERNAME      SCH_BUS  SCH_BUSNAME         RAIL 
##            0            0            0            0            0            0 
##     RAILNAME     NOT_HOUR NOT_HOURNAME      NOT_MIN  NOT_MINNAME     ARR_HOUR 
##            0            0            0            0            0            0 
## ARR_HOURNAME      ARR_MIN  ARR_MINNAME      HOSP_HR  HOSP_HRNAME      HOSP_MN 
##            0            0            0            0            0            0 
##  HOSP_MNNAME       FATALS     DRUNK_DR       VEH_NO       PER_NO     DRUGSPEC 
##            0            0            0            0            0            0 
## DRUGSPECNAME      DRUGRES  DRUGRESNAME 
##            0            0            0

3.1.2.3 Análisis Descriptivo Básico

  • Distribución de Accidentes por Año: respondemos a la pregunta cuántos accidentes ocurrieron en cada año y lo visualizamos con un gráfico de barras.
# Distribución de la variable FATALS
library(dplyr)
df_fatal <- accident3Y %>%
  group_by(FATALS) %>%
  dplyr::summarise(counts = n())

df_fatal
## # A tibble: 9 × 2
##   FATALS counts
##    <int>  <int>
## 1      1 269537
## 2      2  27520
## 3      3   5067
## 4      4   1596
## 5      5    390
## 6      6     93
## 7      7     88
## 8      8     79
## 9     20     23
# 
ggplot(accident3Y, aes(x = as.factor(YEAR), fill = as.factor(YEAR))) +
  geom_bar(stat = "count", show.legend = FALSE) + 
  scale_fill_manual(values = c("2018" = "skyblue", "2019" = "lightgreen", "2020" = "orange")) +
  labs(title = "Número de Accidentes por Año", x = "Año", y = "Número de Accidentes") +
  geom_text(stat = "count", aes(label = ..count..), position = position_dodge(width = 1.2), vjust = 1.5, color = "black") +
  theme_minimal() +
  theme(panel.grid = element_blank(),
        axis.text.x = element_text(angle = 45, hjust = 1))

3.1.2.4 Justificación de los resultados

Obaservamos un aumento de los accidentes en 2020 respecto a los años anteriores, en comparación con 2018 es del 7.37% y un 7.58% con el año 2019. Podriamos decir que existe una tendencia a aumentar del número de accidentes mortales.


  • Accidentes por Condiciones Meteorológicas: Analizamos cómo las condiciones meteorológicas influyen en los accidentes.
# Distribución de la variable WEATHER
df_weather <- accident3Y %>%
  group_by(WEATHER) %>%
  dplyr::summarise(counts = n()) %>%
  left_join(accident3Y %>% select(WEATHER, WEATHERNAME) %>% distinct(), by = "WEATHER")

df_weather
## # A tibble: 13 × 3
##    WEATHER counts WEATHERNAME             
##      <int>  <int> <chr>                   
##  1       1 208045 Clear                   
##  2       2  23852 Rain                    
##  3       3    453 Sleet or Hail           
##  4       4   3314 Snow                    
##  5       5   3230 Fog, Smog, Smoke        
##  6       6    511 Severe Crosswinds       
##  7       7     59 Blowing Sand, Soil, Dirt
##  8       8    212 Other                   
##  9      10  42569 Cloudy                  
## 10      11    325 Blowing Snow            
## 11      12    284 Freezing Rain or Drizzle
## 12      98  20301 Not Reported            
## 13      99   1238 Reported as Unknown
ggplot(accident3Y, aes(x = WEATHERNAME, fill = as.factor(YEAR))) +
  geom_bar(position = "dodge", stat = "count", show.legend = TRUE) +
  scale_fill_manual(values = c("2018" = "skyblue", "2019" = "lightgreen", "2020" = "orange")) +
  labs(title = "Accidentes por Condición Meteorológica y Año", 
       x = "Condición Meteorológica", 
       y = "Número de Accidentes", 
       fill = "Año") +
  theme_minimal() +
  theme(panel.grid = element_blank(),
        axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title.margin = margin(t = 20)) 

#### Justificación de los resultados Se muestra el número de accidentes por condición meteorológica y año. Existe un aumento significativo de accidentes en 2020 bajo condiciones despejadas podría estar relacionado con un aumento del tráfico, mientras que existe una disminución en condiciones meteorológicas adversas como lluvia y nieve que podría reflejar una reducción en el tráfico debido a la pandemia. Este gráfico sugiere que la condición meteorológica más común, el clima despejado, es donde se concentra el mayor número de accidentes. También, vemos un aumento durante la lluvia en 2020 en comparación con los años anteriores. Aunque, estos cambios podrian estar sesgados debido a los años de pandemia, ya que a mayor trafico mayor probabilidad de tener un siniestro, existe una relación positiva.


  • Análisis de Correlación: calculo de la matriz de correlación de las siguientes variables: número de vehículos involucrados, presencia de alcohol y drogas, condiciones meteorológicas, tipo de carretera y el número de víctimas en los accidentes.
    • VE_TOTAL: Número de vehículos involucrados en el accidente
    • DRUNK_DR: Presencia de alcohol en el conductor (0 = No, 1 = Sí)
    • DRUGSPEC: Especificación de si hubo presencia de drogas en el conductor
    • WEATHER: Condiciones meteorológicas en el momento del accidente
    • FUNC_SYS: Tipo de carretera donde ocurrió el accidente
    • FATALS: Número de fatalidades en el accidente
    • RUR_URB: Indicador de zona rural o urbana
    • LGT_COND: Condición de luz en el momento del accidente
    • SP_JUR: Jurisdicción numérica

La matriz de correlación mostrará las relaciones entre las variables. + Las correlaciones cercanas a 1 indican una relación fuerte y positiva entre las variables. + Las correlaciones cercanas a -1 indican una relación fuerte y negativa. + Las correlaciones cercanas a 0 indican que no hay una relación lineal significativa.

# Variables numéricas relevantes
numeric_vars <- accident3Y[, c("VE_TOTAL", "FATALS", "DRUNK_DR", "WEATHER", "FUNC_SYS", "RUR_URB", "LGT_COND", "SP_JUR")]
corr_matrix <- cor(numeric_vars, use = "complete.obs")

# Visualizar la matriz de correlación
library(corrplot)
corrplot(corr_matrix, method = "color", type = "upper", tl.col="black", tl.srt=30, order = "AOE", number.cex=0.75, sig.level = 0.01, addCoef.col = "black")

#### Justificación de los resultados La correlación de 0.12 entre VE_TOTAL y FATALS sugiere una relación muy débil, pero positiva, mientras que la correlación de -0.01 indica que no hay una relación significativa entre el número de vehículos involucrados y la presencia de alcohol. Asimismo, existe una correlación debil pero positiva de 0.07 entre DRUNK_DR y FATALS. Tenemos en su amyoría relaciones débiles a moderadas en su mayoría, otro ejemplo, es la variable RUR_URB que tiene una correlación moderada con VE_TOTAL, lo que indica que las zonas urbanas tienen más vehículos involucrados en los accidentes. Las condiciones de luz en el momento del accidente tienen una correlación algo más fuerte sugiriendo que los accidentes en condiciones de baja visibilidad tienden a ser más graves.


  • Estudio multidimensional de variables STATE,
library(tidyr)
# Contamos los accidentes por estado y año
accident_State <- accident3Y %>%
  group_by(STATENAME, YEAR) %>%
  summarise(total_accidents = n(), .groups = "drop")
# Convertimos año por columna
accident_State2 <- accident_State %>%
  pivot_wider(names_from = YEAR, values_from = total_accidents, 
              values_fill = list(total_accidents = 0))
accident_State2
## # A tibble: 51 × 4
##    STATENAME            `2018` `2019` `2020`
##    <chr>                 <int>  <int>  <int>
##  1 Alabama                2404   2411   2490
##  2 Alaska                  227    210    206
##  3 Arizona                3056   2717   2868
##  4 Arkansas               1336   1214   1556
##  5 California            10716  11012  12323
##  6 Colorado               1808   1735   1893
##  7 Connecticut             834    676    818
##  8 Delaware                332    424    325
##  9 District of Columbia     86     66     88
## 10 Florida                8111   8633   8588
## # ℹ 41 more rows
accident_State3 <- accident_State2 %>%
  pivot_longer(cols = c(`2018`, `2019`, `2020`), 
               names_to = "YEAR", 
               values_to = "total_accidents")

# Gráficamos
library(ggplot2)

ggplot(accident_State3, aes(x = STATENAME, y = total_accidents, fill = YEAR)) +
  geom_bar(stat = "identity", position = position_dodge(width = 0.8), width = 0.7) +  # Ancho ajustado de las barras
  labs(title = "Número de Accidentes por Estado y Año",
       x = "Estado", 
       y = "Número de Accidentes") +
  theme_minimal() +  # Fondo limpio
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, size = 8),  
    axis.text.y = element_text(size = 10),  
    axis.title.x = element_text(size = 12), 
    axis.title.y = element_text(size = 12),  
    legend.position = "top",  
    legend.text = element_text(size = 10), 
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold") 
  ) +
  scale_fill_manual(values = c("2018" = "skyblue", "2019" = "lightgreen", "2020" = "orange")) +  
  coord_cartesian(clip = 'off')

#### Justificación de los resultados Los estados con mayor número de accidentes son California y Texas, especialmente en 2020, donde los accidentes fueron significativamente más altos en comparación con los otros años, auque es una tendencia generalizada en consecuencia de la pandemia. Los 5 primeros estados con mayor número de accidentes durante los 3 años fueron California con 35.400 accidentes, Texas 34.200, Florida 32.400, Ohio 31.500 y Georgia 29.500.


3.1.2.5 Comparativa entre Estados, Condiciones de Carretera y Clima (factores externos)

# Filtramos los estados específicos
accident_ST5 <- subset(accident3Y, 
                           STATENAME %in% c("California", "Texas", "Florida", "Ohio", "Georgia"))
# Gráfico de barras con FATALS por tipo de carretera y estado
ggplot(data = accident_ST5, aes(x = FUNC_SYSNAME, fill = STATENAME)) +
  geom_bar() +
  ggtitle("Número de Fatalidades por Tipo de Carretera y Estado") +
  labs(x = "Tipo de Carretera", y = "Número de Fatalidades") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

#### Justificación de resultados California y Texas presentan más fatalidades en carreteras de alta capacidad como interestatales y principales arteriales, lo que puede deberse a una mayor cantidad de afluencia y tráfico. Florida y Georgia tienen más diversidad según el tipo de carretera, lo que sugiere que tienen una infraestructura más variada y afecta a los accidentes de manera diferente. Ohio presenta menos fatalidades en las carreteras de mayor capacidad, lo que podría ser indicativo de una mejor gestión del tráfico o menores incidencias en carreteras principales. Este análisis sugiere que la infraestructura vial y la cantidad de tráfico son factores clave en la distribución de las fatalidades, y sería útil realizar un análisis más profundo, considerando otras variables como la densidad de tráfico.

# Gráfico de barras apiladas por FATALS, facetas por WEATHER
ggplot(data = accident_ST5, aes(x = FATALS, fill = STATENAME)) +
  geom_bar(position = "fill") +  # Usamos "fill" para ver la proporción
  facet_wrap(~WEATHER) +  # Facetamos por las condiciones meteorológicas
  ggtitle("Número de Fatalidades por Estado, Tipo de Carretera y Condiciones Meteorológicas") +
  labs(x = "Número de Fatalidades", y = "Proporción") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

#### Justificación de los resultados El gráfico nos muestra la relación entre las fatalidades por estado, condiciones meteorológicas y segun el tipo de carrtera. Al comparar los estados, se observa que estados como California y Texas tienen barras más grandes y más segmentos, lo que refleja una mayor cantidad de accidentes en diversas condiciones meteorológicas. En cambio, estados como Ohio y Florida presentan menos fatalidades en ciertas condiciones, lo que podría estar relacionado con diferentes patrones de tráfico. El gráfico identificar patrones en las condiciones meteorológicas, por ejemplo, las condiciones despejadas o soleadas pueden estar relacionadas con más fatalidades en California debido a su alto volumen de tráfico, mientras que las condiciones de lluvia o tormentas podrían estar asociadas con más fatalidades en estados con infraestructuras viales deficientes. En términos de tipo de carretera, se observa que las carreteras de alta capacidad, como las interestatales y autopistas principales en estados como California y Texas, tienen una mayor incidencia de fatalidades. Esto se debe a la alta densidad de tráfico en estas vías. En resumen, este análisis m¡nos permite comparar cómo las fatalidades se distribuyen en los diferentes estados según las condiciones meteorológicas, sugiriendo que factores como la infraestructura vial, la densidad de tráfico y la gestión de condiciones meteorológicas juegan un papel fundamental en los siniestros. Los estados con más fatalidades en condiciones meteorológicas adversas podrían necesitar mejorar la infraestructura o implementar políticas de seguridad vial más estrictas.


3.1.2.6 Discretización de la variable MONTH

Discretizamos la variable MONTH en intervalos mensuales. Se dividirá en intervalos trimestrales (enero-marzo), segundo trimestre (abril-junio), tercer trimestre (julio-septiembre) y cuarto trimestre (octubre-diciembre).

# Discretizamos la variable MONTH en 4 trimestres
accident3Y$MONTH_BIN <- cut(
  accident3Y$MONTH,
  breaks = c(0, 3, 6, 9, 12),
  labels = c("Q1", "Q2", "Q3", "Q4"), 
  right = FALSE
)

# Verificamos las primeras filas con la nueva columna MONTH_BIN
head(accident3Y)
##   ST_CASE STATE STATENAME VE_TOTAL VE_FORMS PVH_INVL PEDS PERSONS PERMVIT
## 1   10001     1   Alabama        2        1        1    0       1       1
## 2   10002     1   Alabama        1        1        0    0       2       2
## 3   10002     1   Alabama        1        1        0    0       2       2
## 4   10003     1   Alabama        2        2        0    0       2       2
## 5   10003     1   Alabama        2        2        0    0       2       2
## 6   10003     1   Alabama        2        2        0    0       2       2
##   PERNOTMVIT COUNTY      COUNTYNAME CITY       CITYNAME DAY DAYNAME MONTH
## 1          0    121 TALLADEGA (121) 1870        LINCOLN   5       5     1
## 2          0    127    WALKER (127) 1780         JASPER   8       8     1
## 3          0    127    WALKER (127) 1780         JASPER   8       8     1
## 4          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
## 5          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
## 6          0     21    CHILTON (21)    0 NOT APPLICABLE   8       8     1
##   MONTHNAME YEAR DAY_WEEK DAY_WEEKNAME HOUR        HOURNAME MINUTE MINUTENAME
## 1   January 2018        6       Friday    6   6:00am-6:59am      0          0
## 2   January 2018        2       Monday    0   0:00am-0:59am     48         48
## 3   January 2018        2       Monday    0   0:00am-0:59am     48         48
## 4   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
## 5   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
## 6   January 2018        2       Monday   22 10:00pm-10:59pm     50         50
##   NHS                    NHSNAME ROUTE  ROUTENAME TWAY_ID   TWAY_ID2 RUR_URB
## 1   1 This section IS ON the NHS     1 Interstate    I-20                  1
## 2   1 This section IS ON the NHS     1 Interstate    I-22 CALUMET RD       2
## 3   1 This section IS ON the NHS     1 Interstate    I-22 CALUMET RD       2
## 4   1 This section IS ON the NHS     1 Interstate    I-65                  1
## 5   1 This section IS ON the NHS     1 Interstate    I-65                  1
## 6   1 This section IS ON the NHS     1 Interstate    I-65                  1
##   RUR_URBNAME FUNC_SYS FUNC_SYSNAME RD_OWNER         RD_OWNERNAME MILEPT
## 1       Rural        1   Interstate        1 State Highway Agency   1676
## 2       Urban        1   Interstate        1 State Highway Agency   1621
## 3       Urban        1   Interstate        1 State Highway Agency   1621
## 4       Rural        1   Interstate        1 State Highway Agency   2028
## 5       Rural        1   Interstate        1 State Highway Agency   2028
## 6       Rural        1   Interstate        1 State Highway Agency   2028
##   MILEPTNAME LATITUDE LATITUDENAME  LONGITUD LONGITUDNAME SP_JUR
## 1       1676 33.59133  33.59133056 -86.13187 -86.13186944      0
## 2       1621 33.80919  33.80918611 -87.28985 -87.28984722      0
## 3       1621 33.80919  33.80918611 -87.28985 -87.28984722      0
## 4       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
## 5       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
## 6       2028 32.76774  32.76773611 -86.56404 -86.56403611      0
##                SP_JURNAME HARM_EV          HARM_EVNAME MAN_COLL
## 1 No Special Jurisdiction      14 Parked Motor Vehicle        0
## 2 No Special Jurisdiction      38                Fence        0
## 3 No Special Jurisdiction      38                Fence        0
## 4 No Special Jurisdiction      24       Guardrail Face        0
## 5 No Special Jurisdiction      24       Guardrail Face        0
## 6 No Special Jurisdiction      24       Guardrail Face        0
##                                      MAN_COLLNAME RELJCT1 RELJCT1NAME RELJCT2
## 1 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 2 Not a Collision with Motor Vehicle In-Transport       1         Yes      19
## 3 Not a Collision with Motor Vehicle In-Transport       1         Yes      19
## 4 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 5 Not a Collision with Motor Vehicle In-Transport       0          No       1
## 6 Not a Collision with Motor Vehicle In-Transport       0          No       1
##                              RELJCT2NAME TYP_INT         TYP_INTNAME WRK_ZONE
## 1                           Non-Junction       1 Not an Intersection        0
## 2 Other location within Interchange Area       1 Not an Intersection        0
## 3 Other location within Interchange Area       1 Not an Intersection        0
## 4                           Non-Junction       1 Not an Intersection        1
## 5                           Non-Junction       1 Not an Intersection        1
## 6                           Non-Junction       1 Not an Intersection        1
##   WRK_ZONENAME REL_ROAD REL_ROADNAME LGT_COND       LGT_CONDNAME WEATHER
## 1         None        2  On Shoulder        4               Dawn       1
## 2         None        3    On Median        2 Dark - Not Lighted       2
## 3         None        3    On Median        2 Dark - Not Lighted       2
## 4 Construction        4  On Roadside        2 Dark - Not Lighted      10
## 5 Construction        4  On Roadside        2 Dark - Not Lighted      10
## 6 Construction        4  On Roadside        2 Dark - Not Lighted      10
##   WEATHERNAME SCH_BUS SCH_BUSNAME    RAIL       RAILNAME NOT_HOUR  NOT_HOURNAME
## 1       Clear       0          No 0000000 Not Applicable        6 6:00am-6:59am
## 2        Rain       0          No 0000000 Not Applicable        0 0:00am-0:59am
## 3        Rain       0          No 0000000 Not Applicable        0 0:00am-0:59am
## 4      Cloudy       0          No 0000000 Not Applicable       99       Unknown
## 5      Cloudy       0          No 0000000 Not Applicable       99       Unknown
## 6      Cloudy       0          No 0000000 Not Applicable       99       Unknown
##   NOT_MIN NOT_MINNAME ARR_HOUR    ARR_HOURNAME ARR_MIN ARR_MINNAME HOSP_HR
## 1      99     Unknown        6   6:00am-6:59am      15          15      88
## 2      99     Unknown        0   0:00am-0:59am      59          59      88
## 3      99     Unknown        0   0:00am-0:59am      59          59      88
## 4      99     Unknown       23 11:00pm-11:59pm      10          10      99
## 5      99     Unknown       23 11:00pm-11:59pm      10          10      99
## 6      99     Unknown       23 11:00pm-11:59pm      10          10      99
##                        HOSP_HRNAME HOSP_MN                       HOSP_MNNAME
## 1 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 2 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 3 Not Applicable (Not Transported)      88  Not Applicable (Not Transported)
## 4                          Unknown      99 Unknown EMS Hospital Arrival Time
## 5                          Unknown      99 Unknown EMS Hospital Arrival Time
## 6                          Unknown      99 Unknown EMS Hospital Arrival Time
##   FATALS DRUNK_DR VEH_NO PER_NO DRUGSPEC   DRUGSPECNAME DRUGRES
## 1      1        0      1      1        1    Whole Blood       1
## 2      2        0      1      1        0 Test Not Given       0
## 3      2        0      1      2        0 Test Not Given       0
## 4      1        0      1      1        1    Whole Blood     401
## 5      1        0      1      1        1    Whole Blood     417
## 6      1        0      1      1        1    Whole Blood     316
##                       DRUGRESNAME MONTH_BIN
## 1 Tested, No Drugs Found/Negative        Q1
## 2                  Test Not Given        Q1
## 3                  Test Not Given        Q1
## 4                     AMPHETAMINE        Q1
## 5                 METHAMPHETAMINE        Q1
## 6                      Clonazepam        Q1
# Eliminar los registros con valores NA en la columna MONTH_BIN
accident3Y_clean <- accident3Y[!is.na(accident3Y$MONTH_BIN), ]

# Verificar nuevamente la distribución de los datos
table(accident3Y_clean$MONTH_BIN)
## 
##    Q1    Q2    Q3    Q4 
## 42705 69595 84364 82452
# Verificamos la distribución de los intervalos
table(accident3Y_clean$MONTH_BIN)
## 
##    Q1    Q2    Q3    Q4 
## 42705 69595 84364 82452
# Gráficamos la distribución de los intervalos de MONTH_BIN
ggplot(accident3Y_clean, aes(x = MONTH_BIN, fill = MONTH_BIN)) +
  geom_bar() +
  ggtitle("Distribución de Accidentes por Trimestre del Año") +
  labs(x = "Trimestre", y = "Número de Accidentes") +
  theme_minimal()

#### Jusrificación de resultados Discretización de la variable MONTH en trimestres nos permite observar cómo varían las fatalidades a lo largo del año. El gráfico muestra que los **accidentes son más frecuentes en los trimestres Q3 y Q4, lo que puede estar relacionado con un aumento del tráfico o condiciones meteorológicas específicas en estos períodos. La menor frecuencia de accidentes en Q1 podría reflejar una disminución de los viajes debido al clima invernal o a las menores actividades en invierno, mientras que Q3 se dispara, lo que podría estar influenciado por el turismo o el tráfico veraniego. Y, Q4 podría estar influenciado por el clima de invierno, la proximidad de las festividades y el aumento de desplazamientos, lo que incrementa el riesgo de accidentes. Útil para diseñar estrategias de seguridad vial en épocas específicas del año, como campañas de concienciación durante los meses con mayor número de accidentes.


  • Análisis de Componentes Principales (PCA) Vamos a usar las variables VE_TOTAL, FATALS, PEDS, PERSONS, DRUGSPEC y realizarEMOS un análisis de PCA.
# Variables numéricas
numerics <- c("VE_TOTAL", "FATALS", "PEDS",  "PERSONS", "DRUGSPEC")
accident_numerics <- accident3Y[, numerics]
# Eliminarmos los NA
accident_numerics <- na.omit(accident_numerics)
# Normalizarmos los datos
accident_numerics2 <- scale(accident_numerics)
# PCA
pca1 <- prcomp(accident_numerics2, center = TRUE, scale. = TRUE)
# Resumen del pca
summary(pca1)
## Importance of components:
##                           PC1    PC2    PC3    PC4     PC5
## Standard deviation     1.3395 1.0025 0.9687 0.9410 0.61380
## Proportion of Variance 0.3589 0.2010 0.1877 0.1771 0.07535
## Cumulative Proportion  0.3589 0.5599 0.7476 0.9246 1.00000
# Visualización de la varianza explicada por cada componente
if(!require("factoextra")) install.packages("factoextra")
library("factoextra")
# Graficamos la varianza explicada
# Usando fviz_eig con colores personalizados por componente
fviz_eig(pca1, addlabels = TRUE, ylim = c(0, 50)) +
  scale_fill_manual(values = c("skyblue", "lightgreen", "orange", "pink", "purple"))

3.1.2.7 Justificación de los resultados

Este gráfico muestra la proporción de la varianza explicada por cada componente principal en el análisis de componentes principales (PCA). El PC1 explica el 35.9% de la varianza total, lo que significa que esta dimensión es la que contiene la mayor cantidad de información de los datos. PC2 explica el 20.1% de la varianza, lo que indica que agrega una cantidad significativa de variabilidad al modelo. PC3, PC4 y PC5 aunque importantes, explican una porción mucho más pequeña de la varianza. PC3 explica el 18.8%, PC4 explica el 17.7%, y PC5 explica el 7.5%. La desviación estándar de los componentes principales indica cuánta variabilidad tiene cada componente.El PC1 tiene una desviación estándar de 1.3395, lo que significa que captura una gran cantidad de variabilidad en los datos, mientras que PC2 captura 1.0025, lo que también es significativo. La Proporción acumulada de PC1 + PC2 explica 55.99% de la varianza total. Por tanto, para un buen ajuste es recomendable capturar el 92.46% de la varianza total que engloba PC1 + PC2 + PC3 + PC4.


  • Clustering usando K-means
# Seleccionamos los primeros 4 componentes principales
pca_data <- pca1$x[, 1:4]
# Aplicamos el algoritmo K-means
set.seed(123)
kmeans_result <- kmeans(pca_data, centers = 4)
# Añadimos la columna con los grupos/clusters al dataframe original
accident3Y$Cluster <- kmeans_result$cluster
# Visualización con ggplot
ggplot(accident3Y, aes(x = pca_data[, 1], y = pca_data[, 2], color = as.factor(Cluster))) +
  geom_point(alpha = 0.7) +
  labs(title = "Visualización de Clusters usando PCA (PC1 vs PC2)",
       x = "PC1", y = "PC2", color = "Cluster") +
  theme_minimal() +
  scale_color_manual(values = c("red", "blue", "green", "purple"))

3.1.2.8 Justificación de los resultados

Al aplicar K-means, hemos podido identificado 4 grupos de accidentes con características similares. Cada color corresponde a un cluster, lo que nos permite visualizar cómo se segmentan los datos en función de las componentes principales. La dispersión de los puntos dentro de cada cluster puede ser útil para entender la variabilidad interna de los grupos. Cluster 1 (rojo): Se concentra en valores negativos de PC1 y PC2, sugiriendo características distintas con valores bajos en ambas dimensiones. Cluster 2 (azul): Centrado cerca de cero en PC1 con alta dispersión en PC2, indicando valores intermedios en PC1 y mayor variabilidad en PC2. Cluster 3 (verde): Con valores positivos en PC1 y negativos en PC2, indicando características correlacionadas con PC1 altos y PC2 bajos. Cluster 4 (púrpura): Se encuentra en el extremo derecho, con valores altos en ambas dimensiones, sugiriendo una correlación positiva entre PC1 y PC2.


  • Análisis de Características de los Clusters Exploración de las características de cada cluster para entender mejor qué factores están contribuyendo a su formación, como la distribución de FATALS, el tipo de carretera, el estado, las condiciones meteorológicas, etc. Esto podría ayudarnos a identificar qué atributos definen a cada cluster.

Calculamos la media y la mode

# Función para calcular la mode
Mode <- function(x) {
  ux <- unique(x)  
  ux[which.max(tabulate(match(x, ux)))] 
}
# Añadimos el cluster resultante al dataset
accident3Y$Cluster <- kmeans_result$cluster
# Analizamos las características por cluster, seleccionando variables relevantes
cluster_summary <- accident3Y %>%
  group_by(Cluster) %>%
  summarise(
    mean_fatalities = mean(FATALS, na.rm = TRUE),
    mean_ve_total = mean(VE_TOTAL, na.rm = TRUE),
    mean_persons = mean(PERSONS, na.rm = TRUE),
    mean_peds = mean(PEDS, na.rm = TRUE),
    mode_weather = Mode(WEATHERNAME), 
    mode_func_sys = Mode(FUNC_SYSNAME),  
    mode_state = Mode(STATENAME),
  )
print(cluster_summary)
## # A tibble: 4 × 8
##   Cluster mean_fatalities mean_ve_total mean_persons mean_peds mode_weather
##     <int>           <dbl>         <dbl>        <dbl>     <dbl> <chr>       
## 1       1            1.09          1.87         3.11  0.000179 Clear       
## 2       2            2.49          3.95        10.1   0.0193   Clear       
## 3       3            1.10          1.77         2.60  0.292    Clear       
## 4       4            1.03          1.28         1.78  1.13     Clear       
## # ℹ 2 more variables: mode_func_sys <chr>, mode_state <chr>
  • Gráfico de barras para comparar las medias por Cluster
library(reshape2)

# Convertir los datos del resumen en formato largo
cluster_long <- cluster_summary %>%
  select(Cluster, mean_fatalities, mean_ve_total, mean_persons, mean_peds) %>%
  melt(id.vars = "Cluster", variable.name = "Variable", value.name = "Mean_Value")

# Gráfico de barras para comparar las medias por Cluster
ggplot(cluster_long, aes(x = factor(Cluster), y = Mean_Value, fill = Variable)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Comparación de Características por Cluster",
       x = "Cluster", y = "Valor Promedio") +
  scale_fill_brewer(palette = "Set3") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

3.1.2.9 Justificación de los resultados

Explicación de las variables: + mean_fatalities (fatalidades): Promedio de las fatalidades por accidente. + mean_ve_total (número total de vehículos involucrados): Promedio de vehículos involucrados en los accidentes. + mean_persons (número de personas involucradas): Promedio de personas involucradas en los accidentes. + mean_peds (número de peatones involucrados): Promedio de peatones involucrados en los accidentes. Cluster 1 representa accidentes más pequeños o menos graves con menos vehículos, personas y peatones involucrados. Cluster 2 representa los accidentes más graves con mayor número de vehículos y personas involucradas. Cluster 3 y Cluster 4 son intermedios, con características que se asemejan más a los grupos más graves pero sin llegar a los niveles del Cluster 2. Este gráfico te permite comparar cómo las variables clave de los accidentes (fatalidades, vehículos, personas, peatones) varían a lo largo de diferentes grupos (clusters) generados en el análisis de PCA y clustering.


  • Gráfico de barras apiladas para las modas de las variables categóricas
# Convertimos las modas
cluster_mode <- cluster_summary %>%
  select(Cluster, mode_weather, mode_func_sys, mode_state) %>%
  melt(id.vars = "Cluster", variable.name = "Variable", value.name = "Mode")
# Gráfico de barras apiladas para las modas de las variables categóricas
ggplot(cluster_mode, aes(x = factor(Cluster), fill = Mode)) +
  geom_bar(stat = "count", position = "stack") +
  facet_wrap(~Variable, scales = "free") +
  labs(title = "Distribución de Modas por Cluster",
       x = "Cluster", y = "Frecuencia") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

#### Justificación de los resultados Gráfico comparativo de las frecuencias de las categorías más comunes de las variables categóricas (como clima, tipo de carretera y estado) por cada cluster. Este gráfico permite observar cómo se distribuyen las categorías más frecuentes (modas) dentro de cada cluster para las variables categóricas como el clima, el tipo de carretera y el estado. Ayuda a ver qué características son predominantes dentro de cada cluster, como si ciertos estados o tipos de carreteras están asociados a clusters con más fatalidades.


4 Ejercicio 3


A partir del juego de datos utilizado en el ejemplo de la PEC, realiza un análisis exploratorio de datos con el paquete explore() de R y comenta las ventajas e inconvenientes que presenta respecto al análisis realizado en el ejercicio 2.

Puedes utilizar la documentación publicada del paquete explore() en https://github.com/rolkra/explore

if (!requireNamespace("explore", quietly = TRUE)) {
  install.packages("explore")
}

# Cargar el paquete
library(explore)

Paso 2: Aplicación del Paquete explore() Aplicaremos el paquete explore() a nuestro conjunto de datos (accident3Y), o al conjunto de datos proporcionado, y realizaremos un análisis exploratorio de datos (EDA).

explore(accident3Y)

Paso 3: Analisis de los resultados con explore()

4.0.0.1 Ventajas de usar explore():

  • Rapidez y eficiencia: El principal beneficio de usar explore() es su rapidez. Con una sola línea de código, obtienes un análisis completo del dataset. En vez de escribir múltiples bloques de código para cada análisis (estadísticas descriptivas, gráficos, identificación de outliers), todo se hace automáticamente. Esto es ideal cuando el tiempo es limitado o cuando se necesita hacer un análisis exploratorio preliminar de manera rápida.

  • Automatización: El paquete se encarga de generar los gráficos y cálculos necesarios sin tener que realizar tareas repetitivas de manera manual. Esto es útil cuando se tiene un gran volumen de datos o cuando se quiere ahorrar tiempo en el análisis preliminar.

  • Resumen: explore() es útil cuando quieres obtener rápidamente una visión global del conjunto de datos. Muestra la distribución de las variables, las relaciones entre ellas y cualquier irregularidad en los datos.

4.0.0.2 Inconvenientes de explore():

  • Poca personalización: Aunque explore() genera resúmenes estadísticos y gráficos automáticamente, la personalización es limitada. Si necesitas ajustar el formato de los gráficos, cambiar los colores, o realizar análisis más específicos (como segmentar datos en función de una variable), entonces tendrás que recurrir a herramientas más flexibles, como ggplot2 o dplyr.

  • Análisis superficial: El paquete ofrece una descripción general, pero no profundiza en el contexto ni en las relaciones complejas entre las variables. En el ejercicio 2, el análisis fue realizado manualmente, lo que nos permitió tomar decisiones informadas sobre qué hacer con los datos (como seleccionar variables de interés, hacer transformaciones específicas o crear gráficos personalizados).

  • Poca flexibilidad para análisis complejos: Si se necesitan análisis más complejos, como el cálculo de métricas específicas o la creación de modelos, explore() no ofrece esta flexibilidad. Por ejemplo, si tuviéramos que aplicar técnicas de modelado o realizar un análisis de regresión, necesitaríamos hacerlo manualmente utilizando otros paquetes de R.

4.0.0.3 Comparación con el ejercicio 2:

En el ejercicio 2, realizamos un análisis exploratorio de datos detallado utilizando ggplot2, dplyr y otras librerías para limpiar, transformar y visualizar los datos. Este enfoque nos permitió:

  • Seleccionar variables específicas para el análisis.
  • Personalizar los gráficos de acuerdo con nuestras necesidades (como cambiar colores, títulos, etc.).
  • Aplicar técnicas específicas para limpiar los datos, como la detección y eliminación de outliers, o la conversión de variables categóricas en factores.

En cambio, con explore(), no tenemos tanto control sobre el análisis y la visualización. El paquete genera un resumen rápido, pero la capacidad de personalizar el análisis y los gráficos es limitada.

4.0.0.4 Ventajas del ejercicio 2 sobre explore():

  • Mayor personalización y control sobre el análisis y la visualización.
  • Más flexibilidad para aplicar técnicas avanzadas de limpieza de datos o realizar análisis más detallados.
  • Posibilidad de realizar análisis específicos como la creación de variables derivadas, el uso de técnicas estadísticas avanzadas, o la aplicación de modelos de Machine Learning.

4.0.0.5 Desventajas:

  • Mayor tiempo de implementación, ya que tenemos que escribir más código para realizar el mismo análisis.
  • Mayor riesgo de errores debido a la necesidad de personalizar cada paso.

4.0.0.6 Conclusión:

explore() es muy útil para realizar un análisis exploratorio rápido y obtener una visión global del conjunto de datos de forma automatizada. Sin embargo, es menos flexible que el enfoque manual realizado en el ejercicio 2, donde tenemos más control sobre el análisis y la personalización de los gráficos. Si buscamos rapidez y eficiencia para un análisis preliminar, explore() es una excelente opción. Sin embargo, si necesitamos un análisis más profundo, específico o personalizado, el enfoque manual será más adecuado.