Split-Apply-Combine- Parte 3

A terminar el ejemplo!!!

En la entrada Split-Apply-Combine parte 2, mostré unos ejemplos sencillos de como usar librerías de R y de Python para manipular los datos y obtener otro tipo de relaciones tales que de manera directa explorando la información sin transformarla no pueden ser detectadas dichas relaciones.

La relevancia de transformar la información y encontrar nuevas relaciones es parte fundamental para un buen análisis, esto requiere un poco de ensayo y error, de jugar con los datos y el tipo de datos, de buscar conexiones para tratar de descubrir alguna relación. Pero lo descubierto no implica que las “relaciones”  determinan algo relevantes, lo que en teoría uno debe de hacer es validar estos supuesto descubrimientos por medio de alguna técnicas estadística  para probar que la hipótesis o descubrimiento son por lo menos estadísticamente ciertos.

Lo que he compartido en las dos entradas previas(Parte 1 y Parte 2) es en general se puede considerar solo como exploración y diagnóstico, la idea de Split-Apply-Combine( SAC) es dividir , aplicar y combinar los resultados y por medio de esto obtener información que relevante.

El ejemplo en la referencia [1] da una idea de la metodología de manera fácil, por lo cual replico uno de los ejemplo para incentivar a revisar la referencia y observar como se aplica la metodología. El código que uso no es el mismo que comparten en dicho artículo, pero se obtienen los mismo resultados.

Las etapas son las siguientes:

  1. Se cargan los datos y se analiza la muestra de datos para un solo jugador( Split).
  2. Se construye un modelo el cual se aplica a cada uno de los jugadores(Apply).
  3. Se eligen dos elementos obtenidos del modelo para analizar el comportamiento de los datos en general y se muestran algunas gráficas para expresar los resultados (Combine).

Los datos corresponden a la base de datos baseball que se puede obtener desde la librería plyr.

#Split
#Se cargan las librerías
library(dplyr)
library(reshape)
library(plyr)
library(ggplot2)

#Se cargan los datos y se revisan que no cuente con NA
data("baseball")
head("baseball")
sum(is.na("baseball"))

#Se elige solo los datos correspondiente a baberuth

baberuth=filter(baseball,id=="ruthba01")
head(baberuth)
dim(baberuth)
#str(baberuth) se puede usar este comando para explorar que tipo de variables se tienen en los datos.

#Se agregan un par de variables para analizar lso años jugando y el número de carreras por bateos y el número de ocasiones que pasó a batear.

L=mutate(baberuth,cyear=year-min(year)+1,indice=rbi/ab)

#Se analiza la relación entre las dos nuevas variables

ggplot(L,aes(x=cyear,y=indice))+geom_line(col="red")+ggtitle('Relación entre datos')+
 xlab('Índice')+ylab('Ciclo de años')+geom_smooth(method='lm')

SAC_baseball

El código anterior lo que hace es elegir una muestra de datos y analizar la relación entre un par de variables que se construyen de las variables existentes, para ver la información de la descripción de la base se puede consultar por medio del comando ?—nombre de la base—.

La gráfica muestra una relación  y se hace un ajuste lineal como modelo para estudiar dicha relación. Esto no necesariamente es buen buen modelo, pero como ejemplo resulta fácil de entender y de aplicar.

#Apply
#Se construye el modelo 

model<-function(df){lm(rbi/ab~cyear,data=df)}

#Seleccionan los datos que permitan aplicar el modelo
L2=filter(baseball,ab>25)
#Se agregan las variables para toda la base

L3=mutate(L2,cyear=year-min(year)+1,indice=rbi/ab)
#Se aplica el modelo 
bmodels<-dlply(L3,.(id),model)
#Se observan el data.frame obtenido después de aplicar el modelo
head(bmodels)

Lo que se hace en el código anterior es lo siguiente, ya que se definen las nuevas variables y se analiza su relación se procede agregar esas variables para toda la base y se define una función que es el modelo aplicar a todos los datos, posteriormente se elige de la base aquellos elementos que permiten que el modelo se aplique. Si se aplica el modelo a toda la base sin revisar los valores de la variable ab, se obtendrá un error, esto debido al cálculo del modelo sobre los datos.

Se aplica el modelo por medio de la función dlplyr, el “dl” del comando indica que manda un data.frame para obtener una lista.Por último observo como son los datos por medio de head().

La parte de combinar los resultados implica usar algún estadístico para analizar los modelos, en la primera gráfica se observa que no es un buen modelo, entonces haciendo los ajustes lineales se puede extraer la r cuadrada que indica que tan bueno es el modelo de manera descriptiva. Ya que si la r cuadrada es muy alta indicaría que la relación es muy lineal y que el modelo es hasta cierto punto bueno, pero si es baja indica que no lo es.

Por lo tanto lo que hago, siguiendo al artículo o referencia [1], es extraer tanto la  r cuadrada de cada modelo, como la intercepto de la recta. Sabiendo un poco de geometría, se debe de tener en cuanta que la pendiente de la recta está relacionada con la correlación de las dos variables analizadas y por lo tanto con el valor de la r cuadrada.

#Combine

# Se define la función para extraer la R cuadrada de los modelos

rsq<-function(x)summary(x)$r.squared
#Se aplica el modelo a la lista y se regresa un data.frame
bcoef<-ldply(bmodels,function(x)c(coef(x),rcuadrada=rsq(x)))
head(bcoef)
#Se cambian nombres a las columnas
names(bcoef)[2:3]<-c("intercept","slope")
head(bcoef)

#Se revisa el histograma de los datos
ggplot(bcoef,aes(rcuadrada))+geom_histogram(col="red")+ggtitle('Histograma de R cuadradas')+
 xlab('Valores de R cuadrada')+ylab('Conteo')

#Se asigna el valor de las variables a toda la base
baseballcoef<-merge(baseball,bcoef,by="id")
head(baseballcoef)
dim(baseballcoef)

#Grafica 1
d1=qplot(data=baseballcoef,x=slope,y=intercept)+xlim(-0.05,0.05)+ylim(-2,2)
d1+geom_point(alpha=0.05,aes(size=rcuadrada))+ggtitle('Cruce de pendiente vs interceptos')+
 xlab('Pendientes')+ylab('Interceptos')+geom_hline(yintercept=0,col="red")+geom_vline(xintercept=0,col="red")

#Gráfica 2
d2=qplot(data=baseballcoef,x=slope,y=intercept)+xlim(-0.010,0.010)+ylim(-.25,.25)
d2+geom_point(alpha=0.05,aes(size=rcuadrada))+ggtitle('Cruce de pendiente vs interceptos')+
 xlab('Pendientes')+ylab('Interceptos')+geom_hline(yintercept=0,col="red")+geom_vline(xintercept=0,col="red")

Lo que hace todo el código anterior es extraer un estadístico que permita saber si es bueno el modelo, analizar gráficamente los datos. Por último analizar la posible relación gráfica entre los estadístico extraídos. Las gráficas son las siguientes:

Hist_baseball

SAC3SAC3_2

Entonces en resumen lo que se hizo, aun que fue replicar los cálculos del artículo trato de agregar algún modo distinto de obtenerlo con el código. La técnica al final es clara con este ejemplo, cada paso puede requerir más detalle y exploración sobre la información, pero en resumen la primera de estas últimas gráficas muestra que el modelo es malo, lo cual reta a buscar un mejor modelo para analizar los datos y otros estadísticos.

Ejemplo 2

Retomando los ejemplos de los datos de vuelos desde tres aeropuertos cercanos a la ciudad de NY, tenemos que que la base de datos es considerablemente más grande que el ejemplo anterior. Lo que trato de evitar con el ejemplo es hacer confuso como aplicar la técncias de SAC, así que lo que hago en un primer momento es algo similar al ejemplo anterior.

Tomo los datos, reviso el indicador “tailnum” para elegir los aviones que tienen el mismo número en la cola del avión y después analizo un par de variables que no son tan relevantes pero que permiten hacer un modelo sobre los tiempos de retraso al despegar y al arribar. Lo único que cambia es que la regresión o el modelo lineal es múltiple y es un buen modelo, lo cual no es de sorprender ya que si un avión se retrasa al despegar se espera que también se retrase al arribar.

Las etapas son las siguientes:

  1. Se cargan los datos y se analiza la muestra de datos para un solo registro “tailnum”, de hecho elijo el que aparece más ocasiones en los datos (Split).
  2. Se construye un modelo el cual se aplica a cada uno de los registros (Apply).
  3. Se eligen dos elementos obtenidos del modelo para analizar el comportamiento de los datos en general y se muestran algunas gráficas para expresar los resultados (Combine).
#Split

library(nycflights13)
library(plyr)
library(dplyr)
library(reshape2)
library(ggplot2)

data("flights")
dim(flights)
#[1] 336776 16

#missing value
sum(is.na(flights))
#[1] 60593
flights2=na.omit(flights)

head(flights2,10)
str(flights2)
dim(flights2)
#[1] 327346 16

#Seleccionamos el que tiene mayor número de apariciones

sort(summary(factor(flights2$tailnum)),decreasing = TRUE)

N725MQ=filter(flights2,tailnum=="N725MQ")

head(N725MQ)
dim(N725MQ)

#Agrego dos variables
N725MQ_2=mutate(N725MQ,dep_d=dep_delay-min(dep_delay)+1,arr_d=arr_delay-min(arr_delay)+1)

#Gráfica de cruce entre variables por mes
ggplot(data=N725MQ_2,aes(y=dep_d, x=arr_d))+geom_point(aes(colour=factor(month)))+geom_smooth(method='lm')

La gráfica que se obtiene es la siguiente:

fligths3_cruce

La gráfica muestra que existe una relación linea entre las dos variables que se definieron, pero esto además agrego una revisión de como se comportan los datos con respecto a los meses de vuelo y si bien no es claro una relación muestra que el comportamiento no es del todo aleatorio, en ciertos meses se presenta un retraso mayor que en otros. Un modo de ver esto es correr la regresión lineal solo para los datos de este registro de tailnum y revisar si es significativa, en este caso R cuadrada marca un 0.8 lo cual es bastante bueno.

Entonces lo que ahora hago es aplicar este modelo a los datos de toda la tabla.

#Apply
#Definición del modelo
modelo<-function(df){lm(dep_d~arr_d+month,data=df)}

#Procesamiento de toda la base
flight3=mutate(flights2,dep_d=dep_delay-min(dep_delay)+1,arr_d=arr_delay-min(arr_delay)+1)

#Aplicación del modelo
fmodelo<-dlply(flight3,.(tailnum),modelo)
head(fmodelo)
str(fmodelo)

#Extracción del estadístico
rsq<-function(x){summary(x)$r.squared}

fcoef<-ldply(fmodelo,function(x)c(coef(x),rcuadrada=rsq(x)))

#Extracción de datos del modelo
fcoef=ldply(fmodelo,function(x)c(coef(x),rcuadrada=rsq(x)))
head(fcoef)
names(fcoef)[2]=c("Intercepto")

#Histograma del modelo
ggplot(fcoef,aes(rcuadrada))+geom_histogram(col="red")+ggtitle('Histograma de R cuadradas')+
 xlab('Valores de R cuadrada')+ylab('Conteo')

La gráfica que se obtiene es la siguiente:

flights_hist

El histograma muestra que la regresión es un buen modelo para analizar todos los datos, y que se comporta bien la r cuadrada, es decir que es próxima a 1, lo único extraño es el conjunto de valores que tienen un valor pequeño  o cercano a cero, esto indica que debe de revisarse el para algunos registros. Esto de puede hacer de modo fácil usando la función filter().

 Lo último es combinar la información con la tabla original y analizar las relaciones entre los valores estimados del modelo. Estos datos permiten hacer otra exploración de la información y de cierto modo los datos fueron enriquecidos.

#Combine
#Unión de las dos tablas
flightscoef<-merge(flights2,fcoef,by="tailnum")
head(flightscoef)
dim(flightscoef)
str(flightscoef)

#Gráficas de resultados por aeropuerto de origen
ggplot(flightscoef,aes(rcuadrada))+geom_histogram(color="black",aes(fill=origin))+ggtitle('Histograma de R cuadradas')+
 xlab('Valores de R cuadrada')+ylab('Conteo')

#Gráficas de coeficientes

d1=qplot(data=flightscoef,x=month.y,y=Intercepto)
d1+geom_point(alpha=0.05,aes(size=rcuadrada))+ggtitle('Cruce de pendiente vs interceptos')+
 xlab('Pendientes de Meses')+ylab('Interceptos')+geom_hline(yintercept=0,col="red")+geom_vline(xintercept=0,col="red")

d2=qplot(data=flightscoef,x=arr_d,y=Intercepto)
d2+geom_point(alpha=0.05,aes(size=rcuadrada))+ggtitle('Cruce de pendiente vs interceptos')+
 xlab('Pendientes de Meses')+ylab('Interceptos')+geom_hline(yintercept=0,col="red")+geom_vline(xintercept=0,col="red")

Las gráficas que se obtienen son las siguientes:

flights_hist_origin

flights1_082015Flights2_082015

Lo que se observa es que efectivamente el comportamiento del modelo por aeropuerto de origen es similar, lo cual se ve en el histograma con diferentes colores por origen. Las otras dos gráficas muestran la primera la relación entre los coeficientes de la regresión, entre los cuales se aprecia cuales tienen cierto comportamiento, para profundizar en el análisis de estos coeficientes se requiere un poco más de detalle, pero en general la idea de aplicar SAC es también reflejada en este ejemplo.

Ultimo ejemplo.

En la entrada Split-Apply-Combine Parte 2 mostré que los datos podían ser analizados como series de tiempo después de una serie de transformación sobre la información, lo que trato de hacer con el ejemplo es aplicar un modelo de series de tiempo a los datos por medio de la metodología SAC.

Lo primero que hago es procesar la información y explorar solo los datos como series de tiempo de un solo registro de “tailnum”, esto se muestra gráficamente que tienen cierto comportamiento, el registro elegido es el que cuenta con mayor cantidad de datos o líneas en la base, el cual es “N725MQ”.

El código para explorar la información y los datos es el siguiente:

#Ejemplo de Series de tiempo
#Cargo las librerías requeridas
library(nycflights13)
library(plyr)
library(dplyr)
library(reshape2)
library(ggplot2)

#Revisión de los datos

data("flights")
dim(flights)
#[1] 336776 16

#missing value
sum(is.na(flights))
#[1] 60593
flights2=na.omit(flights)

head(flights2,10)
str(flights2)
dim(flights2)
#[1] 327346 16

#Seleccionamos el que tiene mayor número de apariciones
sort(summary(factor(flights2$tailnum,labels=levels)),decreasing = TRUE)

#Elijo el registro con mayor cantidad de líneas es "N725MQ"

N725MQ=filter(flights2,tailnum=="N725MQ")
head(N725MQ)
dim(N725MQ)

N725MQ_1=mutate(N725MQ,fechas=as.Date(paste(month,day,year,sep="/"),"%m/%d/%Y"))

Dat_1<-N725MQ_1%>%
 group_by(fechas,tailnum)%>%
 summarise(dis_m=mean(distance))

head(Dat_1)
qplot(data=Dat_1,x=fechas,y=dis_m,geom=c("line","point"))+geom_point(colour="red")+scale_x_date()+ggtitle('Seríe de N725MQ')

La gráfica que se obtiene es la siguiente:

SAC_timeseries

Estos datos se pueden tratar como una serie de tiempo, burdamente elijo un modelo para esta seria, el método que elijo es  ets (exponential smoothing), la intención es aplicar el modelo y tomar el estadístico RMSE, el cual la raíz de la media de los errores cuadrados y a grandes rasgos representa la desviación estándar entre los valores predichos y los estimados.

El código para aplicar el modelo a esta serie y obtener el RMSE, es el siguiente:

#Código
#Modelo
TS=ts(Dat_1$dis_m)
S=ets(TS)
S1=summary(S)
#El valor de RMSE es el siguente
S1[2]
201.2376
plot(TS, main="Serie de N725MQ para el año 2013",col="blue", ylab="Valores de la serie", xlab="Tiempo")
lines(S$fitted,col="2")

SerieN725MQ_1

Ahora el trabajo es elegir un cierto grupo de valores de tailnum que permitan aplicar el modelo y explorar como se comporta el estadístico y ver el comportamiento de los parámetros alpha y beta del modelo para cada un de los registros elegidos. Entonces se requiere que se transformen los datos, que se seleccionen cierto grupo de datos, se aplica un modelo y se extrae un estadístico para revisar el comportamiento del modelo.

#Transformando los datos
Datos1<-Vuelos1%>%
 group_by(fechas,tailnum)%>%
 summarise(dis_m=mean(distance))

Dato2=melt(Datos1,id=c("fechas","tailnum"))

Datos3=dcast(Datos1,fechas~tailnum)

Datos3[is.na(Datos3)] <- 0

#Eligiendo los tailnum con mayor actividad
Lista<-flights2%>%
 group_by(tailnum)%>%
 summarise(cantidad=n())

Aviones=filter(Lista,cantidad>365)

Lista_av=Aviones$tailnum

Lista_av2=c('fechas',Lista_av)

#Selección
Datos4=subset(Datos3,select=Lista_av2)

#Gráficas del comportamiento de algunos registros
ggplot(Datos4,aes(x=fechas,y=N324JB))+geom_line()
ggplot(Datos4,aes(x=fechas,y=N713MQ))+geom_line()
ggplot(Datos4,aes(x=fechas,y=N722MQ))+geom_line()

Como ejemplo gráfico algunos de los registros para ver su comportamiento en el año.

N324JBN713MQN722MQSe aprecia en los gráficos que el comportamiento es diferente y que es de esperar que el modelo no sea el mejor para todos los registros.

#Todos los datos
Datos5=melt(Datos4,id="fechas")
head(Datos5)
ggplot(Datos5,aes(x=fechas,y=value,color=variable,group=variable))+geom_line()+
 ggtitle('Todos los datos')+
 ylab('Media de las distancia de vuelos')+xlab('Fechas')

El comportamiento de todos los registros es el siguiente:

Todos_tailnum

Se hace notar que el comportamiento de separa en dos bloques, por lo cual es posible que para los vuelos que muestran mayor actividad el modelo sea más adecuado.

#Más transformaciones

#Ultima transformación
Datos6=dcast(Datos5,variable~fechas) 
head(Datos6)
colnames(Datos6)[1]<-'tailnum'
str(Datos6)

#Modelo
modelo<-function(df){ets(as.numeric(df))}

#Aplicación del modelo
Amodels<-dlply(Datos6,.(tailnum),modelo)

#Estadistico
rmse<-function(x){sqrt(x$mse)}

#Extracción del estadistico
Acoef<-ldply(Amodels,function(x)rmse(x))



ggplot(Acoef,aes(x=tailnum,y=V1))+geom_boxplot(col="red")+
 ggtitle("RMSE")+theme(plot.title=element_text(lineheight = 1,face='bold'))+ylab("RMSE")+xlab("TailNum")

Estas última parte del código hace el cierre de la metodología SAC, ya teniendo los datos preparados para aplicar el modelo se define la función que se va aplicar, se extrae el estadístico y se revisan los resultados con algún gráfico para tener identificado como se comportó el modelo.

Considerando que RMSE indica la desviación estándar de los errores del modelo, lo que se desea es que no sea muy grande ya que indica que los errores no están tan centrados. Esto es burdo pero es un modo sencillo de interpretar el gráfico que analizar como se comporta el modelo, el gráfico es el siguiente:

RMSE_ejemploRevisando en los datos, se observa que por ejemplo el vuelo N335AA tiene un comportamiento no muy favorable para el modelo,  haciendo la gráfica del comportamiento en el año se observa lo siguiente:

N335AAPara el cual se ve complicado que el modelo funcione bien, ya que siendo de suavizado exponencial no se favorable su aplicación.

 Espero que la entrada ilustre de manera general como se hace uso de la metodología SAC, si bien fueron tres entradas para explicar su aplicación la intensión es tener varios ejemplos tanto de gráficas exploratorias , como de gráficas no triviales y la aplicación de algún modelo sencillo que permita visualizar la metodología de manera clara.

Referencias:

1.-http://www.jstatsoft.org/v40/i01/paper

 

 

Split-Apply-Combine-Parte 1

Sobre Split-Apply-Combine

La idea de esta entrada es aplicar la visión de dividir-aplicar-combinar (Split-Apply-Combine), en las referencias [1,2] se explican más detalles sobre el modelo o metodología guía, la cual fue inspirada por Map-Reduce. Esta última es una metodología apropiada cuando se tienen un cluster de computadoras, en este caso la versión Split-Apply-Combina se centra en el caso del análisis de datos mediante una sola máquina para diseñar una buena estrategia de análisis.

En la entrada desarrollo el ejemplo mediante R project y Python. La intención es mostrar como los dos lenguajes cuentan con las herramientas para realizar el mismo tipo de análisis. Trato de hacer un ejemplo amplio, por lo cual elegí una base más o menos grande de R project, la cual si bien no tiene muchas variables ( solo 16) permite hacer varias cosas con ellas. Por supuesto que el análisis puede tener otro tipo de enfoque, lo único que hago es usar esa base para emplear varias herramientas de R y de Python, las cuales en general uno las consulta de mera independiente.

Divido esta entrada en 3 partes.

Parte 1.-Exploración directa de los datos.

Parte 2.-Procesando los datos y analizando otras posibles relaciones.

Parte 3.-Definiendo técnicas para aplicar y concentrar esto para realizar un proceso Split-Apply-Combine.

Decidí dividir en tres partes para compartir algunos ejemplos sencillos sobre como hacer algunos gráficos y también un poco de análisis. Todos los gráficos usados entran perfectamente en los temas de exploración de datos o análisis exploratorio, en ocasiones solo uso algunas funciones para mostrar como funcionan y el valor que agrega a la exploración puede ser pobre y sin relevancia mayor.

En la referencia [3] se pueden ver detalles sobre las gráficas en R y en la referencia [4] sobre las gráficas en Python.

Parte 1.-Exploración directa de los datos.

En el código considero instaladas las librerías requeridas, en caso de contar con ellas  en caso de usar RStudio se puede hacer uso de Tools para instalar paquetes, lo cual es rápido y fácil.

Los datos corresponden a los tiempos de vuelo del año 2013, contienen información de los vuelos de 3 aeropuertos JFK, LGA y EWR.

#Código para cargar las librerías y datos
library(nycflights13)
library(ggplot2)
library(grid)
library(GGally)
library(gridExtra)#Funciones para la librería Grid

Los datos en general primero son explorados para conocer cuantos datos se tienen como missing values y las dimensiones de estos datos, es decir; cuantas columnas y cuantas filas tiene el archivo.

#Código para revisar las propiedades básicas de los datos
data(flights)
#Para conocer información de los datos 
#?flights

dim(flights)
#[1] 336776     16

#missing value
sum(is.na(flights))
#[1] 60593
flights2=na.omit(flights)
dim(flights2)
#[1] 327346     16

#tipo de datos
str(flights2)
#Aspectos básicos de los datos
head(flights2)
#También se puede ver la parte final de la tabla de datos
#tail(flights2)

#Para revisar el resumen estadístico de los datos
#Se usa el siguiente comando

summary(flights2)

Debido a que se tienen varias variables que son categóricas, se puede revisar su comportamiento considerándolas como “factores”. Un modo de revisar como se comportan ese tipo de datos se puede hacer con el siguiente comando.

#Explorando unas variables categóricas

sort(summary(factor(flights2$dest)),decreasing = TRUE)
sort(summary(factor(flights2$origin)),decreasing = TRUE)
#EWR    JFK     LGA
#117127 109079  101140

Cuando se revisan los datos de manera numérica uno no aprecia mucho la relevancia del comportamiento de los datos. Entonces siempre se buscan algunas gráficas sencillas que representen la información de manera clara y fácil.

Todas las gráficas siguientes pueden ser mejoradas, al final depende del interés de cada persona para definir el tipo de gráfica que se hace.

#Primeras Gráficas

p1=ggplot(data=flights2,aes(x=factor(origin),y=month))+geom_boxplot(col="black",alpha=0.7,aes(fill=factor(origin)))+
xlab('Origen de los Vuelos')+ylab('Mes de vuelo')+ggtitle('Comportamiento de los lugares de salida por mes')+theme(plot.title=element_text(lineheight = 2,face='bold'))

p2=ggplot(data=flights2,aes(x=factor(month)))+geom_bar(col="black",aes(fill=factor(origin)),position="dodge")+
  xlab('Mes de Vuelos')+ylab('Número de vuelos')+ggtitle('Comportamiento de vuelos por mes con respecto al aeropuerto de salida')+theme(plot.title=element_text(lineheight = 2,face='bold'))

grid.arrange(p1,p2,ncol=2,main="Gráficas del comportamiento de vuelos por mes")

La gráfica que se obtiene es la siguiente:Gráficas_vuelos_por_mesLa intensión es revisar la relación que existe entre la cantidad de vuelos por mes respecto al aeropuerto de salida u origen. La primera gráfica muestra como los 3 aeropuertos tienen un mes en el cual es la media de vuelos y por otro lado el JFK muestra que cubre un mayor número de meses con actividad. En las barras se observa que en EWR se realiza un mayor número de vuelos, además muestra las altas en vuelos en primavera y verano.

##Comportamiento de los tiempos de retraso de despegue y de arrive

q1=ggplot(data=flights2,aes(x=dep_time))+geom_histogram(color="black",aes(fill=factor(origin)))+
  xlab('Hora de despegue')+ylab('Número de Vuelos')+ggtitle('Hora de Despegue por aeropuerto de salida')+theme(plot.title=element_text(lineheight = 2,face='bold'))

q2=ggplot(data=flights2,aes(x=arr_time))+geom_histogram(colour="black",aes(fill=factor(origin)))+
  xlab('Hora de arrivo')+ylab('Número de vuelos')+ggtitle('Hora de Arrivo por aeropuerto de salida')+theme(plot.title=element_text(lineheight = 2,face='bold'))

grid.arrange(q1,q2,nrow=1,ncol=2,main="Gráficas del comportamiento de vuelos por mes")

El gráfico que se obtiene es:

Despegue_y_arrivo_por_aeropuertoSe muestran los histogramas de la cantidad de vuelos por hora de despegue y arribo divididos por el aeropuerto de origen. Se aprecian las horas en las cuales se realizan el mayor número de vuelos , los cuales son sumamente notorios. Por otro lado se aprecia la hora de llegada, las cuales muestran cuatro horas pico o el horario con mayor número de arribos.

En la base de datos se tienen registrados los minutos de retraso en despegues y en arribos a su destino, una idea sencilla es graficar el cruce de ambos datos y explorarlos.

#Gráfica de retrasos en despeque y arrivo
ggplot(data=flights2,aes(x=dep_delay,y=arr_delay))+geom_point(aes(colour=factor(origin)))+
  geom_smooth(method='lm',colour='black')+
  xlab('Retraso en Despegar')+ylab('Retraso en arrivo')+ggtitle('Relación entre retraso de despegue y arrivo por aeropuerto')+theme(plot.title=element_text(lineheight = 2,face='bold'))

La gráfica que se genera es la siguiente:

Relación_despegue_vs_arrivoAgregué la línea de tendencias entre los datos calculada con la regresión lineal entre los datos. Esta recta muestra una buena descripción gráficamente o eso aparenta, pero con un poco de cuidado se ve que los mejores datos descritos son aquellos que tienen mayor tiempo de retraso, mientras que la mayoría de datos se muestran como una mancha en los primeros 500 minutos.

Lo que se me ocurrió revisar el comportamiento de la distribución de los datos, haciendo uso de un histograma, después de observar que solo se apreciaba unas cuantas barras estimadas probé transformar los dato y calcular su logaritmo y luego volver a revisar su histograma. Lo que se observa de los datos transformados es la siguiente gráfica:

Hist_Dist_despegue_y_arrivosLa cual se puede obtener con el siguiente código:

#Tranformación de los datos e Histograma
l1=ggplot(data=flights2,aes(x=log(87+arr_delay)))+geom_histogram(color="black",aes(fill=factor(origin)))+
xlab('Log(87+arr_delay)')+ylab('Conteo')+ggtitle('Histograma de los retrasos en tiempo de arrivo')+theme(plot.title=element_text(lineheight = 2,face='bold'))

l2=ggplot(data=flights2,aes(x=log(43+dep_delay)))+geom_histogram(color="black",aes(fill=factor(origin)))+
  xlab('Log(43+dep_delay)')+ylab('Conteo')+ggtitle('Histograma de los retrasos de tiempo de despegues')+theme(plot.title=element_text(lineheight = 2,face='bold'))

grid.arrange(l1,l2,nrow=1,ncol=2,main="Gráficas del comportamiento de vuelos por mes")

Se observa entonces que los datos muestran comportamientos de distribuciones de cols pesadas, lo cual requiere una revisión más meticulosa. Haciendo el gráfico de cruce entre los dos tiempos de retrasos, pero considerando sus valores transformados se obtienen el siguiente gráfico log-log.

Log_vs_log_distancia-arrivosEl otro par de variables que sugiere que tienen alguna relación son la distancia del vuelo y el tiempo de vuelo. La gráfica se puede realizar con el siguiente código:

ggplot(data=flights2,aes(x=air_time,y=distance))+geom_point(aes(colour=factor(origin)))+
  xlab('Tiempo de vuelo')+ylab('Distancia recorrida')+ggtitle('Cruce entre la distancia y el tiempo de vuelo')+theme(plot.title=element_text(lineheight = 2,face='bold'))

La gráfica que se obtiene es la siguiente:

Cruce_Dis_vs_Tiempo_vueloLa gráfica a primera vista sugiere una relación lineal, pero también se puede explorar si no resulta favorable considerar el log-log de los datos, esto por aquellos datos que muestran desconexión en la parte superior de la gráfica.

ggplot(data=flights2,aes(x=log(distance),y=log(air_time)))+geom_point(aes(colour=factor(origin)))+
  xlab('Logaritmo del Tiempo de vuelo')+ylab('Logaritmo de la Distancia recorrida')+ggtitle('Cruce entre la distancia y el tiempo de vuelo')+theme(plot.title=element_text(lineheight = 2,face='bold'))

La gráfica que se obtiene es la siguiente:

Cruce_Log_de_Dist_vs_Timepo_VueloTambién muestra una relación lineal pero es posible que revisando un ajuste lineal entre los datos sea favorable considerar el log-log para cubrir una mayor  población de los datos.

Los siguientes gráficos solo son ejemplos para explorar la información en busca de alguna posible relación entre los datos.

JitterHoras_de_despegueEl código de los dos gráficos es el siguiente:

ggplot(data=flights2,aes(x=factor(carrier),y=factor(origin)))+geom_jitter()
#ggplot(data=flights2,aes(x=hour))+geom_bar(aes(colour=origin,fill=origin),position = "dodge")+coord_flip()

ggplot(data=flights2,aes(x=hour))+geom_bar(color="black",aes(fill=origin))+
  xlab('hora del día')+ylab('Conteo')+ggtitle('Histograma de las horas de despegue o salida')

Estos dos gráficos, sobre todo el primero; son informativos por ejemplo las barras negras muestran por aeropuerto qué aerolíneas tienen actividad. Es inmediatamente es claro cuales aerolíneas tienen presencia y la diferencia entre la actividad por aeropuerto.

La segunda gráfica muestra 3 momentos donde no se registran vuelos, lo cual resulta raro pero los datos de muestran esto, lo cual implica a ser explorado con mayor detalle.

Un gráfico que suele hacerse entre varias variables o entre una matriz de datos es el scatter plot, para explorar la posible relación entre cada par de variables. Para realizarlo en R con ggplot, se ejecuta el siguiente código:

#Scatter Plot
ggpairs(data=flights2[,c("dep_delay","arr_delay","air_time","distance")],columns=1:4,title="Datos")

La gráfica que se obtiene es la siguiente:

Scatterplot-flighLa gráfica de arriba solo muestra la relación entre varias variables de los datos no de todas. La información de la gráfica incluye la correlación, la cual muestra que es casi nula entre algunas de las variables y muy próxima a 1 en dos pares de variables. La otra opción es hacer una revisión entre las variables pero no solo usando la correlación, sino la el índice de información Mutua. Este último no solo considera la posible relación lineal, sino también una posible relación no lineal. Para realizar el cálculo en R project se puede usar la librería “entropy”.

Ejemplo en R la información mutua entre arr_delay y distance, es de 0.0406. Lo cual indica que no existe alguna relación que analizar.

Con la breve exploración anterior se puede ir pensando en el tipo de cosas que pueden resultar interesantes o que podían ser modeladas, ejemplo pronosticar conociendo el lugar de despegue la hora y la aerolínea el lugar de arribo, o construir un modelo para clasificar los datos de vuelos por el aeropuerto de origen. Pronosticar la cantidad de vuelos por aeropuerto para el siguiente año, o pronosticar los tiempo de atraso en arribar un vuelo conociendo el origen, el horario, el mes de vuelo y el lugar al cual arribará.

Por su puesto que el tipo de preguntar a responder depende de la persona que está explorando los datos o el negocio que requiere este análisis. Por lo cual en esta entrada solo deseaba mostrar algunos gráficos que se pueden ayudar a explorar los datos, al final la primera parte de realizar algún análisis es explorar aspectos estadísticos básicos y apoyarse de alguna gráfica ayuda a tener alguna visualización de la información.

Análisis similar con Python

 El procesamiento de datos en Python en general es realizado siempre en el módulo pandas, las estructuras de datos para procesar información es similar a R project. Se cuenta con estructuras de datos como Data.Frame y Series, como su nombre lo indica funcionan equivalentemente a las de R.

Se pueden usar en Python algunos de los datos de R project haciendo uso de algún módulo, ejemplo el módulo Stats Models tienen un api para conectarse con los datos de R project, lo cuales se encuentran su lista en Rdatasets.

En el ejemplo lo que hice fue exportar los datos de la librería nycflights13 a un archivo csv y cargarlo en una sección en Pandas de Python.

El código es el siguiente:

#Carga de datos
import pandas as pd
#Cargar datos
flights=pd.read_csv('flights.csv')

Para procesar o explorar la descripción básica de los datos se hacer con los siguientes código.

#Revisión de los datos
#Se ve un resumen de lso datos
flights.head()
flights.shape()

Unas de las cosas estándar al hacer análisis es procesar los missing data, lo cual se puede hacer en python con el siguiente código.

#Revisión
#Un modo burdo de ver que tienen missin values
flights[flights.isnull()!=True].count()
#Nuevo data.frame para los datos sin missin value
flights2=flights.dropna()
flights2[flights2.isnull()!=True].count()

Otro aspecto básico es revisar la descripción de la información de manera general, es decir; revisar aquella variables que son categóricas como origin o dest y también revisar la descripción estadística de variables que pueden ser consideradas continuas, como dep_delay o distance.

#Revisión de algunas variables
#Para conocer la información en general de los datos
flights2.info()
flights2.describe()
#Se exploran algunas de las variables que no son númericas y otras númericas
#Variables continua
flights2['dep_delay'].describe()
flights2['distance'].describe()

#Variables categoricas
flights2['origin'].astype('category').value_counts()
flights2['origin'].astype('category').describe()
flights2['dest'].astype('category').value_counts()
flights2['dest'].astype('category').describe()

Ahora para visar algunas gráficas básicas sobre las variables se usa en general matplotlib, pese a que pandas cuanta con algunas gráficas básicas como histogramas,gráficas de barras y boxplot.

Otra opción de reciente desarrollo es ggplot en python, la cual no cuenta con la misma flexibilidad que en R, pero está en desarrollo. La diferencia entre matplot y ggplot es en esencia que este último fue desarrollado con la perspectiva de “Grammar of Graphics”, mientras que matplot fue pensado en el marco de desarrollo de gráficos  que se usan en matlab. El concepto de ggplot, en recientes años a tomado relevancia ya que se pueden construir gráficos de manera rápida que involucran de fondo una transformación de los datos.

Un ejemplo es el siguiente, el cual se hace con los gráficos de base de pandas.

#Boxplot
df=flights2[['month','origin']]
df.boxplot(by='origin')

Boxplot_por_Mes

El gráfico es similar al que se obtienen en ggplot con R, pero en su defecto en R no se requiere seleccionar los datos y directamente indicar que se agrupen para hacer el gráfico.

Entonces para hacer un análisis gráfico rápido me resulta más fácil pensarlo en R que en Python, pero se obtienen ciertas ventajas en Python que no se obtienen en R.

Otra opción que existe es usar los gráficos de R desde pandas con el módulo rpy2, por medio del cual de cierto modo se usa todo lo que se quiera de R y se utiliza en python.

Un ejemplo de ggplot en Python, usando el módulo para python y no desde R, es el siguiente:

#Gráfica de barras
from ggplot import *


print ggplot(flights2,aes(x='month',fill='factor(origin)'))+\
 geom_bar(color="black")+\
 ggtitle('Barras por cada Mes')+\
 theme(plot_title=element_text(lineheight=2))+\
 ylab('Cantidad de Vuelos por Mes')+\
 xlab('Meses')

Barras_por_mes

El gráfico es parecido al primero que mostré en R. El boxplot de los vuelos en matplotlib se puede obtener del siguiente modo:

#Boxplot Maplotlib
import numpy as np
import matplotlib.pyplot as plt

plt.style.use('ggplot')

fig,ax1=plt.subplots()
ax1.boxplot(Mes,showmeans=True)
ax1.set_title('Boxplot de vuelos por mes')
ax1.set_xlabel('Valores')
ax1.set_ylabel('Meses')
plt.show()

Boxplot de los meses

Los histogramas para explorar el comportamiento de los tiempo de retraso y de despegue se obtienen en ggplot para python se obtienen con el siguiente código.

#Histogramas
from ggplot import *
print ggplot(flights2,aes(x='dep_time',fill='origin'))+\
 geom_histogram(color="black")+\
 ggtitle('Histograma de la hora despegue')+\
 theme(plot_title=element_text(lineheight=2))+\
 ylab('Cantidad de vuelos')+\
 xlab('Horario')

print ggplot(flights2,aes(x='arr_time',fill='origin'))+\
 geom_histogram(color="black")+\
 ggtitle('Histograma de la hora de arribo')+\
 theme(plot_title=element_text(lineheight=2))+\
 ylab('Cantidad de vuelos')+\
 xlab('Horario')

Histograma_despegues

Histograma_arribos

La gráfica del cruce entre los retrasos de arribo y despegue se obtienen de manera similar a la que se realiza en ggplot para R, lo único que se hace notar, es que la librería ggplot para python es más lenta al crear una gráfica con muchos datos.

#Scatter plot
from ggplot import *
print ggplot(flights2,aes(x='dep_delay',y='arr_delay'))+\
 geom_point()+\
 geom_smooth(color="red")+\
 ggtitle('Cruce de retrasos')+\
 theme(plot_title=element_text(lineheight=2))+\
 ylab('Retrasos en arribos')+\
 xlab('Retrasos en despegue')

Cruce_de_retrasos

Para hacer una gráfica similar en matplotlib se observa que el código es más elaborado, se obtiene una gráfica similar pero con mayor cantidad de líneas de programación.

#Ejemplo en Matplotlib
import matplotlib.pyplot as plt

for aero in ['LGA', 'JFK', 'EWR']:
 x =flights2.loc[flights2.loc[:,'origin']==aero,'dep_delay']
 y = flights2.loc[flights2.loc[:,'origin']==aero,'arr_delay'] 
 
 if aero=='EWR':
 color='red'
 plt.scatter(np.asarray(x.values)[:len(x)-1],np.asarray(y.values)[:len(y)-1],
 c=color, label=aero)
 
 if aero =='JFK':
 color='blue'
 plt.scatter(np.asarray(x.values)[:len(x)-1],np.asarray(y.values)[:len(y)-1],
 c=color, label=aero)

 if aero=='LGA':
 color='green'
 plt.scatter(np.asarray(x.values)[:len(x)-1],np.asarray(y.values)[:len(y)-1],
 c=color, label=aero) 
 

plt.legend(loc=4)
plt.grid(True)
plt.title('Scatter plot de los retrasos')
plt.ylabel('Retrasos en despegues')
plt.xlabel('Retrasos en arribos')
plt.show()

La gráfica es la siguiente:

Scatter_plot_retrasos

Otra gráfica fácil de hacer es la del comportamiento de los histogramas de los retrasos, tanto de despegues como de arribos.

El código es el siguiente:

#Histogramas
import matplotlib.pyplot as plt

plt.style.use('bmh')

x =flights2['dep_delay']
y = flights2['arr_delay'] 

x1=np.asarray(x.values)[:len(x)-1]
y1=np.asarray(y.values)[:len(y)-1]
plt.hist(x1, histtype="stepfilled",
 bins=20, alpha=0.8, normed=True)
plt.hist(y1, histtype="stepfilled",
 bins=20, alpha=0.8, normed=True)
plt.title('Histogramas de los retrasos de despeque y de llegada')
plt.xlabel('Valores de los retrasos')
plt.ylab('Cantidad de retrasos')
plt.show()

La gráfica es la siguiente:

Histogramas_de_los_retrasos

Para hacer un panel de gráficos como el de la exploración en R, se tiene que diseñar el panel con el número de gráficos que se desea. Esto en el fondo también sucede con R, solo que en matplotlib uno tiene que hacer el código con mayor detalle.

Espero que estas cuantas gráficas hechas en python de una imagen de lo versátil que es, se requiere en general procesar más los datos pero se puede tener mayor control sobre el tipo de gráficos que se pueden hacer.En la referencia [4] se pueden consultar muchas gráficas de muestra.

Referencias

1.-http://static.googleusercontent.com/media/research.google.com/es//archive/mapreduce-osdi04.pdf

2.-http://www.jstatsoft.org/v40/i01/paper

3.-http://ggplot2.org/

4.-http://matplotlib.org/

5.-http://pandas.pydata.org/pandas-docs/stable/groupby.html