19 nov. 2017

Árbol de decisión hecho en Python

Tenía algo de tiempo sin escribir (la situación en Venezuela no está facil).

Voy a ir retomando poco a poco los artículos en el blog,  tengo algunas cosas sobre Inteligencia Artificial (Redes Neuronales, Lógica Difusa, machine learning y deep learning), patrones de diseño con Python.

Este artículo trata de un modeo de predicción llamado árbol de decisión, según wikipedia es: Es un modelo de predicción utilizado en diversos ámbitos que van desde la inteligencia artificial hasta la economía. Dado un conjunto de datos se fabrican diagramas de construcciones lógicas, muy similares a los sistemas de predicción basados en reglas, que sirven para representar y categorizar una serie de condiciones que ocurren de forma sucesiva, para la resolución de un problema.

En youtube hay un canal dedicado a la ciencia de datos principalmente con Python, el youtuber es Siraj Raval . En el siguiente vídeo explica como hacer un clasificador de género hecho con un Árbol de decisión, el vídeo lo pueden ver en el siguiente enlace.


El código fuente del ejercicio del vídeo se puede ver en github.

La idea es tener los datos de altura, peso y talla de zapato y si es hombre o mujer, se construye el árbol de decisión, se pasa los datos y de ahí al pasar otro dato se genera una salida de si es hombre o mujer.

A continuación el código:



#!/usr/bin/env python

"""

https://www.youtube.com/watch?v=T5pRlIbr6gg

https://github.com/llSourcell/gender_classification_challenge/blob/master/demo.py



"""

#Se importa la librería sklearn el módulo tre

from sklearn import tree



#Se crea la instancia del árbol de decisión.

clf = tree.DecisionTreeClassifier()



#[altura, peso, talla de zapato]

X = [[181, 80, 44], [177, 70, 43], [160, 60, 38], [154, 54, 37], [166, 65, 40],

     [190, 90, 47], [175, 64, 39],

     [177, 70, 40], [159, 55, 37], [171, 75, 42], [181, 85, 43]]



#La salida donde se dice si es hombre o mujer

Y = ['hombre', 'hombre', 'mujer', 'mujer', 'hombre', 'hombre', 'mujer', 'mujer',

     'mujer', 'hombre', 'hombre']



#Se le pasa los datos  X y Y

clf = clf.fit(X, Y)



#Se definen los datos 1 y 2

dato1 = [190, 70, 43]

dato2 = [185, 62, 37]

prediction = clf.predict([dato1])

#Se muestra el resultado de la predicción de dato1

print(prediction)



prediction = clf.predict([dato2])


#Se muestra el resultado de la predicción de dato 2
print(prediction)


Al ejecutar el script se tiene:

python3 arboldecision.py 
['hombre']
['mujer']

Significa que, para una altura de 1.90 mts, peso 70 kilograms, y talla de 43, el resultado es que es un hombre, y para altura de 1.85 mts, peso 62 kilogramos y talla de 37, el resultado es que es una mujer.

Recomiendo ver el vídeo completo para tener una mejor explicación del funcionamiento del árbol de decisión. 


24 jul. 2017

Almacenar los datos de los eventos sismológicos de Funvisis con Python3

En el artículo sobre la captura de datos de eventos sismológicos de funvisis solamente se trabajó el orenamiento de los datos, pero hace falta almacenar la información en una base de datos, este es el tema del artículo.

Ahora se tiene varios módulos:

  • sismux_getdata.py: Es el módulo que hace el webscraping de la página de funvisis.
  • sismux_mongo.py: Es el módulo que implementa un CRUD para mongoDB a MongoLab.
  • sismux_main.py: Es el módulo principal que consulta cada 5 min la página de Funvisis para guardar la información en la base de datos si no existe.
  • sismux_apirest.py: Es el módulo que implementa un API rest(próximo artículo).
  • sismux_graphql.py: Es el módulo que implementa un API con GraphQL (próximo artículo).

Del artículo anterior se muestra el código de sismux_getdata.py:



#!/usr/bin/env python3





#Se importa beautifulSoup

from bs4 import BeautifulSoup

#Se importa la fecha

import datetime

import requests

import sys

import json



class Sismo(object):

    def __init__(self,url="http://www.funvisis.gob.ve/",home="index.php",referer='http://www.cantv.com.ve'):

        headers = {'User-agent': 'Mozilla/5.0',\

            'SSL_VERIFYHOST': 'False',\

            'FRESH_CONNECT':'True',\

            'RETURNTRANSFER':'True',\

            'SSL_VERIFYPEER': 'False',\

            'Referer': referer

            }

        self.__url = url

        self.__home = home

        self.__urlhome = self.__url + self.__home

        self.__session = requests.Session()

        self.__session.headers.update(headers)



    def GetData(self):

        #Se  obtiene la pagina por medio de session.

        try:

            self.__r = self.__session.get(self.__urlhome)

            self.__page = self.__r.content

        except (requests.exceptions.SSLError):

            print("SSL Error")

            sys.exit(0)

        except (requests.exceptions.ConnectionError):

            print("Connection Error")

            sys.exit(0)

        #Se le pasa la pagina a beautifulsoup usando lxml de parser.

        self.__soup = BeautifulSoup(self.__page,"lxml")

        #Se crea el diccionario que almacena los datos

        self.__sismo = {}



        #SE obtiene el primer  div que tengan class module

        for row in self.__soup('div', {'class': 'module'})[0]:

            #Se obtiene el tag a para luego obtener el href y tener el url

            #del gif del sitio de funvisis que tiene la imagen del sitio donde

            #fue el sismo.

            trs = row.find('a')

            if trs == -1:

                continue

            self.__sismo['urlref'] = self.__url  + trs.get('href',None)



            trs = row.find('tr')

            if trs == -1:

                continue

            #Obtiene los datos del sismo del sitio de funvisis

            datos = trs.find('td').getText().split(' ')[0].split('\n\t')

            self.__sismo['fecha'] = datos[0].split('\xa0')[1]

            date = self.__sismo['fecha'].split("/")

            self.__sismo['hora'] = datos[2].split(" ")[-2]

            time= self.__sismo['hora'].split(":")

            self.__sismo['datetime'] = datetime.datetime(int(date[2]),int(date[1]),int(date[0]), int(time[0]), int(time[1]))

            self.__sismo['magnitud'] = datos[4].split(" ")[-1]

            mag = datos[6].split(" ")[-1].split('\xa0')

            self.__sismo['profundidad'] = mag[0] + " "+ mag[1]

            lat = datos[8].split(" ")

            self.__sismo["latitud"] = lat[-2] + " " + lat[-1]

            lon =  datos[10].split(" ")

            self.__sismo['longitud'] = lon[-2] + " "+ lon[-1]

            self.__sismo['epicentro'] = datos[11].split(":")[1].split('\xa0')[-1]

            self.__sismo['loc'] = {'type':'Point','coordinates' : [ float(lat[-2]) , float(lon[-2]) ]}

        return self.__sismo



El código del módulos sismux_mongo.py se muestra a continuación: 




#!/usr/bin/env python3
import pymongo

from pymongo import MongoClient


#Se define el uri de la conexion a mongolab

uri = 'mongodb://usuario:clave@ds045064.mlab.com:45064/basedatos'

#Se define la base de datos y la cole

#ccion


basedatos = "sismux"

coleccion = "sismos"


#Se crea la clase BaseDatos que simplemente implemente un crud.
class BaseDatos(object):

    #Se define la intancia de mongoclient, se define la base de datos y

    #la coleccion

    def __init__(self,uri=uri,basedatos=basedatos,coleccion=coleccion):

        self.__client = MongoClient(uri)

        self.__db = self.__client[basedatos]

        self.__coleccion = self.__db[coleccion]


  def ConsultarTodos(self):

        #Traer todos los elementos de la consulta.

        elementos = []

        for i in self.__coleccion.find():

            elementos.append(i)

        return i


  def Consultar(self,patron):

        #Se devuelve la consulta de un elemento

        return self.__coleccion.find_one(patron)


  def Insertar(self,documento):

        #Se inserta un documento

        self.__coleccion.insert(documento)


  def Finalizar(self):

        #Se cierra la conexion con la base de datos
        self.__client.close()



A continuación se muestra sismux_main.py:



#!/usr/bin/env python3
from sismux_getdata import Sismo
import json
import time
import sys

from sismux_mongo import BaseDatos
import datetime
import logging

bd = BaseDatos()
sismo  = Sismo()
datos = sismo.GetData()
#bd.Insertar(datos)
#datos['datetime']
datime = datetime.datetime(2017,5,25, 20, 51)
#print(bd.Consultar({'datetime': datie}m))
def main():

   #Se crea la instancia a la base de datos
   bd = BaseDatos()
   #Se crea la instancia del webscraping
   sismo = Sismo()
   #Se optiene los datos de la pagina de funvisis
   datos = sismo.GetData()
   #Se consulta si ya existe el dato guardado en la base de datos
   query1 = bd.Consultar({'datetime': datos['datetime']})
   query2 = bd.Consultar({'loc': {'coordinates': datos['loc']['coordinates']}})
   if (query1 != None):
        return False
   else:
       #Si no existe se inserta en la base de datos.
       bd.Insertar(datos)
       return True

#Se crea un ciclo para consultar cada 5 min

def ciclo(tim=300):
    #Se define el log
    logging.basicConfig(filename="sismux.log",level=logging.DEBUG,format='%(asctime)s %(message)
s')
 
 
    
while True:
        #Se ejecuta main.
       result = main()
       #Se guarda el resultado de main en el log
       logging.info(result)
       #Se espera 5 min (por defecto)
       time.sleep(tim)



if __name__ == '__main__':
   #Se ejecuta la funcion ciclo
   ciclo()


Para ejecutar el programa se corre:

python3 sismux_main.py 

Al ejecutarlo se crea un archivo log y en el se tiene lo siguiente:

tail -f sismux.log 
2017-07-24 11:42:43,149 Starting new HTTP connection (1): www.funvisis.gob.ve
2017-07-24 11:42:43,821 http://www.funvisis.gob.ve:80 "GET /index.php HTTP/1.1" 200 14319
2017-07-24 11:42:56,560 False
2017-07-24 11:47:56,637 Starting new HTTP connection (1): www.funvisis.gob.ve
2017-07-24 11:47:57,466 http://www.funvisis.gob.ve:80 "GET /index.php HTTP/1.1" 200 14319
2017-07-24 11:48:12,086 False
2017-07-24 11:53:12,209 Starting new HTTP connection (1): www.funvisis.gob.ve
2017-07-24 11:53:12,976 http://www.funvisis.gob.ve:80 "GET /index.php HTTP/1.1" 200 14319
2017-07-24 11:53:18,516 False
2017-07-24 11:58:18,605 Starting new HTTP connection (1): www.funvisis.gob.ve
2017-07-24 11:58:30,984 http://www.funvisis.gob.ve:80 "GET /index.php HTTP/1.1" 200 14319
2017-07-24 11:58:57,034 False


Como muestra el log ya se tiene el último sismo en la base de datos.

En la siguiente figura se muestra un documento almacenado en la base de datos:


Para el siguiente artículo se desarrollará el API. 

Nota: En un futur artículo se muestra la creación de un demonio y como ponerlo a funcionar con SystemD y el empaquetado de todo los módulos desarrollados.