English Resources – Women Who Code Bogotá

We are going to share a series of resources to help English Learning. Here we will share them.

Attend Women Who Code Bogotá English Language Exchange every month, you can join and register to attend HERE

Advertisements

Datos Geo localizados con Python Folium

Visualización de Datos Geo-localizados con Python Folium, sobre Open Street Maps.  Puedes consultar el código empleado en GitHub

Este fue el último Webinar del año de Women Who Code Medellín. Para 2019 planeamos volver a los Meetup presenciales en Medellín y realizar algunas actividades en Bogotá.  Estén atentas a los eventos en Meetup, pueden seguirnos en Facebook e Instagram.

Código basado en An Introduction to Geo Data Science with Python con algunos cambios.

Limpieza de datos básica con Python

Por: Isabel Yepes

Los conjuntos de datos que podemos obtener no siempre cuentan con el formato o completitud necesarios para ser analizados apropiadamente.  El proceso de limpieza consiste en eliminar o reemplazar elementos de un conjunto de datos de forma que afecten lo menos posible los resultados finales. Usaremos la librería Pandas de Python para realizar el proceso de limpieza de datos.

Tomaremos como referencia el código presentado en Developer Intelligence, dado que el dataset que proponen no está disponible, hemos ubicado uno similar en Kaggle, para descargarlo se debe crear una cuenta usando google o facebook. El dataset se llama iMDB 5000 Movie Dataset y contiene información sobre películas, sus rankings, fecha de estreno, título, país, entre otros datos que completan 28 columnas, en un archivo llamado “movie_metadata.csv”.

Screenshot 2018-09-15 17.38.47

Podemos observar inspeccionando el archivo descargado que algunas filas tienen valores faltantes, tanto numéricos como texto.

Desde Python3 primero importaremos la librería pandas que ya debe estar instalada, y luego los datos indicando que la columna title_year que contiene el año de estreno sea tipo string.

>>>import pandas as pd
>>>data = pd.read_csv("movie_metadata.csv", dtype={"title_year": str})

Si desplegamos la columna title_year encontraremos que efectivamente se trata como un string sin punto decimal al final y en los campos donde no hay valores aparece NaN

>>>data["title_year"]

Screenshot 2018-09-15 17.48.17

Podemos eliminar las filas que no tengan valor asignado en la columna title_year del siguiente modo

>>>data = data.dropna(subset=["title_year"])

Después de esta operación vemos como las filas se reducen de 5043 a 4935. Sin embargo tengamos en cuenta que en la nueva matriz los índices no se renumeran, simplemente quedan suprimidos los índices de las filas eliminadas.

Screenshot 2018-09-15 17.57.41

Si quisiéramos eliminar todas las filas a las cuales les faltase un valor usaríamos data.dropna() para eliminar solo las filas con todos los valores faltantes usaríamos data.dropna(how=’all’) y para eliminar las filas que superen un número de valores faltantes (por ejemplo dos o más) usaríamos data.dropna(thresh=2)

Para el caso de la duración podríamos sacar estadísticas de dicha columna numérica, para ello usamos el siguiente comando.

>>>data.duration.describe()
count    4923.000000
mean      108.167378
std        22.541217
min         7.000000
25%        94.000000
50%       104.000000
75%       118.000000
max       330.000000
Name: duration, dtype: float64

Estos resultados incluyen las filas que son cero que desvían los resultados, un modo de limpiar los datos es reemplazarlas por el valor promedio de las filas restantes (sin ceros) así:

>>>data.duration = data.duration.fillna(data.duration.mean())

Si buscamos en la columna los valores que antes eran cero por rangos, podremos ver lo siguiente.

>>> data.duration[190:200]
192    101.000000
193    138.000000
194    107.000000
195    142.000000
196    165.000000
197    100.000000
198     82.000000
199    108.167378
200     98.000000
201     95.000000
Name: duration, dtype: float64

Hagamos una anotación, dado que primero se suprimieron las filas con el año de estreno vacío y luego se calculó el promedio de duración, el valor de promedio podría verse alterado por los datos suprimidos, en el video al final de este post podrás ver que al hacerlo en orden inverso hay una ligera variación del promedio.

Podemos observar los tipos de datos de todas las columnas así:

>>> data.dtypes
color                         object
director_name                 object
num_critic_for_reviews       float64
duration                       int32
director_facebook_likes      float64

Si no es relevante conservar los decimales del promedio, podemos convertir esta columna de formato flotante en entera del siguiente modo.

>>> data.duration = pd.Series(data["duration"], dtype="int32")

Al visualizar el mismo rango ya no tendrá los decimales por tratarse de valores enteros.

>>> data.duration[190:200]
192    101
193    138
194    107
195    142
196    165
197    100
198     82
199    108
200     98
201     95
Name: duration, dtype: int32

Podríamos hacer lo opuesto, convertir un valor numérico en texto, lo cual se logra del siguiente modo, lo haremos sobre un nuevo dataframe porque para nuestros datos no requerimos esa transformación.

>>>data2 = data.duration.astype(str)
>>> data2[:10]
0       178
1       169
2       148
3       164
5       132
6       156
7       100
8       141
9       153
10      183
Name: duration, dtype: object

En algunos casos es mejor reemplazar el indicador de dato faltante NaN por un texto vacío o con un texto más indicativo como “Not Known”, por ejemplo en la columna content_rating.

>>>data.content_rating = data.content_rating.fillna("Not Known")
>>> data.content_rating[96:100]
97         PG-13
98     Not Known
99         PG-13
100        PG-13
Name: content_rating, dtype: object

Podemos renombrar columnas para que tengan nombres más intuitivos

>>>data = data.rename(columns = {"title_year":"release_date", "movie_facebook_likes":"facebook_likes"})

A partir de esto podremos acceder a las columnas con sus nuevos nombres

Para cambiar a mayúsculas una columna y eliminar los espacios al final usamos str.upper() y str.strip() respectivamente.

>>> data.movie_title = data["movie_title"].str.upper()
>>> data.movie_title = data["movie_title"].str.strip()
>>> data.movie_title[:10]
0 AVATAR
1 PIRATES OF THE CARIBBEAN: AT WORLD'S END
2 SPECTRE
3 THE DARK KNIGHT RISES
5 JOHN CARTER
6 SPIDER-MAN 3
7 TANGLED
8 AVENGERS: AGE OF ULTRON
9 HARRY POTTER AND THE HALF-BLOOD PRINCE
10 BATMAN V SUPERMAN: DAWN OF JUSTICE

Una vez terminamos exportamos el resultado a un nuevo archivo .csv pudiendo especificar el tipo de codificación, para el caso UTF-8

data.to_csv("cleanfile.csv", encoding="utf-8")

El resultado final nos da un archivo con el formato deseado y sin faltantes en las columnas de interés.

Screenshot 2018-09-15 17.36.00

Si tenemos que realizar el mismo proceso con muchos archivos generaremos un script con el proceso de transformación ya probado, de modo que podamos ejecutarlo cuantas veces lo necesitemos.

El siguiente video explica de forma detallada el proceso antes descrito.

Análisis de palabras frecuentes usando Python

Por: Isabel Yepes

En esta publicación mostramos como hacer un análisis de palabras simple denominado WordCloud, que muestra una imagen con las palabras más frecuentes en un texto, dándoles relevancia por tamaño. Usaremos como fuente textual los tweets de una determinada cuenta, que han sido previamente descargados en csv.

El código aquí presentado se basa en el artículo de DataSciencePlus Twitter Analysis with Python

Para comenzar debemos instalar las librerías pre-requisitos

$pip3 install pandas
$pip3 install numpy
$pip3 install Matplotlib
$pip3 install WordCloud
$pip3 install NLTK

Una vez instalado nltk debemos descargar los diccionarios de palabras que necesitamos, para este caso las stopwords, que son las palabras conectoras que repetimos con frecuencia en un idioma, por ejemplo pronombres como el, la, los, e.o.

En el caso de OSX la descarga requiere que la fuente tenga un certificado instalado, por lo cual debemos cumplir este requisito antes de hacer la descarga, ingresando a la ruta de la aplicación e instalando el certificado

$cd /Applications/Python\ 3.7/
$sudo install ./Install\ Certificates.command

Realizar la descarga

$python3
>>>import nltk
>>>nltk.download

Seleccionar del corpora de nltk las stopwords

Seleccionar del Corpora de nltk las stopwords

Debemos contar ya con un archivo con información de twitter descargada, puedes tomar como referencia las instrucciones de Como descargar tweets a .csv usando Python teniendo en cuenta que la forma de crear credenciales de descarga para twitter cambió y ahora debes aplicar por una cuenta de desarrollador antes de crear la aplicación.  El video al final de este post habla un poco de ello. En este enlace de Twitter Developer puedes comenzar ese proceso.

El siguiente script contempla que tienes dos archivos, uno con los 400 últimos tweets llamado sample_tweets-400.csv y otro con los últimos 3240 llamado sample_tweets.csv. Este script también puede encontrarse en el siguiente repositorio, con el nombre wordcloud.py https://github.com/WomenWhoCode/WWCodeMedellin

import numpy as np
import pandas as pd
import re

#Visualización
import matplotlib.pyplot as plt
import matplotlib
from wordcloud import WordCloud, STOPWORDS

#nltk librería de análisis de lenguaje
import nltk
#Este proceso puede hacerse antes de forma manual, descargar las stopwords de la librerñia nltk
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words_sp = set(stopwords.words('spanish'))
stop_words_en = set(stopwords.words('english'))
#Concatenar las stopwords aplicándose a una cuenta que genera contenido en inglés y español
stop_words = stop_words_sp | stop_words_en
from nltk import tokenize

matplotlib.style.use('ggplot')
pd.options.mode.chained_assignment = None

#Últimos 400 tweets previamente descargados
tweets = pd.read_csv('sample_tweets-400.csv')
#Últimos 3240 tweets previamente descargados
tweets2 = pd.read_csv('sample_tweets.csv')

def wordcloud(tweets,col,idgraf):
	#Crear la imagen con las palabras más frecuentes
    wordcloud = WordCloud(background_color="white",stopwords=stop_words,random_state = 2016).generate(" ".join([i for i in tweets[col]]))
    #Preparar la figura
    plt.figure(num=idgraf, figsize=(20,10), facecolor='k')
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.title("Good Morning Datascience+")


def tweetprocess(tweets,idgraf):
	#Monitorear que ha ingresado a procesar el gráfico
	print(idgraf)
	#Imprimir un tweet que sepamos contenga RT @, handles y puntuación para ver su eliminación
	print(tweets['text'][3])
	tweets['tweetos'] = '' 

	#add tweetos first part
	for i in range(len(tweets['text'])):
	    try:
	        tweets['tweetos'][i] = tweets['text'].str.split(' ')[i][0]
	    except AttributeError:    
	        tweets['tweetos'][i] = 'other'

	#Prepocesar tweets con 'RT @'
	for i in range(len(tweets['text'])):
	    if tweets['tweetos'].str.contains('@')[i]  == False:
	        tweets['tweetos'][i] = 'other'
	        
	# Remover URLs, RTs, y twitter handles
	for i in range(len(tweets['text'])):
	    tweets['text'][i] = " ".join([word for word in tweets['text'][i].split()
	                                if 'http' not in word and '@' not in word and '<' not in word and 'RT' not in word])
	#Monitorear que se removieron las menciones y URLs
	print("------Después de remover menciones y URLs --------")
	print(tweets['text'][3])

	#Remover puntuación, se agregan símbolos del español
	tweets['text'] = tweets['text'].apply(lambda x: re.sub('[¡!@#$:).;,¿?&]', '', x.lower()))
	tweets['text'] = tweets['text'].apply(lambda x: re.sub('  ', ' ', x))
	#Monitorear que se removió la puntuación y queda en minúsculas
	print("------Después de remover signos de puntuación y pasar a minúsculas--------")
	print(tweets['text'][3])
	#hacer el análisis de WordCloud
	wordcloud(tweets,'text',idgraf)

#Graficar tendencia 400 tweets
tweetprocess(tweets,100)
#Graficar tendencia 3240 tweets
tweetprocess(tweets2,200)
plt.show()

La salida generada son dos imágenes que muestran la tendencia de los últimos 400 tweets y los últimos 3240

Word Cloud para los últimos 400 tweets
Word Cloud para los últimos 400 tweets

 

Word Cloud para los últimos 3240 tweets
Word Cloud para los últimos 3240 tweets

Aquí la explicación en video del código empleado

Análisis de Sentimientos sobre Twitter usando la librería TextBlob de Python

Por: Isabel Yepes

El análisis de sentimiento utiliza técnicas de procesamiento de lenguaje natural (NLP) para obtener conclusiones sobre textos producidos por personas y analizar en ellos rasgos de interés asociados a emociones positivas o negativas.  Se requiere un modelo que ya haya sido entrenado con textos que nos permita obtener valores cuantificables.

Hoy nos apoyaremos en la guía de Free Code Camp “Basic data analysis on Twitter with Python” para hacer Análisis de Sentimientos sobre Twitter usando la librería TextBlob de Python que dispone de modelos de NLP para diversos usos.

Puedes ver este ejemplo en GitHub

Primero debemos tener instalada la librería tweepy.

#pip3 install tweepy

Si estás usando Python 3.7 tendrás un error al tratar de usar la librería que todavía no ha sido corregido en la última versión disponible (hoy, eso puede variar en poco tiempo) para resolverlo usamos la siguiente recomendación de StackOverFlow para reemplazar una palabra reservada Async en el archivo streaming.py de la librería.

La ubicación del archivo varía según tu sistema operativo, en Mac estará ubicado en /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tweepy/streaming.py edítala con un editor de texto plano y reemplaza todas las ocurrencias de async por async_ grabas y listo. Versiones anteriores a 3.7 de Python no requieren este cambio.

Previamente debemos tener la librería NLTK instalada, las instrucciones para hacerlo puedes verlas AQUÍ, para actualizarla si ya la tienes instalada

#python3
>>>import nltk
>>>python3 nltk.download
>>>quit()

Procedemos a clonar la librería TextBlob, realizar su instalación y descargar el corpus asociado sobre nltk

#git clone https://github.com/sloria/textblob
#cd textblob
#python3 setup.py install
#python3 -m textblob.download_corpora

Si quieres saber más sobre la librería puede visitar su sitio en GitHub, tiene más herramientas de análisis para texto en Inglés, si deseamos análisis en español es necesario utilizar otra diferente https://github.com/sloria/textblob Dentro del script analizaremos los tweets cuya propiedad “lang” = “en” es decir aquellos cuyo lenguaje fue identificado como inglés. Para otras propiedades del tweet pueden consultar la documentación de Twitter.

Tendremos las claves de aplicación de Twitter en un archivo separado, de modo que no queden en el mismo código que estamos empleando y puedan reusarse en otros scripts, llamaremos a este script de claves twkeys.py

Recientemente Twitter cambió su forma de usar credenciales y ahora debe aplicarse por una cuenta de desarrollador, el proceso para aplicar puedes verlo en https://apps.twitter.com

#Credenciales del Twitter API
def consumer_key():
	#API Key
	return "Add Consumer Key here"

def consumer_secret():
	#API Secret
	return "Add Consumer Secret here"

def access_key():
	#Access Key
	return "Add Access Token here"

def access_secret():
	#Access Secret
	return "Add Access Token Secret here"

Este es el código que usamos para realizar el análisis, el resultado nos mostrará las gráficas de dispersión de dos cuentas, el promedio simple y promedio ponderado de sentimiento de ambas.

#Importar consumer API de Twitter https://github.com/tweepy/tweepy
import tweepy
#importar las credenciales de Twitter de un script
import twkeys
#Importar librería para Sentiment Analysis
from textblob import TextBlob
from time import sleep
from datetime import datetime
#Importar para graficar los Datos
#Para Jupyter notebooks quitar el comentario a la siguiente línea
#%matplotlib inline
import matplotlib.pyplot as plt
#Importar para calcular promedio ponderado
import numpy as np

#Credenciales del Twitter API que están el el script twkeys.py
consumer_key = twkeys.consumer_key()
consumer_secret = twkeys.consumer_secret()
access_key = twkeys.access_key()
access_secret = twkeys.access_secret()

def get_all_tweets(screen_name,graph_id):
    #Este método solo tiene permitido descargar máximo los ultimos 3240 tweets del usuario
    #Especificar aquí durante las pruebas un número entre 200 y 3240
    limit_number =  3240
    
    #autorizar twitter, inicializar tweepy
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_key, access_secret)
    api = tweepy.API(auth)
    
    #inicializar una list to para almacenar los Tweets descargados por tweepy
    alltweets = []    
    
    #Hacer una petición inicial por los 200 tweets más recientes (200 es el número máximo permitido)
    new_tweets = api.user_timeline(screen_name = screen_name,count=200)
    
    #guardar los tweets más recientes
    alltweets.extend(new_tweets)
    
    #guardar el ID del tweet más antiguo menos 1
    oldest = alltweets[-1].id - 1
    
    #recorrer todos los tweets en la cola hasta que no queden más
    while len(new_tweets) > 0 and len(alltweets) <= limit_number:
        print ("getting tweets before" + str(oldest))
        
        #en todas las peticiones siguientes usar el parámetro max_id para evitar duplicados
        new_tweets = api.user_timeline(screen_name = screen_name,count=200,max_id=oldest)
        
        #guardar los tweets descargados
        alltweets.extend(new_tweets)
        
        #actualizar el ID del tweet más antiguo menos 1
        oldest = alltweets[-1].id - 1
        
        #informar en la consola como vamos
        print (str(len(alltweets)) + " tweets descargados hasta el momento")
    
    #Realizar el análisis de sentimiento de los tweets descargados

    #Crear las listas de polaridad polarity_list y frecuencia de polaridad numbers_list
    polarity_list = []
    numbers_list = []
    number = 1

    for tweet in alltweets:
        if tweet.lang == "en":
            try:
                analysis = TextBlob(tweet.text)
                analysis = analysis.sentiment
                #Guardar la polaridad
                polarity = analysis.polarity
                polarity_list.append(polarity)
                #Contar las veces que esa polaridad ha ocurrido
                numbers_list.append(number)
                number = number + 1
            except tweepy.TweepError as e:
                print(e.reason)
            except StopIteration:
                break

    #Crear eje cartesiano
    plt.figure(graph_id)
    axes = plt.gca()
    axes.set_ylim([-1, 3])
    plt.scatter(numbers_list, polarity_list)
    
    #Calcular el promedio de polaridad, NOTA: No es promedio ponderado
    averagePolarity = (sum(polarity_list))/(len(polarity_list))
    averagePolarity = "{0:.0f}%".format(averagePolarity * 100)
    time  = datetime.now().strftime("At: %H:%M\nOn: %m-%d-%y")

    #Calcular el promedio ponderado
    weighted_avgPolarity = np.average(polarity_list, weights=numbers_list)
    weighted_avgPolarity = "{0:.0f}%".format(weighted_avgPolarity * 100)

    #Agregar texto con el promedio de sentimiento
    plt.text(10, 1.5, "Average Sentiment:  " + str(averagePolarity) + "\n" + " Weighted Average Sentiment:  " + str(weighted_avgPolarity) + "\n" + time, fontsize=12, bbox = dict(facecolor='none', edgecolor='black', boxstyle='square, pad = 1'))

    #Título
    plt.title("Sentiment of " + screen_name + " on Twitter")
    plt.xlabel("Number of Tweets")
    plt.ylabel("Sentiment")       
    pass

if __name__ == '__main__':
    #especificar el nombre de usuario de la cuenta a la cual se descargarán los tweets
    get_all_tweets("Add_account_1",200)
    get_all_tweets("Add_account_2",300)
    #Mostrar las gráfica
    plt.show()

El siguiente video explica todo el proceso.