Análisis de datos de API con MongoDB, Seaborn y Matplotlib

Una habilidad comúnmente solicitada para puestos de desarrollo de software es la experiencia con bases de datos NoSQL, incluido MongoDB. Este tutorial explorará la recopilación de datos...

Introducción

Una habilidad comúnmente solicitada para puestos de desarrollo de software es la experiencia con bases de datos NoSQL, incluido MongoDB. Este tutorial explorará la recopilación de datos mediante una API, su almacenamiento en una base de datos MongoDB y el análisis de los datos.

Sin embargo, antes de saltar al código, tomemos un momento para repasar MongoDB y las API, para asegurarnos de que entendemos cómo manejaremos los datos que recopilamos.

MongoDB y NoSQL

MongoDB es una forma de base de datos NoSQL, que permite el almacenamiento de datos en formas no relacionales. Las bases de datos NoSQL se entienden mejor comparándolas con sus progenitores/rivales: las bases de datos SQL.

SQL significa Structure Query Language y es un tipo de herramienta de gestión de bases de datos relacionales. Una base de datos relacional es una base de datos que almacena datos como una serie de claves y valores, y cada fila de una tabla de datos tiene su propia clave única. Los valores en la base de datos se pueden recuperar buscando la clave correspondiente. Así es como las bases de datos SQL almacenan datos, pero las bases de datos NoSQL pueden almacenar datos de forma no relacional.

NoSQL significa "No solo SQL", que se refiere al hecho de que, aunque las consultas al estilo SQL se pueden realizar con sistemas NoSQL, también pueden hacer cosas con las que luchan las bases de datos SQL. Las bases de datos NoSQL tienen una gama más amplia de opciones de almacenamiento para los datos que manejan y, debido a que los datos están relacionados de forma menos rígida, se pueden recuperar de más formas, lo que hace que algunas operaciones sean más rápidas. Las bases de datos NoSQL pueden simplificar la adición de nodos o campos en comparación con las bases de datos SQL.

Hay muchos marcos NoSQL populares, incluidos [MongoDB] (https://www.mongodb.com/), [OrientDB] (https://orientdb.com/), [InfinityDB] (https://boilerbay.com/ ), Aerospike y Cosmos DB. MongoDB es un marco NoSQL específico que almacena datos en forma de documentos, actuando como una base de datos orientada a documentos.

MongoDB es popular debido a su versatilidad y fácil integración en la nube, y puede usarse para una amplia variedad de tareas. MongoDB almacena datos usando el formato JSON. Las consultas de las bases de datos de MongoDB también se realizan en formato JSON y, dado que los comandos de almacenamiento y recuperación se basan en el formato JSON, es fácil recordar y redactar comandos para MongoDB.

¿Qué son las API?

Las APIs son Interfaces de Programación de Aplicaciones, y su función es facilitar las comunicaciones entre clientes y servidores. Las API a menudo se crean para facilitar la recopilación de información por parte de aquellos que tienen menos experiencia con el lenguaje utilizado por los desarrolladores de la aplicación.

Las API también pueden ser métodos útiles para controlar el flujo de información de un servidor, alentando a los interesados ​​en acceder a su información a utilizar los canales oficiales para hacerlo, en lugar de construir un web scraper. Las API más comunes para sitios web son las API REST (Representational State Transfer), que utilizan solicitudes y respuestas HTTP estándar para enviar, recibir, eliminar y modificar datos. Accederemos a una API REST y realizaremos nuestras solicitudes en formato HTTP para este tutorial.

¿Qué API usaremos?

La API que usaremos es la API de GameSpot. GameSpot es uno de los sitios de reseñas de videojuegos más importantes de la web y se puede acceder a su API aquí.

Cómo configurar

Antes de comenzar, debe asegurarse de obtener una clave API para GameSpot. También debe asegurarse de tener instalado MongoDB y su biblioteca de Python. Las instrucciones de instalación de Mongo se pueden encontrar aquí.

La biblioteca PyMongo se puede instalar simplemente ejecutando:

1
$ pip install pymongo

También puede instalar el programa Brújula MongoDB, que le permite visualizar y editar fácilmente aspectos de las bases de datos MongoDB con una GUI.

Creando la base de datos MongoDB

Ahora podemos comenzar nuestro proyecto creando la base de datos MongoDB. Primero, manejaremos nuestras importaciones. Importaremos MongoClient desde PyMongo, así como requests y pandas:

1
2
3
from pymongo import MongoClient
import requests
import pandas as pd

Al crear una base de datos con MongoDB, primero debemos conectarnos al cliente y luego usar el cliente para crear la base de datos que deseamos:

1
2
3
4
5
client = MongoClient('127.0.0.1', 27017)
db_name = 'gamespot_reviews'

# connect to the database
db = client[db_name]

MongoDB puede almacenar múltiples colecciones de datos dentro de una sola base de datos, por lo que también debemos definir el nombre de la colección que queremos usar:

1
2
# open the specific collection
reviews = db.reviews

Eso es todo. Nuestra base de datos y nuestra colección han sido creadas y estamos listos para comenzar a insertar datos en ella. Eso fue bastante simple, ¿no?

Uso de la API

Ahora estamos listos para usar la API de GameSpot para recopilar datos. Echando un vistazo a la documentación de la API aquí, podemos determinar el formato en el que deben estar nuestras solicitudes.

Necesitamos hacer nuestras solicitudes a una URL base que contenga nuestra clave API. La API de GameSpot tiene varios recursos propios de los que podemos extraer datos. Por ejemplo, tienen un recurso que enumera datos sobre juegos como la fecha de lanzamiento y las consolas.

Sin embargo, estamos interesados ​​en su recurso para reseñas de juegos y extraeremos algunos campos específicos del recurso API. Además, GameSpot le pide que especifique un identificador único de agente de usuario al realizar solicitudes, lo que haremos creando un encabezado que pasaremos en la función requests:

1
2
3
4
5
headers = {
    "user_agent": "[YOUR IDENTIFIER] API Access"
}

games_base = "http://www.gamespot.com/api/reviews/?api_key=[YOUR API KEY HERE]&format=json"

Querremos los siguientes campos de datos: id, title, score, deck, body, good, bad:

1
review_fields = "id,title,score,deck,body,good,bad"

GameSpot solo permite la devolución de 100 resultados a la vez. Por esta razón, para obtener una cantidad decente de revisiones para analizar, necesitaremos crear un rango de números y recorrerlos, recuperando 100 resultados a la vez.

Puede seleccionar cualquier número que desee. Elegí obtener todas sus reseñas, que alcanzan un máximo de 14,900:

1
2
pages = list(range(0, 14900))
pages_list = pages[0:14900:100]

Vamos a crear una función que una la URL base, la lista de campos que queremos devolver, un esquema de clasificación (ascendente o descendente) y el desplazamiento de la consulta.

Tomaremos la cantidad de páginas que queremos recorrer y luego, por cada 100 entradas, crearemos una nueva URL y solicitaremos los datos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def get_games(url_base, num_pages, fields, collection):

    field_list = "&field_list=" + fields + "&sort=score:desc" + "&offset="

    for page in num_pages:
        url = url_base + field_list + str(page)
        print(url)
        response = requests.get(url, headers=headers).json()
        print(response)
        video_games = response['results']
        for i in video_games:
            collection.insert_one(i)
            print("Data Inserted")

Recuerde que MongoDB almacena datos como JSON. Por esa razón, necesitamos convertir nuestros datos de respuesta al formato JSON usando el método json().

Después de que los datos se hayan convertido a JSON, obtendremos la propiedad "resultados" de la respuesta, ya que esta es la parte que realmente contiene nuestros datos de interés. Luego revisaremos los 100 resultados diferentes e insertaremos cada uno de ellos en nuestra colección usando el comando insert_one() de PyMongo. También podría ponerlos todos en una lista y usar insert_many() en su lugar.

Ahora llamemos a la función y hagamos que recopile los datos:

1
get_games(review_base, pages_list, review_fields, reviews)

¿Por qué no verificamos que nuestros datos se hayan insertado en nuestra base de datos como esperábamos? Podemos visualizar la base de datos y su contenido directamente con el programa Compass:

Podemos ver que los datos se han insertado correctamente.

También podemos hacer algunas recuperaciones de bases de datos e imprimirlas. Para hacer eso, simplemente crearemos una lista vacía para almacenar nuestras entradas y usaremos el comando .find() en la colección "reseñas".

Al usar la función buscar de PyMongo, la recuperación también debe formatearse como JSON. Los parámetros dados a la función buscar tendrán un campo y un valor.

De forma predeterminada, MongoDB siempre devuelve el campo _id (su propio campo de ID único, no el ID que extrajimos de GameSpot), pero podemos indicarle que suprima esto especificando un valor 0. Los campos que queremos devolver, como el campo score en este caso, deben recibir un valor 1:

1
2
3
4
5
6
scores = []

for score in list(reviews.find({}, {"_id":0, "score": 1})):
    scores.append(score)

print(scores[:900])

Esto es lo que se extrajo e imprimió con éxito:

1
[{'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'}, {'score': '10.0'} ...

También podemos convertir fácilmente los resultados de la consulta en un marco de datos usando Pandas:

1
2
scores_data = pd.DataFrame(scores, index=None)
print(scores_data.head(20))

Esto es lo que se devolvió:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
   score
0   10.0
1   10.0
2   10.0
3   10.0
4   10.0
5   10.0
6   10.0
7   10.0
8   10.0
9   10.0
10  10.0
11  10.0
12  10.0
13  10.0
14  10.0
15  10.0
16  10.0
17   9.9
18   9.9
19   9.9

Antes de comenzar a analizar algunos de los datos, tomemos un momento para ver cómo podríamos unir dos colecciones. Como se mencionó, GameSpot tiene múltiples recursos para extraer datos y es posible que deseemos obtener valores de una segunda base de datos como la base de datos de Juegos.

MongoDB es una base de datos NoSQL, por lo que, a diferencia de SQL, no está diseñada para manejar relaciones entre bases de datos y unir campos de datos. Sin embargo, existe una función que puede aproximarse a una unión de base de datos - lookup().

La función lookup() imita una unión de base de datos y se puede hacer especificando una canalización, que contiene la base de datos de la que desea unir elementos, así como los campos que desea de los documentos de entrada (localField) y los documentos "from" (foreignField).

Finalmente, elige un alias para convertir los documentos extranjeros y se mostrarán con este nuevo nombre en nuestra tabla de respuestas a consultas. Si tuviera una segunda base de datos llamada juegos y quisiera unirlos en una consulta, podría hacerlo así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
pipeline = [{
    '$lookup': {
        'from': 'reviews',
        'localField': 'id',
        'foreignField': 'score',
        'as': 'score'
    }
},]

for doc in (games.aggregate(pipeline)):
    print(doc)

Análisis de los datos

Ahora podemos comenzar a analizar y visualizar algunos de los datos que se encuentran en nuestra base de datos recién creada. Asegurémonos de tener todas las funciones que necesitaremos para el análisis.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pymongo import MongoClient
import pymongo
import pandas as pd
from bs4 import BeautifulSoup
import re
from nltk.corpus import stopwords
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from collections import Counter
import string
import en_core_web_sm
import seaborn as sns

Digamos que queremos hacer un análisis de las palabras que se encuentran en las reseñas de juegos de GameSpot. Tenemos esa información en nuestra base de datos, solo tenemos que obtenerla.

Podemos comenzar recopilando las 40 mejores reseñas (o el número que desee) de nuestra base de datos usando la función find() como antes, pero esta vez especificaremos que queremos ordenar por la variable score y que vamos a ordenar en orden descendente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
d_name = 'gamespot_reviews'
collection_name = 'gamespot'

client = MongoClient('127.0.0.1', 27017)
db = client[d_name]

reviews = db.reviews
review_bodies = []

for body in list(reviews.find({}, {"_id":0, "body": 1}).sort("score", pymongo.DESCENDING).limit(40)):
    review_bodies.append(body)

Convertiremos esa respuesta en un marco de datos de Pandas y la convertiremos en una cadena. Luego extraeremos todos los valores dentro de la etiqueta HTML <p> que contiene el texto de revisión, lo que haremos con [HermosaSopa](https://www.crummy.com/software/BeautifulSoup/bs4/ doc/):

1
2
3
4
5
6
7
8
9
reviews_data = pd.DataFrame(review_bodies, index=None)

def extract_comments(input):
    soup = BeautifulSoup(str(input), "html.parser")
    comments = soup.find_all('p')
    return comments

review_entries = extract_comments(str(review_bodies))
print(review_entries[:500])

Consulte la declaración “imprimir” para ver que se ha recopilado el texto de revisión:

1
[<p>For anyone who hasn't actually seen the game on a TV right in front of them, the screenshots look too good to be true. In fact, when you see NFL 2K for the first time right in front of you...]

Ahora que tenemos los datos del texto de revisión, queremos analizarlos de varias maneras diferentes. Intentemos obtener algo de intuición sobre qué tipo de palabras se usan comúnmente en las 40 reseñas principales. Podemos hacer esto de varias maneras diferentes:

Sin embargo, antes de que podamos hacer cualquier análisis de los datos, tenemos que preprocesarlos.

Para preprocesar los datos, queremos crear una función para filtrar las entradas. Los datos de texto todavía están llenos de todo tipo de etiquetas y caracteres no estándar, y queremos eliminarlos obteniendo el texto sin procesar de los comentarios de revisión. Usaremos expresiones regulares para sustituir los caracteres no estándar con espacios en blanco.

También usaremos algunas palabras vacías de NTLK (palabras muy comunes que agregan poco significado a nuestro texto) y las eliminaremos de nuestro texto creando una lista para contener todas las palabras y luego agregando palabras a esa lista solo si no están en nuestra lista de palabras vacías.

Nube de palabras

Tomemos un subconjunto de las palabras de revisión para visualizarlas como un corpus. Si es demasiado grande al generarlo, puede causar algunos problemas con la nube de palabras.

Por ejemplo, he filtrado las primeras 5000 palabras:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
stop_words = set(stopwords.words('english'))

def filter_entries(entries, stopwords):

    text_entries = BeautifulSoup(str(entries), "lxml").text
    subbed_entries = re.sub('[^A-Za-z0-9]+', ' ', text_entries)
    split_entries = subbed_entries.split()

    stop_words = stopwords

    entries_words = []

    for word in split_entries:
        if word not in stop_words:
            entries_words.append(word)

    return entries_words

review_words = filter_entries(review_entries, stop_words)
review_words = review_words[5000:]

Ahora podemos hacer una nube de palabras muy fácilmente usando una biblioteca de WordCloud prefabricada encontrado aquí.

Esta nube de palabras nos brinda información sobre qué tipo de palabras se usan comúnmente en las reseñas principales:

Desafortunadamente todavía está lleno de palabras comunes, por lo que sería una buena idea hacer un filtrado de las palabras de revisión con un [esquema de filtrado tf-idf](/python-para-nlp-creando-un-modelo-tf -idf-desde-cero/), pero para los propósitos de esta simple demostración, esto es lo suficientemente bueno.

De hecho, tenemos algo de información sobre los tipos de conceptos de los que se habla en las reseñas de juegos: jugabilidad, historia, personajes, mundo, acción, ubicaciones, etc.

Podemos confirmar por nosotros mismos que estas palabras se encuentran comúnmente en las reseñas de juegos al mirar una de las 40 mejores reseñas que seleccionamos: [Reseña de Uncharted 4] de Mike Mahardy (https://www.gamespot.com/reviews/ análisis-final-de-uncharted-4-a-thiefs/1900-6416423/):

Efectivamente, la revisión analiza la acción, el juego, los personajes y la historia.

El tamaño de las palabras nos da una idea de la frecuencia con la que aparecen las palabras en estas reseñas, pero también podemos contar con qué frecuencia aparecen ciertas palabras.

Encimera

Podemos obtener una lista de las palabras más comunes dividiendo las palabras y añadiéndolas a un diccionario de palabras junto con su recuento individual, que se incrementará cada vez que se vea la misma palabra.

Entonces solo necesitamos usar Counter y la función most_common():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def get_word_counts(words_list):
    word_count = {}

    for word in words_list:
        word = word.translate(translator).lower()
        if word not in stop_words:
            if word not in word_count:
                word_count[word] = 1
            else:
                word_count[word] += 1

    return word_count

review_word_count = get_word_counts(review_words)
review_word_count = Counter(review_word_count)
review_list = review_word_count.most_common()
print(review_list)

Aquí están los recuentos de algunas de las palabras más comunes:

1
[('game', 1231), ('one', 405), ('also', 308), ('time', 293), ('games', 289), ('like', 285), ('get', 278), ('even', 271), ('well', 224), ('much', 212), ('new', 200), ('play', 199), ('level', 195), ('different', 195), ('players', 193) ...]

Reconocimiento de entidad nombrada

También podemos hacer el reconocimiento de entidades nombradas usando en_core_web_sm, un modelo de lenguaje incluido con espacioso. Los diversos conceptos y características lingüísticas que se pueden detectar se enumeran aquí.

Necesitamos obtener la lista de entidades y conceptos con nombre detectados del documento (lista de palabras):

1
2
3
doc = nlp(str(review_words))
labels = [x.label_ for x in doc.ents]
items = [x.text for x in doc.ents]

Podemos imprimir las entidades encontradas, así como un recuento de las entidades.

1
2
3
4
5
6
7
8
# Example of named entities and their categories
print([(X.text, X.label_) for X in doc.ents])

# All categories and their counts
print(Counter(labels))

# Most common named entities
print(Counter(items).most_common(20))

Esto es lo que está impreso:

1
2
3
4
5
[('Nintendo', 'ORG'), ('NES', 'ORG'), ('Super', 'WORK_OF_ART'), ('Mario', 'PERSON'), ('15', 'CARDINAL'), ('Super', 'WORK_OF_ART'), ('Mario', 'PERSON'), ('Super', 'WORK_OF_ART') ...]

Counter({'PERSON': 1227, 'CARDINAL': 496, 'ORG': 478, 'WORK_OF_ART': 204, 'ORDINAL': 200, 'NORP': 110, 'PRODUCT': 88, 'GPE': 63, 'TIME': 12, 'DATE': 12, 'LOC': 12, 'QUANTITY': 4 ...]

[('first', 147), ('two', 110), ('Metal', 85), ('Solid', 82), ('GTAIII', 78), ('Warcraft', 72), ('2', 59), ('Mario', 56), ('four', 54), ('three', 42), ('NBA', 41) ...]

Digamos que queremos trazar los términos reconocidos más comunes para diferentes categorías, como personas y organizaciones. Solo necesitamos hacer una función para obtener los conteos de las diferentes clases de entidades y luego usarla para obtener las entidades que deseamos.

Obtendremos una lista de entidades/personas nombradas, organizaciones y GPE (ubicaciones):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def word_counter(doc, ent_name, col_name):
    ent_list = []
    for ent in doc.ents:
        if ent.label_ == ent_name:
            ent_list.append(ent.text)
    df = pd.DataFrame(data=ent_list, columns=[col_name])
    return df

review_persons = word_counter(doc, 'PERSON', 'Named Entities')
review_org = word_counter(doc, 'ORG', 'Organizations')
review_gpe = word_counter(doc, 'GPE', 'GPEs')

Ahora todo lo que tenemos que hacer es graficar los conteos con una función:

1
2
3
4
5
6
7
8
9
def plot_categories(column, df, num):
    sns.countplot(x=column, data=df,
                  order=df[column].value_counts().iloc[0:num].index)
    plt.xticks(rotation=-45)
    plt.show()

plot_categories("Named Entities", review_persons, 30)
plot_categories("Organizations", review_org, 30)
plot_categories("GPEs", review_gpe, 30)

Echemos un vistazo a las parcelas que se generaron.

Como era de esperar de las entidades con nombre, la mayoría de los resultados devueltos son nombres de personajes de videojuegos. Esto no es perfecto, ya que clasifica erróneamente algunos términos como "Xbox" como una entidad con nombre en lugar de una organización, pero aún nos da una idea de qué personajes se discuten en las revisiones principales.

La trama de la organización muestra algunos desarrolladores y editores de juegos como Playstation y Nintendo, pero también etiqueta cosas como "480p" como una organización.

Arriba está el diagrama de GPE, o ubicaciones geográficas. Parece que "Hollywood" y "Miami" aparecen a menudo en las reseñas de juegos. (¿Configuración para los juegos? ¿O tal vez el crítico está describiendo algo en el juego como al estilo de Hollywood?)

Como puede ver, llevar a cabo el reconocimiento de entidades nombradas y el reconocimiento de conceptos no es perfecto, pero puede darle cierta intuición sobre qué tipo de temas se tratan en un cuerpo de texto.

Trazado de valores numéricos {#trazado de valores numéricos}

Finalmente, podemos intentar graficar valores numéricos de la base de datos. Vamos a obtener los valores de puntaje de la colección de reseñas, contarlos y luego trazarlos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
scores = []

for score in list(reviews.find({}, {"_id":0, "score": 1})):
    scores.append(score)
scores = pd.DataFrame(scores, index=None).reset_index()

counts = scores['score'].value_counts()

sns.countplot(x="score", data=scores)
plt.xticks(rotation=-90)
plt.show()

Arriba está el gráfico para el número total de puntajes de revisión dados, que va de 0 a 9.9. Parece que los puntajes más comúnmente otorgados fueron 7 y 8, lo cual tiene sentido intuitivamente. Siete a menudo se considera promedio en una escala de revisión de diez puntos.

Conclusión

La recopilación, el almacenamiento, la recuperación y el análisis de datos son habilidades muy demandadas en el mundo actual, y MongoDB es una de las plataformas de bases de datos NoSQL más utilizadas.

Saber cómo usar bases de datos NoSQL y cómo interpretar los datos en ellas lo equipará para llevar a cabo muchas tareas comunes de análisis de datos.