Diagramas de Voronoi con spatial de python

En breve "mis cachorros", como llamo a un grupo de los mejores Data Scientist de Europa (de los que tengo que hablar algún día) se van a enfrentar a un problema que probablemente tengan que resolver con análisis geométricos muy complejos. Para despertarles la curiosidad (sé que me leen) hoy traigo al blog una entrada que nos aproxima al método de interpolación geométrica más sencillo, al diagrama de Voronoi. Con spatial de scipy podemos trabajar con estos diagramas:

seed(89)
df = pd.DataFrame(np.random.uniform(1,100,size=(20, 2)), columns=list('XY'))
plt.scatter(df.X, df.Y,marker=".")
show()

voronoi_python1

Estos puntos aleatorios en el espacio de 2 dimensiones pueden generar regiones por interpolación y representarlas con voronoi_2d_plot:

from scipy.spatial import Voronoi, voronoi_plot_2d
vor = Voronoi(df)
voronoi_plot_2d(vor)

voronoi_python2

Ahora si queremos determinar si un punto de espacio está dentro de una de las celdas que delimitan los diagramas de Voronoi que hemos creado podemos hacerlo por vecinos cercanos con la función cKDTree:

from scipy.spatial import cKDTree
vor_kdtree = cKDTree(df)
puntos = [[1,100],[100,1]]
test_point_dist, test_point_regions = vor_kdtree.query(puntos,k=1)
test_point_regions

Y ahora viene lo único interesante de esta entrada, ¿cómo identificamos las regiones? No empleéis .regions, emplead:

pd.concat([df,pd.DataFrame(vor.point_region)],axis=1)

Los puntos (1,100) y (100,1) aparecen en la región que nos identifica la posición 19 y 9... Me ha costado tiempo entenderlo, me hago viejo. Saludos.

Los bancos lo llaman Transformación Digital yo lo llamo me da miedo Facebook

¿Si Facebook prestara dinero? ¿Si Facebook hiciera un banco? Tiene información interesante de nosotros, variables relevantes para cualquier modelo de scoring de crédito y sabe como es tu comunidad de amigos cibernéticos y cuales de ellos podrían avalarte a la hora de conceder un crédito. Además tanto Facebook, como Amazon o Wallapop tienen sus propios medios de pago y Google no tardará en crear su propio banco, unos están creando la economía del futuro y otros no pueden sobrevivir sin programadores en COBOL, entiendo que se quieran transformar aunque a lo mejor no es un tema de captar pasivo y prestar dinero a lo mejor es un tema de relacionar los recursos de las personas.

Preparar nuestros datos para sklearn. Pasar de string a número

Cuando trabajamos con python y sklearn necesitamos que todos los datos que vamos a modelizar sean númericos, si tenemos variables carácter necesitamos previamente transformarlas a números. La forma más rápida para realizar esta tarea es emplear preprocesing de sklearn:

import pandas as pd
dias = {'dia': ['lunes','martes','viernes','miercoles','jueves','martes','miercoles','jueves','lunes']}
dias = pd.DataFrame(dias)
dias

Creamos un data frame a partir de una diccionario que se compone de los días de la semana ahora vamos a codificar las etiquetas con el LabelEncoder de sklearn:

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(dias['dia'])

Podemos listar las clases:

list(le.classes_)

Me gustaría destacar que hay que tener especial cuidado con el orden de las codificaciones porque es un orden léxico-gráfico, no va por orden de aparición:

dias = le.transform(dias['dia'])
dias

Ahora ya estamos en disposición de poder emplear sklearn para entrenar nuestro modelo.

Pasando de SAS a R. Primer y ultimo elemento de un campo agrupado de un data frame

Las personas que están acostumbradas a trabajar con SAS emplean mucho los elementos first, last y by, en el blog hay ejemplos al respecto, en R podemos hacer este trabajo con la librería “estrella” dplyr de un modo relativamente sencillo. A continuación se presenta un ejemplo para entender mejor como funciona, creamos un conjunto de datos aleatorio:

id <- rpois(100,20)
mes <- rpois(100,3)+1
importe <- abs(rnorm(100))*100

df <- data.frame(cbind(id,mes,importe))

Tenemos un identificador, una variable mes y un importe y deseamos obtener el menor importe por mes el primer paso a realizar es ordenar el data frame de R por ese identificador, el mes y el importe en orden descendente:

df <- df[with(df,order(id,mes,-importe)),]

Una vez ordenado el data frame de R tenemos que seleccionar el último elemento por id para seleccionar aquellos clientes con menor importe:

library(dplyr)
df_bajo_importe <- df %>% group_by(id) %>% filter(row_number()==n())

Si deseamos seleccionar el mayor importe hacemos lo mismo:

library(dplyr)
df_bajo_importe <- df %>% group_by(id) %>% filter(row_number()==1)

Las funciones group_by unidas a filter(row_number) equivalen a esos first y last de SAS. Saludos.

¿Puede la información de Twitter servir para calcular el precio de tu seguro?

rvaquerizo

Debemos de ir introduciendo el concepto de Social Pricing en el sector asegurador, si recordamos el año pasado Admirall y Facebook tuvieron un tira y afloja por el uso de la información de Facebook para el ajuste de primas de riesgo. Facebook alegaba a la sección 3.15 de su privacidad para no permitir emplear esta información a Admirall. Probablemente es un tema más económico. El caso es que tanto Facebook, como Instagram, como Twitter, como LinkedIn, como xVideos,… tienen información muy interesante acerca de nosotros, información que se puede emplear para el cálculo de primas en el sector asegurador (por ejemplo). No voy a decir como hacer esto, este blog no es el lugar, el que quiera conocer mis ideas que se ponga en contacto conmigo. Yo soy alguien “público”, no tengo problema en dejar mis redes sociales abiertas y este caso me sirve de ejemplo para analizar que dice Twitter de mí y también sirve de ejemplo para refrescar el manejo de información con Twitter con #rstats. Esta entrada es una combinación de entradas anteriores de esta bitácora así que recordemos como empezábamos a hacer scrapping de Twitter:

 library(twitteR)
library(base64enc)

consumer_key="XXXXXXXXXxxxxXXXXXXXxx"
consumer_secret="xxXXXXXXXXxxXXXXXXXXXxxXXxxxxx"
access_token="81414758-XXxXxxxx"
access_secret="XXXxXXxXXxxxxx"

setup_twitter_oauth(consumer_key, consumer_secret, access_token=access_token, access_secret=access_secret)

Vía Oauth ya podemos trabajar con el paquete twitteR desde nuestra sesión de R y ahora lo que vamos a crear es un objeto R del tipo “user” con la información que tiene el usuario r_vaquerizo (yo mismo):

rvaquerizo <- getUser('r_vaquerizo')
rvaquerizo_seguidos <- rvaquerizo$getFriends(retryOnRateLimit=120)
seguidos <- do.call("rbind", lapply(rvaquerizo_seguidos, as.data.frame))

El objeto rvaquerizo tiene mucha información sobre mí Sigue leyendo ¿Puede la información de Twitter servir para calcular el precio de tu seguro?

Archivos shape y geojason para crear un mapa de España por códigos postales

Como sabéis Correos (empresa de capital 100% público) ha decidido no colaborar con CartoCiudad (leer los comentarios de este enlace) y poner precio a los mapas de España por códigos postales. El ahora escribiente no se descargó todas las provincias y no puede pasaros estos archivos shape, sin embargo un comentario de Iñigo Flores en el mismo enlace de antes nos pone en la pista de un dataset con los códigos postales. Podemos encontrar tanto los archivos shape como los archivos geojson:

Pulsa aquí para acceder al repositorio git de Iñigo con los archivos necesarios para realizar un mapa de España por códigos postales.

El único problema... no está actualizado, si quieres el mapa actualizado prepara 5.000 € para tu proyecto de fin de grado, master,...

Truco Python. Reemplazar una cadena de caracteres en los nombres de las columnas de un data frame

Más largo el título de la entrada que la entrada en si misma. Tenemos un conjunto de datos que os podéis descargar de este link que ya es conocido. Os descargáis los datos y creamos un data frame que tiene 10.000 registros y 251 columnas, casi todas se llaman attx y queremos cambiar el nombre a columna_x. Mi sugerencia para hacerlo vía pandas es:

import pandas as pd
df = pd.read_csv('C:\\temp\\wordpress\\au2_10000.csv')
df.head()

df.columns = df.columns.str.replace('att','columna_')
df.head()

Espero que sea de utilidad. Saludos.

Gráfico de barras y líneas con Python

grafico de barras y lineas python

Típico gráfico de dos ejes de barras y líneas donde las barras miden una exposición y las líneas una frecuencia, en el mundo actuarial son muy habituales y son muy útiles para ver proporciones dentro de grupos a la vez que representamos el tamaño del grupo. Los datos habituales del curso de GLM for insurance data:

import pandas as pd
df = pd.read_csv('http://www.businessandeconomics.mq.edu.au/our_departments/Applied_Finance_and_Actuarial_Studies/acst_docs/glms_for_insurance_data/data/claimslong.csv')

df.head()

Ya tenemos un data frame con nuestros datos leyendo directamente del csv, ahora preparamos los datos para representarlos:

frecuencia =  pd.DataFrame((df['claim']).groupby(df['period']).mean())
exposicion = pd.DataFrame((df['claim']).groupby(df['period']).count())

No tenemos un campo exposición en los datos, asumo que la exposición es igual al número de registros así que la frecuencia será la media de los siniestros y la exposición el total de registros, el análisis lo hacemos por el campo period, es el campo por el que agrupamos y ahora solo tenemos que realizar el gráfico:

import matplotlib.pyplot as plt

fig = plt.figure()
ax = exposicion['claim'].plot(kind='bar',grid=True)
ax2 = ax.twinx()
ax2.plot(frecuencia['claim'].values, linestyle='-', linewidth=2.0,color='red')
plt.show();

El eje principal es ax y representa la exposición en barras, con ax.twinx añadimos eje secundario, ax2 que será la línea que contiene la frecuencia. No es un código python complejo y es un tipo de gráfico que nos ofrece mucha información. En breve GLM con python (espero).

Machine learning. Elegir el mejor Gradient Boost de forma iterativa con GridSearchCV

Carlos [aka "el tete"] me está enseñando python y una de las cosas que me ha enseñado es seleccionar de forma iterativa el mejor modelo con GridSearchCV y por si fuera poco vamos a emplear el método de clasificación "gradient boosting" para que no caiga en desuso sobre todo porque es una técnica que, bajo mi punto de vista, ofrece modelos muy estables. El ejemplo para ilustrar el proceso ya es conocido ya que vamos a estimar la letra O, mi talento no da para mucho más. Recordamos los primeros pasos:

import numpy as np
import pandas as pd
from pylab import *
 
largo = 10000
 
df = pd.DataFrame(np.random.uniform(0,100,size=(largo, 2)), columns=list('XY'))
 
dependiente1 = np.where(((df.X-50)**2/20**2 + (df.Y-50)**2/40**2>1) ,1,0)
dependiente2 = np.where(((df.X-50)**2/30**2 + (df.Y-50)**2/50**2>1) ,1,0)
dependiente = dependiente1 - dependiente2
 
plt.scatter(df.X, df.Y,c=dependiente,marker=".")
show()

Tenemos una letra O fruto de jugar con la ecuación de la elipse y ahora creamos el conjunto de datos con el que entrenamos el modelo y el conjunto de datos de test para comprobar posteriormente como funciona:

#Dividimos en validacion y test
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df,dependiente,stratify=dependiente,
test_size = 0.5, random_state=123)

Nada nuevo bajo el sol pero me gusta poner los ejemplos al completo para que sean reproducibles. Ahora vienen las enseñanzas "del tete":

# GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingClassifier 
from sklearn.model_selection import GridSearchCV
np.random.seed(40)

#Parámetros para iterar
fun_perdida = ('deviance', 'exponential')
profundidad = range(5,15)
minimo_split =range(5,10,1)
learning_rate = [ 0.01, 0.1, 0.2, 0.3]

modeloGBM = GradientBoostingClassifier(random_state=1,n_estimators =100)

param_grid = dict(max_depth = profundidad, min_samples_split=minimo_split,
                  loss = fun_perdida, learning_rate=learning_rate)

grid = GridSearchCV(modeloGBM, param_grid, cv=10,scoring= 'roc_auc')
grid.fit(X_train,y_train)

mejor_modelo = modeloGBM.fit(X_train,y_train)

Los protragonistas de la entrada son GradientBoostingClassifier Sigue leyendo Machine learning. Elegir el mejor Gradient Boost de forma iterativa con GridSearchCV