In [1]:
speakers = [{'name':'Mai Giménez',
'twitter': '@adahopper',
'weapons': ['Python', 'Bash', 'C++'],
'pyladies': True},
{'name':'Angela Rivera',
'twitter': '@ghilbrae ',
'weapons': ['Python', 'Django', 'C++'],
'pyladies': True}]
for speaker in speakers:
for k,v in speaker.items():
print("- {}: {}".format(k,v))
print()
#print('\n'.join(["- {}: {}".format(k, v) for speaker in speakers for k,v in speaker.items()]))
In [2]:
from IPython.display import Image
Image(filename='pyladies.png')
Out[2]:
In [3]:
Image(filename='notebook.png')
Out[3]:
In [4]:
Image(filename='marvel_logo.jpg')
Out[4]:
Marvel, es una editorial de cómics estadounidense fundada por Martin Goodman en 1939, como Marvel Mystery Comics. Aunque Marvel, tal y como hoy la conocemos (Marvel Worldwide Inc.), data de 1961 con la publicación de Los Cuatro Fantásticos y otras historias de superhéroes creadas por autores como Stan Lee, Jack Kirby o Steve Ditko, entre otros.
Marvel es madre de archiconocidos personajes o equipos como:
¡Y todos estos datos son nuestros!
In [5]:
from marvel.marvel import Marvel
from marveldev import Developer
Para acceder a la API es necesario pedir unas credenciales de desarrolladores en http://developer.marvel.com/
¡Ojo con las peticiones! Podemos pedir hasta 100 resultados cada vez.
In [6]:
developer = Developer()
marvel = Marvel(*developer.get_marvel_credentials())
In [7]:
character_data_wrapper = marvel.get_characters(orderBy="-modified", limit="100")
print(character_data_wrapper.status)
In [8]:
for character in character_data_wrapper.data.results[:10]:
print("* {character.name}: {character.modified_raw}".format(character=character))
¿Qué información tenemos disponible para cada personaje?
In [9]:
', '.join([attr for attr in dir(character) if not attr.startswith('_')])
Out[9]:
Mmmm, no está mal pero ¿es eso lo que buscamos? Veamos el wiki:
In [25]:
from IPython.core.display import HTML
HTML("<iframe src={} width=1000 height=800></iframe>".format(character_data_wrapper.data.results[2].wiki))
Out[25]:
Aquí encontramos muchos más datos acerca del personaje: Rasgos físicos, ocupación, educación... ¡Tiene buena pinta! ¡Scrappemos la wiki!
El problema reside en que Marvel sólo nos deja obtener hasta 100 resultados cada vez.
Lo primero que deberíamos hacer es recoger información de la web y almacenarla.
Pero, a alguien más se le ha ocurrido eso, y no vamos a reinventar la rueda. @asamiller ha desarrollado una app en node.js que explora la API de Marvel y almacena los datos usando Orchestrate. El código está disponible en github.
# TODO
En realidad molaría scrappear la wiki y no tener una versión estática, que además puede estar un poco desfasada, pero esto deberíamos incluirlo en la librería pyMarvel. Si te animas, búscanos después de la charla y hablamos.
In [26]:
import json
In [33]:
from os.path import join
from os import listdir
import socket
MARVELOUSDB_PATH_A = "../marvelousdb-master/data/characters/"
MARVELOUSDB_PATH_M = "../marvelousdb/data/characters/"
MARVELOUSDB_PATH = MARVELOUSDB_PATH_M if 'alan' in socket.gethostname() else MARVELOUSDB_PATH_A
In [34]:
json_db = [join(MARVELOUSDB_PATH, json_file) for json_file in listdir(MARVELOUSDB_PATH)]
print("En MarvelousDB tenemos un backup de {} personajes".format(len(json_db)))
Pandas es una librería de código abierto, con licencia BSD, que permite trabajar eficientemente analizando datos en Python.
A pandas se le da bien:
In [35]:
import pandas as pd
Un DataFrame es una estructura de 2 dimensiones con datos etiquetados en columnas. Los datos que componen un DataFrame pueden ser de distintos tipos. Piensa en un dataframe como si fuera una hoja de cáculo o una tabla SQL.
Se pueden crear a partir de:
Al crear un DataFrame, también se pueden especificar los índices (index, etiquetas para las filas) y las columnas. Si no se proporcionan estas etiquetas como argumentos pandas creará un DataFrame usando el sentido común.
En nuestro caso, leeremos todos los ficheros json y crearemos un DataFrame. Como tenemos información jerárquica en los ficheros json necesitamos normalizar los datos, pero pandas tiene funciones que lo hacen por nosotros.
In [36]:
json_to_dataframe = []
for json_file in json_db:
with open(json_file, 'r') as jf:
json_character = json.loads(''.join(jf.readlines()))
json_plain = pd.io.json.json_normalize(json_character)
json_to_dataframe.append(json_plain)
marvel_df = pd.concat(json_to_dataframe)
Podemos hacer esto en una super instrucción. Perdemos en legibilidad pero ganamos en molancia. ¡Totalmente desaconsejado!
In [37]:
df = pd.concat([pd.io.json.json_normalize(json.loads(''.join(open(json_file,'r').readlines()))) for json_file in json_db])
Podemos realizar operaciones lógica sobre todos los elementos de un DataFrame, son operaciones vectoriales. Esto acerlera los cálculos.
In [38]:
all(df == marvel_df)
Out[38]:
¿Y que pinta tiene un DataFrame?
In [39]:
marvel_df.head()
Out[39]:
Los DataFrames de pandas están implementados sobre numpy, de modo que si queremos saber la longitud que tiene un DataFrame es exactamente igual que en numpy.
In [40]:
marvel_df.shape
Out[40]:
Tenemos 89 columnas, es decir 89 campos que explorar sobre personajes de la Marvel, ¡Genial!
In [41]:
', '.join(marvel_df.columns.values)
Out[41]:
En realidad no deberíamos lanzar las campanas al vuelo porque spoiler muchos de los campos están vacios
In [42]:
marvel_df.dropna()
Out[42]:
Series es un array de 1 dimensión etiquetado. Como una tabla con una única columna. Puede almacenar cualquier tipo de datos:
Se etiquetan en función del índice, si por ejemplo, el índice que le pasamos son fechas se creará una instancia de TimeSerie.
Cuando se hace una selección de 1 columna en un DataFrame se crea una Serie.
Vamos a usar los creadores de comics para jugar un poco con las Series.
In [43]:
#Sacamos la lista de creadores que hay en nuestros datos
creators_serie = marvel_df['wiki.creators'].dropna()
creators_serie.describe()
Out[43]:
In [44]:
#Renombramos la serie y el índice
creators_serie.name = 'Creadores de personajes'
creators_serie.index.name = 'creators'
# Podemos usar head o como estamos sobre series también podemos coger una porción de la lista
# creators_serie.head()
creators_serie[:20]
Out[44]:
Vamos a eliminar todos aquellas filas en el DataFrame en las que el creador no exista, bien porque encontremos la cadena de error o bien porque el campo esté vacío.
In [45]:
default_string = creators_serie != "this has not been updated yet"
default_string.head()
Out[45]:
In [46]:
empty_string = creators_serie != ""
empty_string[:10]
Out[46]:
Ahora simplemente juntamos estas dos máscaras.
In [47]:
default_string and empty_string
A pesar de que la palabra reservada and exista también en pandas y pudiéramos pensar que funcionaría para unir series no es así, ya que la operación no se aplica elemento a elemento.
Sin embargo, pandas sabe que esto nos podría hacer falta y tenemos operadores que funcionan para elementos (& (and), | (or), ~(not))
In [49]:
creators_mask = default_string & empty_string
creators_mask[:10]
Out[49]:
In [50]:
creators_serie[creators_mask].head()
Out[50]:
Aquí ya tenemos buena parte de la información que queremos, pero vamos a separar los autores que trabajan juntos para poder contar cuantos personajes ha creado cada uno.
In [51]:
import re
creators = [re.split('&|and|,', line) for line in creators_serie[creators_mask]]
clean_creators = pd.Series([c.rstrip().lstrip() for creator in creators for c in creator])
clean_creators.head()
Out[51]:
In [52]:
clean_creators.value_counts()
Out[52]:
Esperábamos que Stan Lee ganara y además tenemos la impresión de que ha creado más de 9 personajes, sin querer hacer un feo a Chris Claremont.
Según nuestras fuentes (Wikipedia & ComicVine):
In [53]:
from IPython.display import Image
Image(filename='stanvschris.png')
Out[53]:
Obviamente es un problema de falta de datos. Por eso debemos ser muy cuidadosos con la confianza que tenemos en nuestros resultados. Un corpus con errores nos llevará a conclusiones erróneas, hay que ser conscientes de esto.
En la API Marvel no distingue entre personajes y equipos. Es decir, Los Vengadores tiene el mismo status de personaje que Rachel Grey, pero existe un campo en la wiki que nos permite diferenciar grupos de personajes: Former members. Intentaremos entonces filtrar para quedarnos sólo con los personajes.
Lo normal es que quisiéramos eliminar las filas que contienen nulos, y pandas tiene implementada una función para ello dropna. Pero lo que queremos es quedarnos con aquellas filas en cuya columna current_members tengamos un nulo, porque hemos comprobado que si no hay miembros es porque es un personaje.
In [54]:
marvel_df.dropna(subset=['wiki.current_members'])['name']
Out[54]:
In [55]:
%timeit (~marvel_df['wiki.current_members'].isnull())
import numpy as np
%timeit (np.invert(marvel_df['wiki.current_members'].isnull()))
In [56]:
not_groups_mask = marvel_df['wiki.current_members'].isnull()
not_groups_mask.head()
Out[56]:
In [57]:
marvel_df_characters = marvel_df[not_groups_mask]
marvel_df_characters.head()
Out[57]:
Se nos han colado The Watchers, éste es uno de los problemas del aprendizaje automático que los datos de entrada pueden contener errores, y el sistema que entrenemos debe ser capaz de generalizar suficiente como para sobreponerse a estos errores.
In [58]:
marvel_df_characters.shape
Out[58]:
Hemos comenzado con 1402 personajes, eliminando los equipos nos quedamos con 1332. Es decir, hemos perdido el 4.9929 % de los datos. Nada grave por ahora.
Un caso de estudio...
In [59]:
from IPython.display import Image
Image(filename='oracle.jpg')
Out[59]:
Por ejemplo, ¿qué encontramos en relación a la representación racial?
In [60]:
marvel_df_characters['wiki.skin'].dropna()
Out[60]:
Otro ejemplo: sería muy interesante saber quienes son los líderes de los grupos de superhéroes, pero...
In [61]:
marvel_groups = marvel_df.dropna(subset=['wiki.current_members'])
marvel_groups['wiki.leader'].dropna()
Out[61]:
Vamos a eliminar a todos aquellos personajes de los que no tenemos información. Sin datos no tenemos nada que analizar. A los científicos nos encantaría tener muchos datos disponibles, porque eso implicaría que podríamos hacer muchos experimentos y sacar conclusiones probablemente válidas. Lamentablemente la mayor parte del tiempo no podremos hacer machine learning sobre big data.
Vamos a querer quedarnos con la siguiente información:
In [62]:
# Agrupamos los datos para tener claro con que queremos trabajar
# No hay nadie con 'ocupation' así que lo quitamos
physical_data = {'wiki.hair':'hair', 'wiki.weight':'weight', 'wiki.height':'height', 'wiki.eyes':'eyes'}
cultural_data = {'wiki.education':'education', 'wiki.citizenship':'citizenship',
'wiki.place_of_birth':'place_of_birth', 'wiki.occupation':'occupation'}
personal_data = {'wiki.bio':'bio', 'wiki.bio_text':'bio', 'wiki.categories':'categories'}
marvelesque_data = {'wiki.abilities':'abilities', 'wiki.weapons':'weapons', 'wiki.powers': 'powers'}
data_keys = (list(physical_data.keys()) + list(cultural_data.keys()) +
list(personal_data.keys()) + ['name','comics.available'])
#+ marvelesque_data
In [63]:
print(data_keys)
In [64]:
clean_df = marvel_df_characters.dropna(subset = data_keys)
clean_df = clean_df[data_keys].set_index('name')
clean_df.shape
Out[64]:
In [65]:
clean_df[list(physical_data.keys())].head()
Out[65]:
In [66]:
clean_df[list(physical_data.keys())].describe()
Out[66]:
In [67]:
clean_df[list(cultural_data.keys())].head()
Out[67]:
¿Cómo diriáis que es físicamente el personaje típico de la marvel? (pandas lo sabe)
In [68]:
clean_df[list(cultural_data.keys())].describe()
Out[68]:
De modo que el personaje arquetípico de la Marvel tiene el pelo negro y los ojos azules, es de EE.UU. se dedica a ser aventurero.
¡Los datos son caros! Teníamos 1402, pero en realidad solo tenemos 762 personajes con datos para poder trabajar. Hemos perdido el 45.6491 % de los datos.
Exploremos el dataframe que nos ha quedado:
In [69]:
clean_df.dtypes
Out[69]:
In [70]:
clean_df.describe()
Out[70]:
In [71]:
clean_df[clean_df['comics.available'] == 2575.000000]
Out[71]:
¡Spiderman es el rey del cómic!
Antes de ponernos a jugar con los datos (más), tenemos una columna de la que se pude sacar mucho partido "wiki.categories"
In [72]:
clean_df['wiki.categories']
Out[72]:
A priori no tenemos información de que personajes son hombres, mujeres o alienígenas. Pero Marvel debió intuir que nos podría interesar el papel de las mujeres en los cómics y nos incluyó una categoría: "Mujeres", que nos va a facilitar la vida un montón. Vamos a crear dos nuevas columnas en el DataFrame:
In [73]:
women = clean_df['wiki.categories'].map(lambda x: 'Women' in x)
clean_df['Women'] = women
women[:5]
Out[73]:
In [74]:
# ~ Esto es una negación element-wise
print("Women: #{}, men #{}".format(clean_df[women].shape[0],clean_df[~women].shape[0]))
Es decir, tenemos 199 personajes femeninos y 563 masculinos. Es decir solo el 26% de los personajes son femeninos.
In [75]:
villain = clean_df['wiki.categories'].map(lambda x: 'Villains' in x)
clean_df['Villain'] = villain
In [76]:
men = ~women
gender_data = {'Women':{'Heroes':0,'Villains':0},'Men':{'Heroes':0,'Villains':0}}
# Women and villains
gender_data['Women']['Villains'] = clean_df[villain & women].shape[0]
# Women and heroes
gender_data['Women']['Heroes'] = clean_df[~villain & women].shape[0]
# Men and villains
gender_data['Men']['Villains'] = clean_df[villain & men].shape[0]
# Men and heroes
gender_data['Men']['Heroes'] = clean_df[~villain & men].shape[0]
gender_data
Out[76]:
In [77]:
%matplotlib inline
import matplotlib.pyplot as plt
In [78]:
n_groups = 2
men_data = (gender_data['Men']['Villains'], gender_data['Men']['Heroes'])
women_data = (gender_data['Women']['Villains'], gender_data['Women']['Heroes'])
fig, ax = plt.subplots()
index = np.arange(n_groups)
bar_width = 0.4
opacity = 0.5
rects1 = plt.bar(index, men_data, bar_width,
alpha=opacity,
color='b',
label='Hombres')
rects2 = plt.bar(index + bar_width, women_data, bar_width,
alpha=opacity,
color='r',
label='Mujeres')
plt.xlabel('Rol')
plt.ylabel('Número de personajes')
plt.title('Distribución por género y roles')
plt.xticks(index + bar_width, ('Villanos', 'Héroes'))
plt.legend(loc=0, borderaxespad=1.)
plt.show()
Otro caso de además de contrastar el número de villanos, es el de salir de dudas con respecto a una sospecha que tenemos. La cantidad de pelirrojas que hay en los cómics!
La ocurrencia del cabello rojo en la población es del 1-2% globalmente y del 2-6% en poblaciones con ascendencia del norte u oeste de Europa. Irlanda y Escocia destacan con un 10% y 13% de ocurrencia, respectivamente.
Veamos qué ocurre en Marvel.
In [79]:
red_heads = clean_df['wiki.hair'].map(lambda x: 'Red' in x)
clean_df['red_heads'] = red_heads
red_heads[:5]
Out[79]:
In [80]:
print("Red heads: #{}, Non-red heads #{}".format(clean_df[red_heads].shape[0],clean_df[~red_heads].shape[0]))
In [81]:
non_red = ~red_heads
hair_data = {'Women':{'Red heads':0,'Non-red heads':0},'Men':{'Red heads':0,'Non-red heads':0}}
# Red haired women
hair_data['Women']['Red heads'] = clean_df[red_heads & women].shape[0]
# Non-red haired women
hair_data['Women']['Non-red heads'] = clean_df[~red_heads & women].shape[0]
# Red haired men
hair_data['Men']['Red heads'] = clean_df[red_heads & men].shape[0]
# Non-red haired women
hair_data['Men']['Non-red heads'] = clean_df[~red_heads & men].shape[0]
hair_data
Out[81]:
In [1]:
#¿Qué es esto?
redwomen = 30 / 199.
redmen = 46 / 563.
print('Women: {0:5.2f}%, Men: {1:5.2f}%'.format(redwomen * 100, redmen * 100))
Es "simplemente" una serie de algoritmos que permiten que una máquina aprenda a partir de datos. Por lo tanto los ingredientes básicos para nuestra receta son datos + algoritmos.
Para hacer este cócktel necesitamos las habilidades de ingeniería informática, estadística y conociemiento del problema.
Y a partir del aprendizaje automático podremos:
In [83]:
import sys
import matplotlib
%matplotlib inline
import sklearn
print("Versión de Python: ", sys.version)
print("Versión de Pandas: ", pd.version.short_version)
print("Versión de Numpy: ", np.version.short_version)
print("Versión de Matplotlib: ", matplotlib.__version__)
print("Versión de Pandas: ", pd.version.short_version)
print("Versión de scikit-learn: ", sklearn.__version__)
Calcula la distancia a los vecinos de la muestra a etiquetar. Cada vecino (hasta N o K) vota.
Añadir nuevos datos no es gratis.
*Hipótesis*: Las características físicas diferencian a los personajes femeninos de los masculinos
In [84]:
clean_df['wiki.weight'].describe()
Out[84]:
In [85]:
physical = clean_df[clean_df['wiki.weight'] != "Unrevealed"]
In [86]:
any(physical['wiki.height'] == "Unrevealed")
Out[86]:
¡Genial! Al menos los que no tienen peso son los mismo que no tienen altura
In [87]:
physical_knn = physical[['wiki.weight', 'wiki.height', 'Women', 'Villain']]
In [88]:
physical_knn.dtypes
Out[88]:
Queremos que sean enteros
In [89]:
physical_knn
Out[89]:
In [90]:
physical_knn.applymap(str)
physical_knn = physical_knn[physical_knn['wiki.weight'].str.contains("lbs.")]
physical_knn = physical_knn[physical_knn['wiki.height'].str.contains('’|\'')]
In [91]:
def get_weight(pandas_weight):
""" Return first int parameter in a string """
for p in pandas_weight.split():
try:
return int(p)
except ValueError:
pass
In [92]:
physical_knn['wiki.weight'] = physical_knn['wiki.weight'].map(get_weight)
In [93]:
FOOT = 30.48
INCH = 2.54
def get_height(pandas_height):
""" Return first int parameter in a string """
height = None
for p in pandas_height.split():
colon_split = p.split('\'')
strange_colon_split = p.split('’')
if len(colon_split) == 2 :
height = colon_split
elif len(colon_split) == 4 :
height = colon_split[:2]
height[1] += "\'"
elif len(strange_colon_split) == 2 :
height = strange_colon_split
elif len((pandas_height.split()[-1]).split('\'')) == 2:
height = pandas_height.split()[-1].split('\'')
elif len((pandas_height.split()[-1]).split('’')) == 2:
height = pandas_height.split()[-1].split('’')
else:
universe_split = ((pandas_height.split(';')[0]).split()[-1]).split('\'')
if len(universe_split) == 2:
height = universe_split
else:
space_split = (pandas_height.split(';')[0].split()[-2:])
if space_split[0][-1] == '\'' or space_split[0][-1] == '’':
height = [space_split[0][:-1], space_split[1]]
else:
return None
if height:
try:
foot_part = int(height[0])
inch_part = int(height[1][:-1]) if height[1][:-1].strip() else 0
return (foot_part*FOOT + inch_part*INCH)
except ValueError:
pass
In [94]:
physical_knn['wiki.height'] = physical_knn['wiki.height'].map(get_height)
In [95]:
physical_knn = physical_knn.dropna()
In [96]:
physical_knn.dtypes
Out[96]:
In [97]:
physical_knn.shape
Out[97]:
¡Ahora ya podemos empezar a trabajar!
In [98]:
from math import floor
In [99]:
TRAIN_PERCENTAGE = 0.8
train_section = floor(physical_knn.shape[0]*TRAIN_PERCENTAGE)
test_section = physical_knn.shape[0]-train_section
print("Usaremos {} personajes para entrenar el clasificador y"\
" {} para probar el clasificador entrenado.".format(train_section, test_section))
In [100]:
train_rows = np.random.choice(physical_knn.index.values, train_section)
test_rows = np.setdiff1d(physical_knn.index.values,train_rows)
In [101]:
physical_knn.loc[train_rows[0]]
Out[101]:
Separamos datos y etiquetas
In [102]:
X_train = physical_knn.loc[train_rows][['wiki.weight','wiki.height']]
y_train = physical_knn.loc[train_rows]['Women']
X_test = physical_knn.loc[test_rows][['wiki.weight','wiki.height']]
y_test = physical_knn.loc[test_rows]['Women']
Vamos a echarle un vistazo a los datos para comprobar la complejidad de la tarea:
In [103]:
for i, group in physical_knn.groupby(women):
if not i:
ax = group.plot(kind='scatter', x='wiki.height', y='wiki.weight',
color='DarkBlue', label='Men');
else:
print(i)
group.plot(kind='scatter', x='wiki.height', y='wiki.weight',
color='DarkGreen', label='Women', ax=ax)
print(physical_knn.groupby(women).aggregate(np.mean))
Creamos una instancia del clasificador
In [104]:
from sklearn import neighbors
classifier = neighbors.KNeighborsClassifier()
In [105]:
classifier.fit(X_train, y_train)
Out[105]:
In [106]:
predict = classifier.predict(X_test)
predict
Out[106]:
In [107]:
from sklearn import metrics
accuracy = metrics.accuracy_score(y_test, predict)
precision, recall, f1, _ = metrics.precision_recall_fscore_support(y_test, predict)
print("* Acierto: {:.2f}%".format(accuracy*100))
print("* Precisión: {}\n* Exhaustividad: {}.\n* F1-Score: {}".format(accuracy*100, precision, recall, f1))
In [123]:
%%latex
\begin{align}
accuray = \frac{\text{# True Positives}+\text{# True Negatives}}
{\text{# True Positives}+\text{False Positives} + \text{False Negatives} + \text{True Negatives}}
\end{align}
In [124]:
%%latex
\begin{align}
precision = \frac{\text{# True Positives}} {\text{# True Positives}+\text{False Positives}}
\end{align}
In [125]:
from matplotlib.colors import ListedColormap
cmap_light = ListedColormap(['#AAAAFF', '#AAFFAA'])
cmap_bold = ListedColormap(['#0000FF', '#00FF00'])
step = 2
x_min, x_max = X_test['wiki.height'].min() - 1, X_test['wiki.height'].max() + 1
y_min, y_max = X_test['wiki.weight'].min() - 1, X_test['wiki.weight'].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, step),
np.arange(y_min, y_max, step))
prediction = classifier.predict(X_test)
Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
plt.scatter( X_test['wiki.height'], X_test['wiki.weight'], c=y_test, cmap=cmap_bold)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), 400)
Out[125]:
In [126]:
import time
for n in range(1,20, 2):
classifier = neighbors.KNeighborsClassifier(n_neighbors=n)
classifier.fit(X_train, y_train)
predict = classifier.predict(X_test)
accuracy = metrics.accuracy_score(y_test, predict)
print("({}) Acierto: {:.2f}%".format(n, accuracy*100))
*Hipótesis*: Las características sociales (nacionalidad y educación) diferencian a los personajes femeninos de los masculinos
In [127]:
cultural_knn = clean_df[['wiki.education', 'wiki.citizenship', 'Women', 'Villain']]
cultural_knn.dtypes
Out[127]:
In [128]:
usa = cultural_knn['wiki.citizenship'].map(lambda x: 'U.S.A.' in x)
cultural_knn['USA'] = usa
cultural_knn = cultural_knn.drop('wiki.citizenship',1)
In [129]:
cultural_knn
Out[129]:
In [130]:
def delete_without_education(cultural_knn, not_education):
for word in not_education:
cultural_knn = cultural_knn[~cultural_knn['wiki.education'].str.contains(word)]
return cultural_knn
In [131]:
#Eliminar todo los que sean "Unreveal"
cultural_knn = delete_without_education(cultural_knn, ["Unrevealed", "unrevealed", 'None', 'none', 'Not applicable',
'Unknown', 'unknown', 'Inapplicable', 'Limited'])
cultural_knn = cultural_knn[cultural_knn['wiki.education'] != '']
# Crear los grupos de niveles educativos
education = cultural_knn['wiki.education']
unfinished = education.map(lambda x: 'unfinished' in x or 'dropout' in x or 'incomplete' in x
or 'drop-out' in x or 'No official schooling' in x
or 'No formal education' in x or 'Unfinished' in x
or 'Incomplete' in x)
education[unfinished].tolist()
education = education[~unfinished]
phd = education.map(lambda x: 'Ph.D' in x or 'master' in x or 'Masters' in x or 'PhD' in x
or 'doctorate' in x or 'Doctorate' in x or 'Ph.d.' in x or 'Doctoral' in x
or 'NASA' in x or 'Journalism graduate' in x or 'scientist' in x
or 'Geneticist' in x or 'residency' in x)
education[phd].tolist()
education = education[~phd]
college = education.map(lambda x: 'College' in x or 'college' in x or 'University' in x
or 'post-graduate' in x or 'B.A' in x or 'B.S.' in x or 'university' in x
or 'Master' in x or 'Collage' in x or 'Degree' in x or 'degree' in x
or 'Engineering' in x or 'engineer' in x or 'programming' in x
or 'Programming' in x or 'Doctor' in x or 'Medical school' in x
or 'higher education' in x)
education[college].tolist()
education = education[~college]
militar = education.map(lambda x: 'Military' in x or 'Xandarian Nova Corps' in x or 'FBI' in x
or 'S.H.I.E.L.D.' in x or 'military' in x or 'Nicholas Fury' in x
or 'Warrior' in x or 'combat' in x or 'Combat' in x or 'Soldier' in x
or 'spy academy' in x or 'Police' in x or 'warfare' in x or 'Public Eye' in x)
education[militar].tolist()
education = education[~militar]
hs = education.map(lambda x: 'High school' in x or 'high school' in x or 'High-school' in x
or 'High School' in x or 'high School' in x)
education[hs].tolist()
education = education[~hs]
tutored = education.map(lambda x: 'Tutored' in x or 'tutors' in x or 'tutored' in x
or 'Mentored' in x or 'Home schooled' in x or 'Private education' in x)
education[tutored].tolist()
education = education[~tutored]
autodidacta = education.map(lambda x: 'Self-taught' in x or 'self-taught' in x
or 'Little or no formal schooling' in x or 'Little formal schooling' in x
or 'Some acting school' in x or 'through observation' in x)
education[autodidacta].tolist()
education = education[~autodidacta]
special = education.map(lambda x: 'Sorcery' in x or 'cosmic experience' in x or 'magic' in x
or 'Priests of Pama' in x or 'Xavier Institute' in x or 'Carlos Javier’s' in x
or 'Self educated' in x or 'Shao-Lom' in x or 'Centuries of study and experience' in x
or 'Askani' in x or 'Madame DuPont' in x or 'Titanian' in x or 'arcane arts' in x
or 'Muir-MacTaggert' in x or 'Uploaded data' in x or 'Programmed' in x
or 'Accelerated' in x or 'Inhumans' in x or 'Able to access knowledge' in x
or 'lifetime' in x or 'Watchers\' homeworld' in x or 'Uranian Eternals' in x
or 'Arcturus' in x or 'Oatridge School for Boys' in x)
education[special].tolist()
education = education[~special]
basic = education.map(lambda x: 'Self-taught' in x or 'Homed schooled' in x or 'graduate school' in x
or 'Elementary school' in x or 'Secondary school' in x or 'school graduate' in x
or 'Boarding school' in x or 'Massachusetts Academy' in x
or 'school graduate' in x)
education[basic].tolist()
education = education[~basic]
In [132]:
educational_dict = {'autodidacta': autodidacta, 'unfinished': unfinished, 'superior': phd, 'college':college,
'militar': militar, 'high school':hs, 'tutored': tutored, 'special':special, 'basic': basic}
numeric = {'autodidacta': 1, 'unfinished': 2, 'superior': 3, 'college':4,
'militar': 5, 'high school':6, 'tutored': 7, 'special':8, 'basic': 9}
In [133]:
def clean_education_levels(educational_dict, cultural_knn):
""" It will use our new categories in the wiki.education column"""
for k, education in educational_dict.items():
index = education[education.loc[:]].index
for character in index:
cultural_knn.loc[character, 'wiki.education'] = numeric[k]
In [134]:
clean_education_levels(educational_dict, cultural_knn)
In [135]:
TRAIN_PERCENTAGE = 0.8
train_section = floor(cultural_knn.shape[0]*TRAIN_PERCENTAGE)
test_section = cultural_knn.shape[0]-train_section
print("Usaremos {} personajes para entrenar el clasificador y"\
" {} para probar el clasificador entrenado.\n".format(train_section, test_section))
train_rows = np.random.choice(cultural_knn.index.values, train_section)
test_rows = np.setdiff1d(cultural_knn.index.values,train_rows)
print(cultural_knn.loc[train_rows[0]])
In [150]:
for i, group in cultural_knn.groupby(women):
print(group)
In [168]:
for i, group in cultural_knn.groupby(women):
if not i:
area = (np.pi * (group.shape[0])**2)*.002
ax = group.plot(kind='scatter', x='wiki.education', y='USA', s=area,
color='Cornflowerblue', label='Men', alpha=0.5);
else:
area = (np.pi * (group.shape[0])**2)*.002
group.plot(kind='scatter', x='wiki.education', y='USA',
color='LightGreen', label='Women', ax=ax, s=area, alpha=0.5)
ax.set_xticks(range(1,10))
ax.set_xticklabels(list(numeric.keys()), rotation='vertical')
ax.set_yticks(range(0,2))
ax.set_yticklabels(['USA', 'non USA'], rotation='horizontal')
ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=2, fancybox=True, shadow=True)
Out[168]:
In [108]:
X_train = cultural_knn.loc[train_rows][['wiki.education','USA']]
y_train = cultural_knn.loc[train_rows]['Women']
X_test = cultural_knn.loc[test_rows][['wiki.education','USA']]
y_test = cultural_knn.loc[test_rows]['Women']
classifier = neighbors.KNeighborsClassifier()
classifier.fit(X_train, y_train)
predict = classifier.predict(X_test)
accuracy = metrics.accuracy_score(y_test, predict)
precision, recall, f1, _ = metrics.precision_recall_fscore_support(y_test, predict)
print("* Acierto: {:.2f}%".format(accuracy*100))
print("* Precisión: {}\n* Exhaustividad: {}.\n* F1-Score: {}".format(accuracy*100, precision, recall, f1))
In [127]:
cmap_light = ListedColormap(['#AAAAFF', '#AAFFAA'])
cmap_bold = ListedColormap(['#0000FF', '#00FF00'])
step = 1
xx, yy = np.meshgrid(np.arange(1, 10, step),
np.arange(0, 1, step))
prediction = classifier.predict(X_test)
Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
plt.scatter( X_test['wiki.education'], X_test['USA'], c=y_test, cmap=cmap_bold)
plt.xlim(xx.min(), xx.max())
plt.yticks(range(0,2),['USA', 'non USA'], rotation='horizontal')
plt.ylim(-0.5, 1.5)
plt.xticks(range(1,11), list(numeric.keys()), rotation='vertical')
plt.xlabel("Education")
Out[127]:
In [128]:
%pylab inline --no-import-all
pd.set_option('display.mpl_style', 'default')
figsize(15, 6)
pd.set_option('display.line_width', 4000)
pd.set_option('display.max_columns', 100)
from matplotlib.pyplot import *
In [132]:
marvel_df['modified'] = pd.to_datetime(marvel_df['modified'])
In [134]:
plot(marvel_df['modified'])
Out[134]:
In [135]:
start = marvel_df.modified.min()
end = marvel_df.modified.max()
yearly_range = pd.date_range(start, end, freq='365D6H')
In [137]:
marvel_df[['modified']].head()
Out[137]:
In [139]:
characters_per_year = marvel_df.groupby(marvel_df['modified'].map(lambda x: x.year)).size()
characters_per_year
Out[139]:
In [140]:
characters_per_year.plot()
Out[140]:
In [141]:
marvel_df.sort('modified',ascending=False).head()
Out[141]: