Archivos de la categoría Python

Truco Python. Seleccionar o eliminar variables de un data frame en base a un prefijo, sufijo o si contienen un caracter

A la hora de seleccionar las características de un data frame es posible que nos encontremos con la necesidad de seleccionar o eliminar características del data frame y que el nombre de esas características tenga un determinado patrón. Esta labor la podemos realizar mediante selección de elementos en listas, en esta entrada del blog vamos a tener 3 tipos de selecciones:

1. Seleccionar o eliminar aquellas variables que empiezan por un determinado prefijo
2. Seleccionar o eliminar aquellas variables que contienen una cadena de caracteres
3. Seleccionar o eliminar aquellas variables que finalizan con un sufijo

Para ilustrar este trabajo generamos un data frame con datos aleatorios y 10 columnas:

import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(0,100,size=(100, 10)),
columns=['A1','A2','A3','B1','B2','B3','C1','C2','C3','DA'])

El primero de los filtros a realizar es identificar que variables de nuestro data frame contienen el string 'A':

col = list(df.columns)
#Filtro 1: Columnas que tienen una A
filtro1 = [col for col in df if col.find('A')>=0]
#Eliminar
df1_drop = df.drop(columns=filtro1)
#Seleccionar
df1_keep = df[filtro1]

Siempre vamos a hacer el mismo proceso, las características de nuestro data frame irán en una lista, después recorremos la lista y seleccionamos aquellos donde el método .find('A') sea mayor o igual a 0, con esto hemos creado una sublista con aquellas características que tienen el string 'A' mediante .drop(columns=) eliminamos del data frame los elementos contenidos en una lista Sigue leyendo Truco Python. Seleccionar o eliminar variables de un data frame en base a un prefijo, sufijo o si contienen un caracter

Truco Python. Agrupar variable en función de la frecuencia

Me ha surgido la necesidad de crear una nueva variable en un data frame a partir de la frecuencia de otra, es decir, quedarme con los valores más frecuentes y aplicar una categoría resto para aquellos valores que no estén en los más frecuentes. Para realizar esto se me ha ocurrido la siguiente función en Python:

def agrupa_frecuencia (var_origen, var_destino, df, grupos, valor_otros):
    df_grp= df[var_origen].value_counts()
    list_grp = list(df_grp.iloc[0:grupos,].index)
    df[var_destino] = df[var_origen].map(lambda x: x if x in list_grp else valor_otros, na_action='ignore')

Es una función con más parámetros que líneas, pero necesitamos una variable de origen, una variable de destino que será la que calcularemos, el data frame sobre el que realizamos la tarea, el número de grupos más otro que será el "resto" y dar un valor a ese "resto". La función lo que hace es una tabla de frecuencias ordenada descendentemente con .value_counts() y creamos una lista con el número de grupos que deseamos. Por último mediante lambdas si la variable origen está en la lista generada anteriormente le asignamos el mismo valor, en caso contrario asignamos el valor "resto". Es una programación sencilla, seguramente haya una función específica en sckitlearn para agrupar variables en base a la frecuencia, pero no la he encontrado y he tardado más en buscarla que en hacerla.

Como es habitual os pongo un ejemplo de uso para que podáis ver como funciona:

personas = 1000
grupo = pd.DataFrame(np.random.poisson(15,personas))
grupo['clave']=0
valor = pd.DataFrame(np.random.uniform(100,10000,personas))
valor['clave']=0
df = pd.merge(grupo,valor,on='clave')
del df['clave']
df.columns = ['grupo', 'valor']
df['grupo'].value_counts()

Vemos que grupo crea muchos valores y vamos a agrupar la variable del data frame de forma que los 10 más frecuentes toman su valor y los demás serán un resto:

agrupa_frecuencia('grupo', 'grupo_nuevo', df, 10, 99)
df['grupo_nuevo'].value_counts()

Parece que funciona, si mejoráis, actualizáis o encontráis pegas...

Beatifulsoup. Web scraping con Python o como las redes sociales pueden estar cambiando la forma de escribir

Boxplot_BeatifulShop

Desde hace tiempo mis frases son más cortas. Creo que es un problema de las redes sociales, sobre todo twitter, que está cambiando mi comportamiento. Para analizar si esto está pasando se me ha ocurrido analizar la longitud de las frases de este blog desde sus inicios y de paso aprovechar para hacer web scraping con la librería Beatifulshop de Python. La idea es recorrer el blog y calcular la longitud de las frases y representar gráficamente como ha ido evolucionando esa longitud.

Podía haber trabajado directamente con la base de datos de wordpress pero he preferido leer las páginas de la web. Hay un problema, si véis el nombre de las páginas no tiene un orden cronológico, son el nombre de la propia entrada [http://analisisydecision.es/los-bancos-lo-llaman-transformacion-digital-yo-lo-llamo-me-da-miedo-facebook/] pero es cierto que se almacena una vista por mes de las entradas publicadas [http://analisisydecision.es/2017/02/] vamos a emplear esas vistas que no recogen la entrada entera pero si las primeras frases, con estas limitaciones vamos a medir la longitud de las frases.

Luego la analizamos paso a paso pero la función de Python que voy a emplear es:

import pandas as pd
from bs4 import BeautifulSoup
import requests
import re
import time 
import string

def extrae (anio, mes):
    url = "http://analisisydecision.es/" + anio + "/" + mes + "/"
    print (url)
    # Realizamos la petición a la web
    pagina = requests.get(url)
    soup = BeautifulSoup(pagina.content, 'html.parser')
    m = str(soup.find_all('p'))
    m = BeautifulSoup(m)
    m = str(m.get_text())
    frases = pd.DataFrame(m.split("."),columns=['frase'])
    frases['largo'] =  frases['frase'].str.len()
    frases['mes'] = anio + mes
    frases['frase'] = frases['frase'].apply(lambda x:''.join([i for i in x if i not in string.punctuation]))
    frases = frases.loc[frases.largo>10]
    time.sleep(60) 
    return frases

Os comento paso a paso, a la función le vamos a pasar el mes y el año y esa será la url que lee http://analisisydecision.es/2017/02/ esa es la web sobre la que vamos a hacer el scraping. Vía request obtenemos la web y BeatifulSoup sólo para quedarnos con el contenido en HTML de la web cargada Sigue leyendo Beatifulsoup. Web scraping con Python o como las redes sociales pueden estar cambiando la forma de escribir

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.

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.

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