Portada
 

1 Palabaras Clave

Tiempo resolución
Defecto /No defecto
Random Forest
Segmentación

2 Resumen

Ante la problemática surgida del tiempo de resolución de tramitaciones, se planteó el objetivo de determinar las principales causas que originaban dificultades en dichos tiempos. Para ello recurrimos a análisis descriptivo, que posteriormente nos ayudó a la construcción de un modelo que nos permita comprender el comportamiento.

Tras este proceso hemos visto como las distintas titulaciones y las actividades influyen en el tiempo que se tarda en resolver las solicitudes. Al igual que el mes, en el que se produce un pico en los meses de septiembre lo que lleva a la acumulación de las solicitudes, y el consiguiente retraso. Por último, destacar que el curso también influye sobre el tiempo, variando de un período a otro.

3 Objetivos

El análisis se centra en la búsqueda de los principales motivos que provocan un retraso en el proceso administrativo de tramitación de documentos en el cegeca de elche.

Además, nos planteamos si existe alguna relación entre los distintos tipos de actividades y el periodo de tramitación. Por otra parte si el tipo de titulación también puede influir en dicho periodo.

Finalmente, también nos interesa conocer si existe un comportamineto estacional en la entrada de solicitudes, para valorar la concentración del trabajo en determinados periodos de tiempo, y además valorar si el curso en qué se realiza la tramitación supone algún efecto sobre el tiempo.

Con todo esto, pretendemos mejorar la eficiencia de este departamento, para que los usuarios estén más satisfechos con el servicio ofertado.

4 Información Disponible

Los datos han sido recogidos durante el transcurso de dos años lectivos, concretamente los cursos 2015/16 y 2016/17 y fueron facilitados por lel organismo CEGECA de la Universidad Miguel Hernández.

4.1 Presentación de los datos

La base esta formada por un total de 4120 observaciones y un total de 19 variables.

La variable de interés para nuestro análisis es los días de resolucion. Se trata de una variable contínua que refleja el tiempo que se tardó en resolver la solicitud desde el día en que fue presentada.

El resto de variables explicativas que nos van a ayudar a comprender el comportamiento de los días de resolución son las siguientes:

  • tipo.titulación : consiste en una variable categórica con tres niveles ( grado, master, extinto).
  • centro : factor con 4 niveles (CEXP,CSJE,CSSA,EPSE) que representa las facultades de pertenencia.
  • curso : factor con dos niveles (2015/16 y 2016/17)
  • tipo.actividad : factor que cuenta con 8 niveles (deporte,estudiosnouni, idiomas, laboral,movibilidad, otros, practicas,universidad), haciendo referencia a las distintos tipos de solicitudes.
  • mes de solicitud : factor que cuenta con un nivel para cada mes del año.

Otras variables que se incluían en la base, pero que no eran significativas para el análisis, son:

  • dni
  • titulación
  • cod.centro
  • actividad
  • fecha de solicitud
  • fecha de resolucion
  • dia laboral
  • dia de solicitud
  • semana de solicitud
  • año de solicitud
  • días desde el origen

5 Análisis de los datos

5.1 Procesado de los datos

Antes de empezar a tratar con los datos, declaramos como factores aquellas varibles que R no detecta como tal. Además, cambiamos el formato de la fecha, para su mejor manipulación. Y finalmente eliminamos del conjunto de varibales el dni y días desde el origen, ya que son innecesarias y no aportan nada al estudio.

5.2 Métodos estadísticos

Para una primera inmersión en los datos y su análisis descriptivo emplearemos distintos gráficos, que permitan comprender la naturaleza de los datos de mejor forma. Y así poder crear un modelo que se ajuste de la mejor manera posible a las observaciones.

Para este análisis el modelo seleccionado es el de random forest, que consiste en, utilizando las variables independientes proporcionadas, crear árboles de decisión aleatorios para hacer la clasificación en una categoría o otra. Dicha clasificación se realiza a través de una votación, es decir, se agrupan los resultados de los distintos árboles aleatorios, y al registro se le asigna la categoría predominante.

Hemos elegido este modelo ya que nos indica en la salida qué variables son más importantes. Además nos permite distinguir y elegir las variables más significativas de entre las multitud que se pueden introducir.

Por otra parte, gracias a que utiliza una gran cantidad de árboles, no se limita a un único, nos permite encontrar la solución con la mayor precisión posible, y por consigueinte el menor error.

El modelo final seleccionado para el análisis ha sido el siguiente:

\(\color{#0B6889}{defectos= tipo.titulacion\ + curso\ + centro\ + tipo.actividad\ + mes.solic }\)

5.3 Software y Hardware

Para llevar a cabo el estudio hemos utilizado el software estadístico de R Studio. Hemos recurrido a las siguientes librerías :

  • ggplot2
  • extrafont
  • ggridges
  • ggpubr
  • scales
  • gridextra
  • grid
  • dplyr
  • tidyverse
  • rcolorbrewer
  • randomforest

6 Resultados

6.1 Estadisticas Descriptivas

Comenzamos haciendo unos histogramas para tener una primera visión de la distribución de la variable días de resolución.

En el primer gráfico, se observa cómo existen muy pocos valores con un tiempo de resolución alto, y haciendo un análisis de los datos nos damos cuenta que únicamente estamos hablando de 7 registros por encima de los 100 días de resolución. Por eso, reducimos la base de datos a los días con tiempo de resolución menor a 100. Se observa que, a pesar de quitar los datos superios a 100, la distribución sigue siendo asimétrica.

En este gráfico se informa sobre los porcentajes de las actividades,el curso y los distintos centros. El tipo de actividad predominante son las practicas, mientras que el centro que destaca es CSSA, seguido por CSJE.

Por otro lado, vemos como el volumen de registros en ambos cursos es similar.

Como se observa, la variable curso resulta de gran interés, ya que en el curso 2015 existe una mayor variabilidad. En el curso 2016 la gran mayoría tardan hasta 30 días, mientras que en el curso 2015 se alargaba mas.

Este gráfico nos muestra la duración de las actividades para los dos cursos. Se destaca la gran variabilidad en función de la actividad. En el curso 2015 el intervalo de variación de las tramitaciones era sensiblemente superior, mientras que estos plazo se consiguieron mejorar un poco en el 2016. Por último, podemos ver como la actividad laboral presenta los mayores tiempos de resolución.

Estos gráficos reflejan como el mayor volumen de solicitudes que recibe el cegeca se produce en los meses de septiembre, debido al inicio del cursos, y el mes de junio, coincidiendo con el final del curso.

En cuanto a los días de resolución, vemos que hay una serie de actividades que tardan más que el resto, como son laborales y estudiosnouni si los solicitamos en los meses de septiembre.

Este gráfico nos muestra el tiempo medio de resolución por el tipo de titulación. Se observa cómo el máster es la titulación en la cual se más tarda en resolver las solicitudes.

En cambio, en este otro gráfico, en el que se representan el volumen de solicitudes de los distintos tipos de actividad para los 3 niveles de titulación, existe una tipologia de actividad que predomina en el máster como son las del tipo laboral, mientras que el resto son más frecuentes en los grados.

Con este gráfico lo que pretendemos es observar si de manera visual, vemos si los centros pueden influir sobre los tiempos de resolución. Observamos que en algunos centros, el volumen de ciertas actividades es muy elevado en comparación con el resto, lo que nos puede llevar a pensar que puede ser significativo en el modelo.

6.2 Análisis estadístico

Una vez tenemos las variables seleccionadas, preparamos los datos estableciendo la consideración de que si una solicitud tarda más de 15 días se cosidera un defecto.

## 
## Call:
##  randomForest(formula = datadef ~ tipo.titulacion + curso + tipo.actividad +      mes.solic, data = datos100, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 19.69%
## Confusion matrix:
##            DEFECTO NO_DEFECTO class.error
## DEFECTO       1491        374   0.2005362
## NO_DEFECTO     436       1812   0.1939502
## 
## Call:
##  randomForest(formula = datadef ~ tipo.titulacion + curso + centro +      tipo.actividad + mes.solic, data = datos100, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 18.19%
## Confusion matrix:
##            DEFECTO NO_DEFECTO class.error
## DEFECTO       1446        419   0.2246649
## NO_DEFECTO     329       1919   0.1463523

EL primer modelo, en el que se incluye la variable centro, nos proporciona una precisión del 80%. Mientras que si la incluimos, aumenta hasta un 82%. Si el usuario esta dispuesto a perder ese 2% de precisión, puede escoger el modelo sin la variable centro.

En el modelo que seleccionamos, incluyendo la variable centro, podemos destacar que nos proporciona una precisión del 82%, que se obtiene como el resultado de restarle a 100 el porcentaje de OBB error. El modelo random forest cuenta con dos parámetros (ntree:número de árboles y mtry:variables candidatas a ser nodo principal.) cuya modificación nos puede llevar a una posible mejora de dicho modelo.

Este gráfico muestra la evolucion del error si aumentamos el número de árboles en nuestro modelo. Hay que destacar que, a partir de los 250 árboles, el error comienza a estabilizarse, lo que nos da una idea de que, a pesar que vayamos aumentando el número de árboles, no vamos a conseguir mejorar el modelo. Por tanto el parámetro ntree no nos ayuda a aumentar la precisión.

El parámetro OOB( Out of Bag) nos indica el error global del modelo, mientras que los parámetros de defectos y no defectos, indican la proporción de error dentro de las mismas clases respectivamente.

Por otro lado, tenemos el parámetro mtry, que sirve para establecer el número de variables independientes aleatorios candidatas a ser el nodo principal en cada árbol.

Se puede ver que no existe diferencias significativas entre las 5 posibilidades. Por tanto, la única opción que nos resta, para intentar mejorar el modelo es la introducción de las interacciones en el mismo.

## 
## Call:
##  randomForest(formula = datadef ~ tipo.titulacion + tipo.titulacion:curso +      curso + curso:centro + curso:tipo.actividad + curso:mes.solic +      centro + centro:tipo.actividad + centro:mes.solic + centro:mes.solic:curso +      tipo.actividad + mes.solic, data = datos100) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 18.28%
## Confusion matrix:
##            DEFECTO NO_DEFECTO class.error
## DEFECTO       1451        414   0.2219839
## NO_DEFECTO     338       1910   0.1503559

Aquí vemos como intoduciendo más variables al modelo y aumentando su complejidad, la precisión no mejora mucho. De tal forma que, teniendo en cuenta el principio de parsimonia, nos vamos a quedar con el modelo simple.

Con ayuda de este gráfico podemos ver la importancia que tiene cada una de las variables introducidas en nuestro modelo. La que más nos aporta es la de mes en que se realiza la solicitud, ya que si la quitamos de nuestro modelo, hace disminuir en media un 20% de la precisión del modelo.

Con el modelo seleccionado, cogemos 30 observaciones aleatorias del data set para hacer las predicciones y vemos que el modelo se equivoca sólo en 4 a la hora de clasificar.

##             dias_de_resolucion
## prediccion   DEFECTO NO_DEFECTO
##   DEFECTO         11          1
##   NO_DEFECTO       3         15

7 Conclusiones

Tras haber analizado a fondo los problemas existentes, podemos tratar de resolver las cuestiones que nos planteábamos al principio y que han sido objeto de nuestro análisis.
El principal objetivo que debíamos tratar de solventar era el excesivo tiempo de tramitación de los documentos. Para ello, decidimos encontrar las causas que provocaban este retraso en los trámites, para que posteriormente el CEGECA supiese que problemas debía abordar.
Por un lado, hemos visto como efectivamente el tipo de actividad influye en los periodos de tramitación. En este caso vemos como las actividades laborales tienden a ser las que más tiempo requieren.

Por otro lado, también nos interesaba saber al igual que con el tipo de actividad si la titulación también influía en los intervalos de resolución. En este sentido, hemos visto como en los masters el tiempo de resolución es sensiblemente superior que en los títulos extintos, donde se dan los tiempos de espera más pequeños.

También se ha observado cómo existe una variación en los tiempos en función del curso en el que se realiza el trámite. De tal manera, en el curso 2015 las variaciones en el tiempo son superiores en comparación con el curso 2016, lo que nos refleja que se ha implementado alguna mejora en el 2016 que ha ayudado a controlar la variación.

Finalmente, también nos planteábamos en un principio si existía una componente estacional tanto en la llegada de solicitudes como en los tiempos de tramitación. En cuanto a las llegadas se observa como septiembre es el mes donde más solicitudes se registran, cosa atribuible al inicio de curso. Esto hecho genera fundamentalmente en el año 2015, aunque también en menor medida en el año 2016 un considerable aumento en los tiempos de tramitación que vemos reflejo en los meses posteriores a septiembre.

Estos son los factores más relevantes que pueden estar generando una congestión en el sistema.Posibles mejoras serían, estudiar el protocolo que se está aplicando en el momento actual y ver pasos innecesarios o repetitivos, e intentar agilizar el proceso. Por otro lado, teniendo en cuenta que el volumen más elevado de solicitudes se produce en el comienzo y fin del curso, se podrían planificar de una manera más eficiente los recursos disponibles.

8 referencias

9 Anexos

A continuación se detalla el código que se ha empleado para el estudio de nuestra problema.

datos=(read.csv('datos.csv', stringsAsFactors = TRUE,
                colClasses   =c(rep('factor',10),
                                'numeric',rep('factor',7),'numeric')))

datos1<-datos

datos1$solicitud=as.Date(datos1$solicitud,format= "%d/%m/%y")
datos1$resolucion=as.Date(datos1$resolucion,format= "%d/%m/%y")
datosf=datos1[,-c(1,19)]

library(extrafont)
loadfonts(device = "win")

#Gráfico Distribución días

library(ggplot2)
ggplot(data=datosf, aes(x=dias.resol)) + 
  geom_histogram(bins  = 50,color='#004958',fill='#A1DFFF',binwidth = 10,
                 alpha=0.2,size=1.1)+
  xlab('Días de resolución')+
  ylab('conteo')+
  theme( panel.grid = element_blank(),
         axis.title = element_text(             #axis titles
           family = "Verdana",            #font family
           size = 14),               #font size
         
         axis.text = element_text(              #axis text
           family = 'Georgia',            #axis famuly
           size = 14))
datos100<-datosf[-which(datosf$dias.resol>100),]
library(ggplot2)
ggplot(data=datos100, aes(x=dias.resol)) + 
  geom_histogram(bins  = 20,color='#004958',fill='#A1DFFF',binwidth = 10,
                 alpha=0.2,size=1.1)+
  xlab('Días de resolución')+
  ylab('conteo')+
  theme( panel.grid = element_blank(),
         axis.title = element_text(             #axis titles
           family = "Verdana",            #font family
           size = 14),               #font size
         
         axis.text = element_text(              #axis text
           family = 'Georgia',            #axis famuly
           size = 14))
library(ggpubr)
library(scales)
Porcurso <- data.frame(round(prop.table(table(datos100$curso)),3))
names(Porcurso) <- c("curso","porcentaje")


tabla1<-ggtexttable(Porcurso,rows = NULL)

graf1<-ggplot(Porcurso, aes(x="", y=porcentaje, fill=curso)) +
  geom_bar(stat="identity", width=1) +
  coord_polar("y", start=0)+
  geom_text(aes( label = percent(porcentaje)), position = position_stack(vjust = 0.5),
            color = "black",size=5)+
  
  theme_void()



Poract <- data.frame(round(prop.table(table(datos100$tipo.actividad)),3))
names(Poract) <- c("tipo.actividad","porcentaje")


tabla2<-ggtexttable(Poract,rows = NULL)


graf2<-ggplot2:: ggplot(Poract, aes(x="", y=porcentaje, fill=tipo.actividad)) +
  geom_bar(stat="identity", width=1) +
  coord_polar("y", start=0)+
  geom_text(aes( label = percent(porcentaje),x=1.35), position = position_stack(vjust = 0.5),
            color = "black",size=3)+
  
  theme_void()


Porcent<- as.data.frame(round(prop.table(table(datos100$centro)),3))
names(Porcent) <- c("centro","porcentaje")


tabla3<-ggtexttable(Porcent,rows = NULL)


graf3<-ggplot(Porcent, aes(x="", y=porcentaje, fill=centro)) +
  geom_bar(stat="identity", width=1) +
  coord_polar("y", start=0)+
  geom_text(aes( label = percent(porcentaje),x=1.2), position = position_stack(vjust = 0.5),
            color = "black",size=4)+
  
  theme_void()

library(ggplot2)
library(grid)
library(gridExtra)
grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 4)))
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
print(graf1, vp = vplayout(2, 1))  # key is to define vplayout
print(graf2, vp = vplayout(1, 1:2))
print(graf3, vp = vplayout(2, 3))
print(tabla1, vp = vplayout(2, 2))  # key is to define vplayout
print(tabla2, vp = vplayout(1, 3))
print(tabla3, vp = vplayout(2, 4))
library(ggridges)

ggplot(datos100, aes( x=dias.resol,y=curso,  fill=curso)) +
  geom_density_ridges(alpha=0.6, stat="binline", bins=20,
                      scale = 1) +
  theme_ridges()+
  geom_vline(xintercept = 30,size=1)+
  annotate('text',x=37,y=0.8,label='1 mes')+
xlab('Días resolución')+
  ylab('Conteo')
library(dplyr)
library(tidyverse)
actividadm<-datos100 %>% group_by(curso,tipo.actividad) %>% 
  summarise(media=median(dias.resol),c1=quantile(dias.resol,0.25),
            c3=quantile(dias.resol,0.75))


ggplot(actividadm,aes(x=tipo.actividad,y=media,group=curso))+
  geom_point(aes(color=curso),position = position_dodge(0.3),size=2)+
  geom_errorbar(aes(ymin=c1, ymax=c3,color=curso), width=.3,
                position=position_dodge(0.3),size=1)
#estacionalidad volumen

estac<- datos100 %>% group_by(curso,mes.solic) %>% 
  count()

ggplot(estac,aes(x=mes.solic,y=n,group=curso))+
  geom_line(aes(color=curso),size=2)+
  geom_point(aes(fill=curso),size=4,shape=23)+
  ylab('Volumen')
#resolucion

tiemporesol<- datos100 %>% group_by(mes.solic,curso,tipo.actividad) %>% 
  count(dias.resol) %>% mutate(media=median(dias.resol*n))



ggplot(tiemporesol,aes(mes.solic,y=media,group=curso))+
  geom_point(aes(fill=curso),shape=23,size=3)+
  geom_line(aes(color=curso),size=1)+
  facet_wrap(~tipo.actividad)
datostitu=datos100 %>% group_by(tipo.titulacion) %>% 
  summarise(media=median(dias.resol))

ggplot(datostitu,aes(x=tipo.titulacion,y=media))+
  geom_bar(aes(fill=tipo.titulacion),stat = 'identity')
  conteo<- datos100 %>% group_by(tipo.actividad,tipo.titulacion) %>% 
  count()


ggplot(conteo,aes(x=tipo.actividad,y=n,fill=tipo.titulacion))+
  geom_bar(stat = 'identity',position = 'dodge')+
  ylab('Volumen')
volumen<-datos100 %>% group_by(curso,centro,tipo.actividad) %>% count()


ggplot(volumen,aes(x=tipo.actividad,y=n,fill=curso))+
  geom_bar(stat = 'identity',position = 'dodge')+ coord_flip()+
  
  facet_wrap(~centro)
  library(randomForest)
datadef<- ifelse(test = datos100$dias.resol>15,yes = 'DEFECTO',no='NO_DEFECTO')

datadef<- as.factor(datadef)

datos100$datadef=datadef

set.seed(1247)
model1<- randomForest(datadef~tipo.titulacion + 
                       curso + tipo.actividad + mes.solic,data=datos100,
                     importance=TRUE
                     )

model<- randomForest(datadef~tipo.titulacion + 
                       curso + centro +  tipo.actividad + mes.solic,data=datos100,
                     importance=TRUE
                     )
                     
set.seed(1247)

model<- randomForest(datadef~tipo.titulacion + 
                       curso + centro +  tipo.actividad + mes.solic,data=datos100,
                     importance=TRUE, ntree=1000
                     )
ooberror<-data.frame(Trees=1:nrow(model$err.rate),
                     OOB=model$err.rate[,'OOB'],
                     DEFECTO=model$err.rate[,'DEFECTO'],
                     NODEFECTO=model$err.rate[,'NO_DEFECTO'])
datosplot<- gather(ooberror,value = 'ERROR',key = 'fuentes',2:4)
ggplot(datosplot,aes(x=Trees,y=ERROR,group=fuentes))+
  geom_line(aes(color=fuentes),size=1)
  set.seed(1247)
ooberror=vector(length = 5)

for(i in 1:5){
  
  modelotemp<- randomForest(datadef~tipo.titulacion  + curso + tipo.actividad + mes.solic + centro 
                            ,data=datos100,mtry=i)
  ooberror[i]= modelotemp$err.rate[500,'OOB']
}

ooberror<-as.data.frame(ooberror)
ggplot(ooberror, aes(x=1:5, y=ooberror)) + geom_bar(stat = 'identity')+
  ylab('ERROR')+xlab('mtry')
  set.seed(1200)
  modelo2<- randomForest(datadef~tipo.titulacion + tipo.titulacion:curso + 
             curso + curso:centro + curso:tipo.actividad +
             curso:mes.solic + centro + centro:tipo.actividad + 
             centro:mes.solic + centro:mes.solic:curso + 
             tipo.actividad + mes.solic  ,data=datos100)
             
barplot(model$importance[,3], xlab = 'Variables', ylab = 'Media Disminución Precisión %')