Taller Analytics y ML

1. Introducción

El objetivo de este taller es aprender a usar diferentes componentes de la plataforma onesait Platform para completar el flujo de un proceso de analítica de información, desde la ingesta, pasando por el ML hasta la visualización.

Se parte de un dataset de la plataforma Kaggle https://www.kaggle.com/mylesoneill/game-of-thrones así como de kernels del mismo https://www.kaggle.com/shaildeliwala/exploratory-analysis-and-predictions y https://www.kaggle.com/zhy450324080/death-prediction-battle-analysis.


Se compondrá de los siguientes pasos:
 

  • Ingesta y preparación de los datos:

    • Creación de las ontologías con las que se trabajará

    • Creación de un DigitalClient para acceder a las ontologías

    • Se tendrán 3 tipos de ingesta de información:

      1. Import tool

      2. Dataflow

      3. Notebook

  • Data analytics & exploration & ML de los datos cargados mediante los notebooks de la plataforma

  • Visualización de los datos mediante los dashboards de la plataforma


Para este taller se usarán los siguientes datasets:

  • character-predictions.csv que se cargará desde la herramienta de Importación del Control Panel (Import Tool)

  • battles.csv cargado desde el componente DataFlow

  • character-deaths.csv que se cargará desde el componente Notebooks

Se presuponen conocimientos de programación (el Python principalmente) y la realización del taller IoT por lo que no se explicarán conceptos básicos de la plataforma.
Para este taller es necesario disponer de un usuario en el entorno CloudLab con Rol Analytics.

2. Ingesta de información

En este capítulo se va a realizar la ingesta de los ficheros del dataset a través de diversas herramientas incluidas en la plataforma (cada una adecuada a diversos ámbitos).

Carga puntual desde la herramienta Import Tool

La herramienta Import Tool de la plataforma provee una forma sencilla de crear ontologías infiriendo los esquemas de datos desde ficheros json, csv, xls, xlsx o xml y cargando los mismos de forma automática. También puede usarse tanto para cargar datos en una ontología existente, como para la creación vacía de la misma.
El primer paso desde un usuario analytics, ya logado en la plataforma, será acceder al menú del mismo:

Dentro, vemos una simple interfaz con dos columnas destacadas, a la izquierda los datos (o un resumen de los mismos) que se cargarán y a la derecha el esquema inferido.
Seleccionaremos el fichero a subir en forma de ontología, en nuestro caso será el siguiente:


Para descargarlo, podemos hacer doble click en el mismo y guardarlo después en alguna carpeta. Este mismo fichero será el que seleccionemos cuando demos click en "Select File To Upload".


Se puede observar que el csv ha sido leído y transformado correctamente a formato JSON. El siguiente paso será darle a Generate Schema y podremos ver el esquema de ontología inferido automáticamente por la herramienta.

Como último paso de esta sección teniendo New Ontology y Import Data To RTDB marcados

Daremos abajo a Submit y nos aparecerá un popup para introducir el nombre de la ontología, descripción de la misma y la base de datos sobre la que irá. Es importante tener un nombre de ontología propio ya que la plataforma no permite dos ontologías con el mismo nombre:

  • Nombre: ws_got_{fecha}_{usuario}_deaths

  • Descripción: La que el usuarios quiera.

  • Datasource: MONGO


Una barra de progreso nos indicará el estado de la carga. Cuando ésta termine, se nos notificará.
Para ver los datos que acaban de cargarse podemos dirigirnos a la herramienta Query Tool y lanzar la query por defecto que tenemos:

Seleccionamos la ontología que acabamos de crear y cargar:


Finalmente ejecutamos la query por defecto generada con "Execute query":

Podemos observar cómo, en cada instancia, se han cargado los datos correspondientes a cada fila del csv inicial. Se puede ver también que, si un campo no está incluido en la fila, no llegará a la ontología destino.

ETL/Streaming desde la herramienta Dataflow

La herramienta Dataflow se basa en el software open-source Streamsets (https://streamsets.com/).

Esta herramienta permite, de forma visual, modelar ETLs y flujos streaming de datos, así como controlarlos, ver los logs de los mismos...

Los datos entrarán por un origen único seleccionado y podrán ir a uno o varios destinos. Entre muchos otros, se permite tanto origen como destino la propia plataforma mediante el nodo "onesait Platform" correspondiente.
En este caso no podremos inferir el esquema automáticamente y por tanto debemos crear tanto una ontología como un Digital Client asociado (estos pasos se obvian ya que se presupone este conocimiento del taller de IoT).


Los datos de la ontología destino será el siguiente:
Nombre: ws_got_{date}_{user}_battle
Esquema: General > EmptyBase (una vez seleccionado dar a update schema y pegar el siguiente esquema)
Esquema (incluir al crear la nueva ontología):
{
"properties": {
"name": {
"type": "string"
},
"year": {
"type": "number"
},
"attacker_king": {
"type": "string"
},
"defender_king": {
"type": "string"
},
"attacker_1": {
"type": "string"
},
"defender_1": {
"type": "string"
},
"attacker_outcome": {
"type": "string"
},
"battle_type": {
"type": "string"
},
"major_death": {
"type": "number"
},
"major_capture": {
"type": "number"
},
"attacker_size": {
"type": "number"
},
"defender_size": {
"type": "number"
},
"attacker_commander": {
"type": "string"
},
"defender_commander": {
"type": "string"
},
"summer": {
"type": "number"
},
"location": {
"type": "string"
},
"region": {
"type": "string"
},
"note": {
"type": "string"
},
"attacker_2": {
"type": "string"
},
"attacker_3": {
"type": "string"
},
"attacker_4": {
"type": "string"
},
"defender_2": {
"type": "string"
},
"defender_3": {
"type": "string"
},
"defender_4": {
"type": "string"
}
},
"type": "object",
"description": "Info workshop_got_battle",
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ws_240119_User01_battle",
"additionalProperties": false
}


Una vez creada la ontología y un DigitalClient para poder acceder a la misma, pasamos a la creación de nuestro primer DataFlow:

Nos aparecerá el listado de los mismos y tendremos que dar click en crear uno nuevo arriba a la derecha.

Daremos el nombre de: ws_got_{date}_{user}_ingestion y daremos a OK.

Una vez cargado el lienzo, podemos observar la pantalla de edición del mismo.

Mediante la paleta de elementos de la derecha podremos ir construyendo nuestro flujo.
El primer paso será solucionar uno de los errores que aparecen en pantalla, la forma en que el pipeline actual trata con los registros erróneos. Para ello nos tendremos que situar en la pestaña de Error Records y elegir Discard ya que no queremos hacer nada con esos registros en el caso de que existieran.

Con esto resuelto, pasamos a incluir nuestro origen de datos.

En este caso, se tiene en la ruta del contenedor del componente Dataflow, un volumen compartido con los datos que queremos ingestar, en una ruta del sistema de ficheros Unix. Para poder ingestar este fichero de datos usaremos el origin "Directory" dentro de Origins. Arrastrando y soltando lo tendremos en el lienzo.

Configuraremos la ruta origen con el path:
/data/workshop
Y el file patter pondremos el fichero directamente:
battle.csv



Se trata de un fichero csv estándar separado por comas con cabecera, por lo que pasamos a especificar este formato en la pestaña Data Format (dentro de Configuración).

Al ser un fichero estándar no será necesario especificar nada más que eso y que queremos usar la cabecera del mismo (With Header Line). Por lo que pasamos a nuestro primer inicio del flujo. Esto no será posible sin un destino, por lo que usaremos a modo debug el Destination "Trash" y lo conectaremos con nuestro origen "Directory 1". Al unir ambos, no debería aparecer ningún error en el flujo:

Por lo que podemos pasar a validarlo y ver que nuestro flujo está listo para arrancar:

Si es correcto, nos lo indicará. A posteriori, vamos a ver los datos que entran en el flujo, por lo que en vez de arrancarlo vamos a hacer una preview del mismo:

Con los parámetros por defecto daremos a Run Preview:

Podremos ver como los campos se estructuran en registros y ver como estos van cambiando en cada fase del proceso, por lo que podremos depurar de forma visual nuestro flujo.

Una vez visto cómo podemos depurar y ver nuestro flujo, lo completaremos.

Si observamos la estructura que nos devuelve Directory, vemos que nos sobra el campo battle_number y que todos los campos son de tipo String, por lo que tendremos que convertir los oportunos a integer, para que puedan ser correctamente ingestados. También vemos que hay campos que vienen vacíos, por lo que, por sencillez, haremos que se sustituyan por -1 en todos los casos. 

Primero, incluiremos una nueva etapa en nuestro flujo para convertir estos vacíos en -1. Esto, será posible, gracias al Processor "Field Replacer". En este caso para poder hacerlo de forma masiva, una vez añadido incluiremos esto dentro de la pestaña Replace:
/*[${f:value() == ""}]
Y pondremos el new value a -1

La siguiente etapa será la de convertir los campos numéricos al formato Integer. Para ello usaremos otro processor "Field Type Converter".
Antes de configurar nada más, uniremos los 3 elementos en orden, de manera que gracias a esto se nos facilitará la selección de campos en el último elemento añadido. Iremos a la pestaña Conversions e incluiremos los campos siguientes a formato Integer (si no aparecen hay que recordar que hay que incluir una / delante de cada campo):

  • year

  • major_death

  • major_capture

  • attacker_size

  • defender_size

  • summer



Como penúltimo paso, vamos a eliminar el campo battle_number, ya que nuestra ontología no lo tiene y no lo creemos necesario. Lo haremos con el Processor "Field Remover":

Finalmente vamos a incluir como destino el nodo de la plataforma en "destinations" llamado "Onesaitplatform Destination", el cual configuraremos de la siguiente manera:
Protocol: http
Host: iotbrokerservice
Port: 19000
Token: (el token que nos ha generado el Digital Client para la ontología que hemos creado y tenemos asociada con el mismo)
IoT Client: Nombre del Digital Client que hemos creado

IoT Client ID: Podemos dejar el autogenerado
Ontology: Nombre de la ontología que hemos creado
 
En la pestaña Destination del componente dejaremos los valores por defecto.

Finalmente arrancaremos el flujo y podremos ver en tiempo real como avanza la ingesta, throughtput de la carga, número de registros totales o en error…

Si hemos lanzado por error el flujo y vemos que no carga datos, podemos resetear el origen en los que aparecen. Esto es debido a que Streamsets mantiene un cursor sobre el último fichero leído y la posición para no realizar lecturas duplicadas.


Podremos comprobar con el Query Tool de nuevo que la carga ha sido correcta.

Una vez terminada esta sección y cargados los datos, se parará el flujo mediante el botón de stop:



Carga desde la herramienta Notebooks para Data Scientists

Esta sección servirá de introducción a la herramienta de notebooks de la plataforma y de la siguiente sección.



Lo primero que haremos será subir un nuevo fichero a la herramienta My Files de la plataforma.

Con esta herramienta, se pueden disponibilizar ficheros de forma sencilla y securizada por URL y gestionarlos desde la plataforma, usarlos desde terceros y otros componentes de la plataforma como son los notebooks.

Daremos a Create y seleccionaremos el siguiente fichero que debemos haber bajado a nuestro ordenador, el resto de parámetros por defecto y a submit .


 
Con esto aparecerá el fichero en nuestro listado.

Lo último, por sencillez, lo que tenemos que hacer, será darle a public para poder hacer el fichero público y leerlo sin autenticación.

Una vez hecho esto podemos darle a "Copy URL to Clipboard" ya que la necesitaremos en los siguientes pasos.


Ahora, vamos a crear nuestro primer Notebook en la plataforma. Este ecosistema está basado en Apache Zeppelin en su última versión (https://zeppelin.apache.org/). Provee notebooks para analítica, visualización inline, ejecución de algoritmos y muchas opciones más, en un entorno multiusuario, colaborativo, versionado, ejecutable tanto en remoto, puntual o temporizado, controlado y con capacidad de ejecutar diferentes lenguajes de programación y comunicarlos entre sí.

Se compone de párrafos en cada cual se incluye un "intérprete" con la notación %interprete. Cada tipo de interprete tiene diferentes asignaciones, desde ejecutar un cierto lenguaje de programación como Python, scala o R o lanzar queries contra una base de datos, un SparkQL, Neo4J, ejecutar comandos Shell o pintar HTML en la pantalla del usuario.
Para entenderlo lo mejor el verlo en acción. Vamos a My notebooks y damos a Crear nuevo notebook :


Le daremos el nombre de ws_got_{date}_{user}_analytics y al crearlos llegaremos a la siguiente pantalla

Nuestro objetivo, va a ser, en el lenguaje Python, usando la librería de pandas (librería de dataframes de python) cargar el archivo que anteriormente hemos subido a la plataforma como dataframe e insertarlo en la plataforma con una nueva ontología.

En la siguiente sección jugaremos con este dataframe para hacer visualizaciones inline y algoritmos de ML.


Lo primero, crearemos esta última ontología y la asociaremos nuestro DigitalClient. El nombre y el esquema son los siguientes. Se crearía de la misma manera que la anterior del ejercicio de Dataflow.
Nombre: ws_got_{date}_{user}_cdeads
Esquema:
{
"properties": {
"sNo": {
"type": "number"
},
"actual": {
"type": "number"
},
"pred": {
"type": "number"
},
"alive": {
"type": "number"
},
"plod": {
"type": "number"
},
"name": {
"type": "string"
},
"male": {
"type": "number"
},
"mother": {
"type": "string"
},
"father": {
"type": "string"
},
"heir": {
"type": "string"
},
"book1": {
"type": "number"
},
"book2": {
"type": "number"
},
"book3": {
"type": "number"
},
"book4": {
"type": "number"
},
"book5": {
"type": "number"
},
"isAliveMother": {
"type": "number"
},
"isAliveFather": {
"type": "number"
},
"isAliveHeir": {
"type": "number"
},
"isMarried": {
"type": "number"
},
"isNoble": {
"type": "number"
},
"numDeadRelations": {
"type": "number"
},
"boolDeadRelations": {
"type": "number"
},
"isPopular": {
"type": "number"
},
"popularity": {
"type": "number"
},
"isAlive": {
"type": "number"
}
},
"type": "object",
"description": "Info workshop_got_cpred",
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "workshop_got_cpred",
"additionalProperties": true
}


Con esta ontología creada y asociada al Digital Client volvemos a nuestro notebook.
Como vemos, tenemos el cursor en el primer párrafo, lo primero que haremos será import de la librería pandas en Python por lo que el párrafo tiene que in precedido por %python.

%python
import pandas as pd
import os, ssl



Para organizar nuestro trabajo, pondremos un título al párrafo para recordar lo que hacía si lo volvemos a usar.


Como vemos vamos a usar un par de librerías más, os y ssl, que nos ayudarán a acceder de forma insegura al fichero.

Este párrafo lo podemos ejecutar ya mediante el botón  (Play) de la derecha del mismo o con el cursor en el mismo shift+enter. Vemos que pasa casi instantáneamente del estado Ready a Finished lo cual nos indica el fin de la ejecución del párrafo

En el siguiente párrafo, también de Python, haremos la carga en una línea del fichero remoto en un pandas dataframe.
Añadimos un nuevo párrafo al que titularemos load cdeath file into pandas:

%python if (not os.environ.get('PYTHONHTTPSVERIFY', '') and     getattr(ssl, '_create_unverified_context', None)):     ssl._create_default_https_context = ssl._create_unverified_context character_deaths = pd.read_csv("url del fichero copiada")


Y lo ejecutamos.

Si no nos da ningún error significa que ha cargado correctamente el fichero. Las 3 primeras líneas son para desactivar la verificación ssl en Python y evitar un error de certificados. Debemos poner correctamente la url del fichero público que hemos subido.
Si queremos ver los datos, no hace falta más que escribir el nombre del dataframe (character_deaths) en un tercer párrafo y ejecutarlo.

Finalmente como, de momento, nuestro objetivo es insertar datos en la plataforma vamos a transformar este dataframe de pandas en un json válido, quitaremos los datos NA por tipo de campo y será compatible con la ontología. En la segunda línea incluido este String JSON en una variable del contexto de zeppelin que nos servirá para comunicarnos entre lenguajes:

%python
character_deaths[['sNo','actual','pred','alive','plod','male','dateOfBirth','dateoFdeath','book1','book2','book3','book4','book5','isAliveMother','isAliveFather','isAliveHeir','isAliveSpouse','isMarried','isNoble','age','numDeadRelations','boolDeadRelations','isPopular','popularity','isAlive']] = character_deaths[['sNo','actual','pred','alive','plod','male','dateOfBirth','dateoFdeath','book1','book2','book3','book4','book5','isAliveMother','isAliveFather','isAliveHeir','isAliveSpouse','isMarried','isNoble','age','numDeadRelations','boolDeadRelations','isPopular','popularity','isAlive']].fillna(-1)

character_deaths[['name','title','culture','mother','father','heir','house','spouse']] = character_deaths[['name','title','culture','mother','father','heir','house','spouse']].fillna("")
jsondataframe = character_deaths.to_json(orient="records")
z.z.put("instances",jsondataframe)

Para poder interactuar con la plataforma, se dispone del intérprete %onesaitplatform. Para poder iniciar esta interacción necesitamos lo primero iniciar una conexión con la plataforma de la siguiente forma:

%onesaitplatform
initConnection("IoT Client","Access token")



De modo que si nuestros parámetros son correctos podemos acceder a nuestras ontologías al ejecutarlo. Por seguridad, la conexión termina a los minutos por lo que si otras instrucciones de la plataforma dan problemas y puede ser necesario ejecutar de nuevo este párrafo de nuevo

Finalmente, como tenemos la conexión hecha con la plataforma ejecutamos también con el intérprete onesaitplatform y gracias también al zeppelin context insertamos los datos en la ontología destino:

%onesaitplatform
insert("{nueva ontologia creada}",z.get("instances"))



Podemos comprobar con el Query Tool de Nuevo que nuestra ontología se ha cargado de datos.

3. Data analytics & exploration & ML de los datos cargados mediante los notebooks de la plataforma

En esta sección realizaremos una breve exploración de los datos que hemos cargado en el dataframe character_deaths.
Partiendo del notebook creado anteriormente, podemos empezar a ver los datos que contiene el mismo. Lo primero, usaremos un nuevo intérprete (markdown) para escribir HTML de forma sencilla y delimitar las secciones. La # significa h1 en HTML es decir un texto cabecera grande.

Ahora iremos añadiendo consecutivamente párrafos %python para ir viendo el dataframe
Longitud del dataframe:

%python
character_deaths.size

Tamaño del mismo
%python
character_deaths.shape

Mostrar sólo la cabecera
%python
print character_deaths.head(1)

Aumentar el máximo de columnas visibles y ver datos estadísticos de cada columna
%python
pd.set_option('display.max_columns', 100)
character_deaths.describe()

Se pueden ir haciendo infinidad de transformaciones, procesamientos y visualizaciones de Python con pandas, incluso unir unos dataframes con otros, etc,... 
Ahora pasaremos a usar el motor SQL sobre pandas de los notebooks con %python.sql, para poder graficar instantáneamente los datos como si de una base de datos se tratara. Sólo hace falta definir una variable que sea dataframe, en nuestro caso character_deaths y lanzar queries sobre la misma haciéndonos preguntas:
Most popular characters by house
%python.sql
select name, house, popularity from character_deaths where House!="None" order by popularity desc limit 50

Podemos observar la visualización en formato tabla. Pero mediante un gráfico de barras podemos llegar a una visualización más interesante:


O ver la casas más populares en formato pie:
%python.sql
select house, sum(popularity) as phouse from character_deaths where House!="None" group by House order by phouse desc limit 10


O el top de casas con más gente viva:
%python.sql
select house, sum(isAlive) as alivecount from character_deaths group by house order by alivecount desc limit 10

También es possible hacer estas queries o visualizaciones directamente contra ontologías de la plataforma con el intérprete de la misma.

%onesaitplatform
select region, count(*) as c from {battle_ontology} as c group by region limit 10

Mediante asZTable se activa la visualización built-in:

%onesaitplatform
asZTable(select region, count(*) as c from {battle_ontology} as c group by region limit 10)

Como será útil en la siguiente sección, vamos a guardar como pandas los datos de la ontología del fichero battle de la siguiente forma:
%onesaitplatform
z.put("battleData", select year,attacker_outcome,summer,major_death,attacker_commander,attacker_4,attacker_3,attacker_2,attacker_1,defender_4,defender_3,defender_2,defender_1,defender_size,defender_king,battle_type,attacker_size,name,attacker_king,defender_commander,location,major_capture,region from {battle_ontology})

Y lo pasaremos a dataframe de pandas después:
%python
import json
from pandas.io.json import json_normalize
lbattle = list(z.z.get("battleData"))
battles = json_normalize(list(map(lambda x: json.loads(x), lbattle)))

Finalmente, para terminar la parte de data exploratory podemos usar potentes gráficos de las librerías de Python como matplotlib o seaborn.

Están librerías nos darán aún más libertad que la visualización built-in a costa de escribir más código para construir el gráfico objetivo.
Importamos nuevas librerías
%python
import numpy as np
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
from collections import Counter
import matplotlib.patches as mpatches
sns.set_style("white")
Generamos nuevos atributos
%python
character_predictions = character_deaths.copy(deep = True)
battles.loc[:, "defender_count"] = (4 - battles[["defender_1", "defender_2", "defender_3", "defender_4"]].isnull().sum(axis = 1))
battles.loc[:, "attacker_count"] = (4 - battles[["attacker_1", "attacker_2", "attacker_3", "attacker_4"]].isnull().sum(axis = 1))
battles.loc[:, "att_comm_count"] = [len(x) if type(x) == list else np.nan for x in battles.attacker_commander.str.split(",")]
character_predictions.loc[:, "no_of_books"] = character_predictions[[x for x in character_predictions.columns if x.startswith("book")]].sum(axis = 1)
Major death/capture events by year
%python
p = battles.groupby('year').sum()[["major_death", "major_capture"]].plot.bar(rot = 0)
_ = p.set(xlabel = "Year", ylabel = "No. of Death/Capture Events", ylim = (0, 9)), p.legend(["Major Deaths", "Major Captures"])
Which pairs fought the most battles?
%python
#Ignoring records where either attacker_king or defender_king is null. Also ignoring one record where both have the same value.
c = list(Counter([tuple(set(x)) for x in battles.dropna(subset = ["attacker_king", "defender_king"])[["attacker_king", "defender_king"]].values if len(set(x)) > 1]).items())
p = pd.DataFrame(c).sort_values(1).plot.barh(figsize = (10, 6))
_ = p.set(yticklabels = ["%s vs. %s" % (x[0], x[1]) for x in list(zip(*c))[0]], xlabel = "No. of Battles"), p.legend("")
How many commanders did armies of different kings have?
%python
p = sns.boxplot("att_comm_count", "attacker_king", data = battles, saturation = .6, fliersize = 10., palette = ["lightgray", sns.color_palette()[1], "grey", "darkblue"])
_ = p.set(xlabel = "No. of Attacker Commanders", ylabel = "Attacker King", xticks = range(8))


Por último, vamos generar varios modelos de ML con el objetivo de ver las herramientas de python que ya vienen en la plataforma integradas:
Data Cleansing

%python #-------- Copy Again ---------- data = character_deaths.copy(deep = True) #-------- culture induction ------- cult = { 'Summer Islands': ['summer islands', 'summer islander', 'summer isles'], 'Ghiscari': ['ghiscari', 'ghiscaricari', 'ghis'], 'Asshai': ["asshai'i", 'asshai'], 'Lysene': ['lysene', 'lyseni'], 'Andal': ['andal', 'andals'], 'Braavosi': ['braavosi', 'braavos'], 'Dornish': ['dornishmen', 'dorne', 'dornish'], 'Myrish': ['myr', 'myrish', 'myrmen'], 'Westermen': ['westermen', 'westerman', 'westerlands'], 'Westerosi': ['westeros', 'westerosi'], 'Stormlander': ['stormlands', 'stormlander'], 'Norvoshi': ['norvos', 'norvoshi'], 'Northmen': ['the north', 'northmen'], 'Free Folk': ['wildling', 'first men', 'free folk'], 'Qartheen': ['qartheen', 'qarth'], 'Reach': ['the reach', 'reach', 'reachmen'], } def get_cult(value): value = value.lower() v = [k for (k, v) in cult.items() if value in v] return v[0] if len(v) > 0 else value.title() data.loc[:, "culture"] = [get_cult(x) for x in data.culture.fillna("")] #-------- culture induction ------- data.drop(["name", "alive", "pred", "plod", "isAlive", "dateOfBirth", "dateoFdeath"], 1, inplace = True) data.loc[:, "title"] = pd.factorize(data.title)[0] data.loc[:, "culture"] = pd.factorize(data.culture)[0] data.loc[:, "mother"] = pd.factorize(data.mother)[0] data.loc[:, "father"] = pd.factorize(data.father)[0] data.loc[:, "heir"] = pd.factorize(data.heir)[0] data.loc[:, "house"] = pd.factorize(data.house)[0] data.loc[:, "spouse"] = pd.factorize(data.spouse)[0] data.fillna(value = -1, inplace = True) ''' $$ The code below usually works as a sample equilibrium. However in this case, this equilibirium actually decrease our accuracy, all because the original prediction data was released without any sample balancing. $$ data = data[data.actual == 0].sample(350, random_state = 62).append(data[data.actual == 1].sample(350, random_state = 62)).copy(deep = True).astype(np.float64) ''' Y = data.actual.values Odata = data.copy(deep=True) data.drop(["actual"], 1, inplace = True)


Feature Correlation
%python
sns.heatmap(data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2) #data.corr()-->correlation matrix
fig=plt.gcf()
fig.set_size_inches(30,20)
plt.show()


RandomForest (over fit)
%python
data.drop(["sNo"], 1, inplace = True)
''' ATTENTION: This rf algorithm achieves 99%+ accuracy, this is because the \
original predictor-- the document releaser use exactly the same algorithm to predict!
'''
from sklearn.ensemble import RandomForestClassifier

random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(data, Y)
print('RandomForest Accuracy:(original)\n',random_forest.score(data, Y))


DecisionTree
%python
from sklearn.tree import DecisionTreeClassifier
DT=DecisionTreeClassifier()
DT.fit(data,Y)
print('DecisionTree Accuracy:(original)\n',DT.score(data, Y))


SVC
%python
from sklearn.svm import SVC, LinearSVC
svc = SVC()
svc.fit(data, Y)
print('SVC Accuracy:\n',svc.score(data, Y))


LogisticRegression
%python
from sklearn.linear_model import LogisticRegression
LR = LogisticRegression()
LR.fit(data, Y)
print('LogisticRegression Accuracy:\n',LR.score(data, Y))


KNN
%python
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(data, Y)
print('kNN Accuracy:\n',knn.score(data, Y))


NaiveBayes Gaussian
%python
from sklearn.naive_bayes import GaussianNB
gaussian = GaussianNB()
gaussian.fit(data, Y)
print('gaussian Accuracy:\n',gaussian.score(data, Y))


RandomForest with CrossValidation
%python
from sklearn.model_selection import cross_validate
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
predictors=['title', 'culture', 'mother', 'father', 'heir', 'house', 'spouse', 'male', 'book1', 'book2', 'book3', 'book4', 'book5', 'isAliveFather', 'isAliveMother', 'isAliveHeir', 'isAliveSpouse', 'isMarried', 'isNoble', 'age', 'numDeadRelations', 'boolDeadRelations', 'isPopular', 'popularity']
alg=RandomForestClassifier(random_state=1,n_estimators=150,min_samples_split=12,min_samples_leaf=1)
kf=KFold(n_splits=3,random_state=1)
kf=kf.get_n_splits(Odata.shape[0])
scores=cross_val_score(alg,Odata[predictors],Odata["actual"],cv=kf)
print('RandomForest Accuracy:\n',scores.mean())

4. Visualización de los datos mediante los dashboards de la plataforma

Nuestro objetivo en esta sección será el de usar el motor de dashboards integrado en la plataforma (se recomienda usar Google Chrome ya que el motor web, está optimizado para el mismo).

Generaremos una visualización con los datos cargados en los pasos anteriores. Estos dashboards generados podrán compartirse con diferentes usuarios, parametrizarse, hacerlos públicos y muchas opciones más.
Primero, con un usuario ya logado vamos a crear nuestro primer dashboard, para ellos vamos a My Dashboards dentro del menú Visualizations y hacemos click en Create Dashboard.

Podemos observar que al entrar al menú vemos los dashboard públicos/propios o a los que nos han dado permiso.


Dentro daremos el nombre a nuestro nuevo dashboard
ws_got_{date}_{User01}_Visualization

Por último, sin cambiar nada más, damos a "New" y nuestro dashboard estará creado. Lo podremos comprobar, ya que llegaremos a un lienzo vacía sobre el que incluiremos elementos denominados "gadgets" que recuperarán datos directamente desde las ontologías.

Vamos a crear nuestro primer gadget. En este caso vamos a crear un gadget Pie para ver la distribución de popularidad de las diferentes casas.

Este gadget nos servirá de filtro sobre otros del dashboard a futuro.
Para ello vamos añadir un nuevo gadget con el botón arriba a la derecha. Veremos que nos aparece una banda con los gadgets disponibles en la plataforma:

Arrastraremos el tipo de gadget (Pie chart) dentro del lienzo, sobre la zona que queremos pintarlo. Al soltar nos aparecerá el siguiente popup, en el cual al no tener ningún gadget pie previamente creado daremos a "New Gadget"

Nos parecerá el formulario de creación del gadget, en el cual:
Nombre: ws_got_240119_User01_pie
Ontology or Datasource: (nuestra ontología de deaths)

Como vemos, se nos dará una query por defecto.

Dejaremos lo primero Max Values a 10 y como queremos ver la popularidad de las casas, usaremos esta query:
select house, sum(popularity) as phouse from {ontología cdeaths} where house!= -1 and house!="None" group by house order by phouse desc limit 10
Con lo que tendremos agrupadas las casas por la popularidad total. Si ejecutamos la misma en el formulario podemos ver los resultados.

Damos a "Continue".

Finalmente seleccionamos los campos para nuestro pie:

X Axis: house

Y Axis :phouse

y damos a New.


Mediante los bordes del mismo podemos redimensionar el elemento o moverlo en el lienzo mediante las flechas de la cabecera.

Crearemos rápido un par de gadgets más:
El primero un mixed en el que veremos la popularidad individual de los personajes
Nombre: ws_got_{date}_{user}_mixed

select * from {ontología cdeaths} order by popularity desc limit 10



Damos a Continue y ponemos la configuración que aparece en la imagen



Finalmente nos tiene que aparecer el siguiente gráfico:

El segundo una tabla a modo de logbook de batallas:
ws_got_240119_User01_table
Query: select name,attacker_1,defender_1, case note when "-1" then "" else note end as note from {ontología battle} 


Colocando todo deberíamos tener en nuestro dashboard algo como lo siguiente:

Vamos a relacionar los dos gadgets de arriba ya que, como vemos, es posible a nivel de datasource filtrar los personajes de la derecha desde la casas. Para ellos usaremos el (Datalink):

Pondremos como origen nuestro Pie y como destino nuestro Mixed. El campo de cruce será house en los dos casos:

Daremos al para añadir la conexión y al cerrar cualquier click en el pie nos filtrará el Gadget Mixed



Decorando un poco más el Dashboard podemos llegar a un resultado final como el siguiente: