Sign In
Free Sign Up
  • English
  • Español
  • 简体中文
  • Deutsch
  • 日本語
Sign In
Free Sign Up
  • English
  • Español
  • 简体中文
  • Deutsch
  • 日本語

Realizando un Análisis Avanzado de Datos de Eventos de Facebook con una Base de Datos Vectorial

En la era digital actual, los profesionales de todas las industrias deben mantenerse actualizados con los próximos eventos, conferencias y talleres. Sin embargo, encontrar de manera eficiente eventos que se alineen con los intereses de uno en medio del vasto océano de información en línea presenta un desafío significativo.

Este blog presenta una solución innovadora a este desafío: una aplicación integral diseñada para extraer datos de eventos de Facebook (opens new window) y analizar los datos extraídos utilizando MyScale (opens new window). Si bien MyScale se asocia comúnmente con la pila tecnológica RAG o se utiliza como una base de datos vectorial, sus capacidades se extienden más allá de estos ámbitos. Lo utilizaremos para el análisis de datos, aprovechando su funcionalidad de búsqueda vectorial para analizar eventos que son semánticamente similares, proporcionando así mejores resultados e información.

Es posible que notes que Grok AI (opens new window) utilizó la base de datos vectorial Qdrant como motor de búsqueda para recuperar información en tiempo real de los datos de X (anteriormente conocido como Twitter). También puedes evaluar el poder de las bases de datos vectoriales de esta manera con MyScale mediante la integración de MyScale con otras plataformas como Apify para mejorar las tareas diarias mediante el desarrollo de aplicaciones personalizadas simples.

Entonces, en este blog, desarrollaremos una aplicación que tome solo el nombre de una ciudad como entrada y extraiga todos los eventos relacionados de Facebook. Posteriormente, realizaremos un análisis de datos y una búsqueda semántica utilizando las capacidades avanzadas de vectores SQL de MyScale.

# Herramientas y Tecnologías

Utilizaremos varias herramientas, incluyendo Apify, MyScale (opens new window) y OpenAI, para desarrollar esta útil aplicación.

  • Apify: Una plataforma popular de web scraping (opens new window) y automatización que agiliza significativamente el proceso de recopilación de datos. Proporciona la capacidad de extraer datos y posteriormente alimentarlos a LLMs. Esto nos permite entrenar LLMs con datos en tiempo real y desarrollar aplicaciones.
  • MyScale: MyScale es una base de datos vectorial SQL que utilizamos para almacenar y procesar datos estructurados y no estructurados de manera optimizada.
  • OpenAI: Utilizaremos el modelo text-embedding-3-small de OpenAI (opens new window) para obtener los embeddings del texto y luego guardar esos embeddings en MyScale para el análisis de datos y la búsqueda semántica.

# Cómo Configurar MyScale y Apify

Para comenzar a configurar MyScale y Apify, deberás crear un nuevo directorio y un archivo de Python (opens new window). Puedes hacer esto abriendo tu terminal o línea de comandos e ingresando los siguientes comandos:

mkdir MyScale
cd MyScale
touch main.ipynb

Instalemos los paquetes necesarios. Copia el siguiente comando y pégalo en tu terminal. Estos paquetes nos proporcionarán las herramientas y bibliotecas que necesitamos para desarrollar nuestra aplicación.

pip install openai apify-client clickhouse-connect pandas numpy

Esto debería instalar todas las dependencias en tu sistema. Para confirmar que todo se haya instalado correctamente, puedes ingresar el siguiente comando en tu terminal.

pip freeze | egrep '(openai|apify-client|clickhouse-connect|pandas|numpy)'

Esto debería incluir todas las dependencias instaladas con sus versiones. Si encuentras alguna dependencia faltante, es posible que debas volver a ejecutar el comando de instalación para ese paquete específico. Ahora, estamos listos para escribir nuestro código después de las instalaciones.

💡 Nota: Trabajaremos en un cuaderno de Python. Considera cada bloque de código como una celda de cuaderno.

# Cómo Extraer Datos con Apify

Ahora, utilizaremos la API de Apify para extraer datos de eventos de la ciudad de Nueva York utilizando el rascador de eventos de Facebook (opens new window).

import pandas as pd
from apify_client import ApifyClient
# Inicializa ApifyClient con tu token de API
client = ApifyClient("Ingresa_tu_clave_de_apify_aquí")

# Prepara la entrada del Actor
run_input = {
    "searchQueries": ["Deportes Nueva York"],
    "startUrls": [],
    "maxEvents": 50,
}

# Ejecuta el Actor y espera a que termine
run = client.actor("UZBnerCFBo5FgGouO").call(run_input=run_input)

df_columns = ['Nombre', 'Fecha y Hora', 'Descripción', 'Usuarios Asistentes', 'Usuarios Interesados', 'Usuarios que Respondieron', 'Ciudad', 'Organizado Por', 'Dirección']
dataframe1 = pd.DataFrame(columns=df_columns)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    # Utiliza una comprensión de diccionario para reemplazar los valores None con una cadena vacía
    row = {
        'Nombre': item.get('name', ''),
        'Fecha y Hora': item.get('dateTimeSentence', ''),
        'Descripción': item.get('description', ''),
        'Usuarios Asistentes': item.get('usersGoing', ''),
        'Usuarios Interesados': item.get('usersInterested', ''),
        'Usuarios que Respondieron': item.get('usersResponded', ''),
        'Ciudad': item.get('location', {}).get('city', '') if item.get('location') else '',
        'Organizado Por': item.get('organizedBy', ''),
        'Dirección': item.get('location', {}).get('streetAddress', '') if item.get('location') else ''
    }
    # Asegúrate de que todos los valores None se reemplacen por una cadena vacía
    row = {k: (v if v is not None else '') for k, v in row.items()}
    dataframe1 = dataframe1._append(row, ignore_index=True)

# Limpieza de los datos
dataframe1['Descripción'] = dataframe1['Descripción'].replace('\\n', '', regex=True)

Este script nos proporciona los detalles de los próximos eventos en forma de un DataFrame de pandas. 💡 Nota: No olvides agregar tu clave de API de Apify en el script anterior. Puedes encontrar tu token de API en la página de Integraciones (opens new window) en la Consola de Apify.

# Preprocesamiento de Datos

Cuando recopilamos datos en bruto, estos vienen en varios formatos. En este script, llevaremos las fechas de los eventos a un formato único para que nuestra filtración de datos se pueda realizar de manera más eficiente.

# Importa las bibliotecas necesarias para la manipulación de datos y el análisis de fechas
import pandas as pd
import numpy as np
from datetime import datetime
from dateutil import parser

# Función para analizar cadenas de fechas que pueden representar un rango o una sola fecha
def parse_dates(date_str):
    # Verifica si la cadena de fecha contiene un guion, lo que indica un rango
    if '-' in date_str:
        parts = date_str.split('-')
        # Si la cadena se divide en dos partes, es un rango válido
        if len(parts) == 2:
            try:
                # Analiza las fechas de inicio y fin, formateándolas a un formato legible
                start_date = parser.parse(parts[0], fuzzy=True).strftime('%a, %b %d')
                end_date = parser.parse(parts[1], fuzzy=True).strftime('%a, %b %d')
                return start_date, end_date
            except ValueError:
                # En caso de error de análisis, no hacer nada (se manejará a continuación)
                pass
    # Si no es un rango o si el análisis del rango falló, intenta analizarlo como una sola fecha
    try:
        parsed_date = parser.parse(date_str, fuzzy=True)
        # Formatea la fecha única para start_date y formatea de manera diferente para end_date
        start_date = parsed_date.strftime('%a, %b %d A LAS %I:%M %p EDT')
        end_date = parsed_date.strftime('%a, %b %d')  # Omite la hora para end_date
        return start_date, end_date
    except ValueError:
        # Devuelve NaN para ambas fechas si el análisis falla
        return np.nan, np.nan

# Función para extraer la fecha, hora y día detallados de una cadena de fecha
def extract_date_time_day(date_str):
    try:
        # Analiza la cadena de fecha, permitiendo cierta flexibilidad en el formato de entrada
        parsed_date = parser.parse(date_str, fuzzy=True)
        # Extrae y formatea las partes de fecha, hora y día
        date = parsed_date.strftime('%Y-%m-%d')
        day = parsed_date.strftime('%a')
        # Determina si la cadena original incluía un componente de hora
        time_component = parsed_date.strftime('%I:%M %p') not in date_str
        time = parsed_date.strftime('%H:%M:%S') if not time_component else np.nan
    except ValueError:
        # Si el análisis falla, establece la fecha, hora y día en NaN
        date, time, day = np.nan, np.nan, np.nan

    return date, time, day

# Aplica la función parse_dates al DataFrame, creando nuevas columnas para las fechas de inicio y fin
dataframe1[['Fecha de Inicio', 'Fecha de Fin']] = dataframe1.apply(lambda row: pd.Series(parse_dates(row['Fecha y Hora'])), axis=1)

# Elimina las filas donde Fecha de Inicio es NaN, lo que indica que el análisis no tuvo éxito
dataframe = dataframe1.dropna(subset=['Fecha de Inicio'])

# Aplica extract_date_time_day para dividir las fechas de inicio y fin en columnas separadas de fecha, hora y día
dataframe['Fecha de Inicio'], dataframe['Hora de Inicio'], dataframe['Día de Inicio'] = zip(*dataframe['Fecha de Inicio'].apply(extract_date_time_day))
dataframe['Fecha de Fin'], _, dataframe['Día de Fin'] = zip(*dataframe['Fecha de Fin'].apply(extract_date_time_day))

# Elimina la columna original 'Fecha y Hora' ya que ya no es necesaria
dataframe=dataframe.drop(['Fecha y Hora'], axis=1)

# Convierte 'Fecha de Inicio' y 'Fecha de Fin' al formato de fecha, extrayendo solo la parte de la fecha
dataframe['Fecha de Inicio'] = pd.to_datetime(dataframe['Fecha de Inicio']).dt.date
dataframe['Fecha de Fin'] = pd.to_datetime(dataframe['Fecha de Fin']).dt.date

# Convierte 'Hora de Inicio' al formato de fecha y hora, conservando la información de la hora
dataframe['Hora de Inicio'] = pd.to_datetime(dataframe['Hora de Inicio'])

Este fragmento de código utiliza pandas con los paquetes datetime y dateutil de Python para formatear los datos.

# Generación de Embeddings

Para comprender y buscar eventos en profundidad, generaremos embeddings a partir de sus descripciones utilizando el modelo text-embedding-3-small. Estos embeddings capturan la esencia semántica de cada evento, lo que ayuda a que la aplicación devuelva mejores resultados.

# Importa la biblioteca OpenAI para acceder a la API.
from openai import OpenAI 
# Inicializa el cliente de OpenAI con una clave de API.
openai_client = OpenAI(api_key="tu_clave_de_api_de_openai_aquí")
# Función para obtener embeddings de texto
def get_embedding(text, model="text-embedding-3-small"):
    return openai_client.embeddings.create(input=text, model=model).data
embeddings = get_embedding(dataframe["Descripción"].tolist())
# Extrae los vectores de embedding del objeto de embeddings
vectors = [embedding.embedding for embedding in embeddings]
array = np.array(vectors)
embeddings_series = pd.Series(list(array))
# Agrega los embeddings como una nueva columna en el DataFrame.
dataframe['Embeddings de Descripción'] = embeddings_series

Ahora, insertaremos el nuevo DataFrame con embeddings en MyScale.

# Conexión con MyScale

Como hemos discutido al principio, utilizaremos MyScale como una base de datos vectorial para almacenar y gestionar datos. Aquí, nos conectaremos a MyScale en preparación para el almacenamiento de datos.

import clickhouse_connect
client = clickhouse_connect.get_client(
    host='nombre_del_host_aquí',
    port=443,
    username='nombre_de_usuario_aquí',
    password='contraseña_aquí'
)

Esta configuración de conexión asegura que nuestra aplicación pueda comunicarse con MyScale y utilizar la potencia de SQL para la manipulación y el análisis de datos.

💡 Nota: Consulta los Detalles de Conexión (opens new window) para obtener más información sobre cómo conectarte al clúster de MyScale.

# Crear Tablas e Índices Utilizando MyScale

Ahora crearemos una tabla de acuerdo con nuestro DataFrame. Todos los datos se almacenarán en esta tabla, incluidos los embeddings.

client.command("""
    CREATE TABLE default.Eventos (
    Nombre String,
    Descripción String,
    Usuarios_Asistentes Int64,
    Usuarios_Interesados Int64,
    Usuarios_Respondieron Int64,
    Ciudad String,
    Organizado_Por String,
    Dirección String,
    Fecha_de_Inicio Date32,
    Fecha_de_Fin Nullable(Date32),
    Hora_de_Inicio Nullable(DateTime64),
    Día_de_Inicio String,
    Día_de_Fin String,
    Embeddings_de_Descripción Array(Float32),
    CONSTRAINT check_data_length CHECK length(Embeddings_de_Descripción) = 1536
    ) ENGINE = MergeTree()
    ORDER BY (Nombre);
    """)

Las declaraciones SQL anteriores crean una tabla llamada Eventos en el clúster. La CONSTRAINT se asegura de que todos los vectores de embedding tengan la misma longitud 1536.

# Almacenamiento de Datos y Creación de un Índice en MyScale

En este paso, insertaremos los datos procesados en MyScale. Esto implica la inserción por lotes de los datos para garantizar un almacenamiento y recuperación eficientes.

tamaño_lote = 10  # Ajusta según tus necesidades

num_lotes = len(dataframe) // tamaño_lote

for i in range(num_lotes):
    índice_inicial = i * tamaño_lote
    índice_final = índice_inicial + tamaño_lote
    datos_lote = dataframe[índice_inicial:índice_final]
    # print(datos_lote["Embeddings_de_Descripción"])
    client.insert("default.Eventos", datos_lote.to_records(index=False).tolist(), column_names=datos_lote.columns.tolist())
    print(f"Lote {i+1}/{num_lotes} insertado.")

client.command("""
ALTER TABLE default.Eventos
    ADD VECTOR INDEX vector_index Embeddings_de_Descripción
    TYPE MSTG
""")

Utilizando pandas, el código anterior transfiere eficientemente nuestro conjunto de datos preparado a la base de datos MyScale.

# Análisis de Datos Utilizando MyScale

Finalmente, utilizaremos las capacidades analíticas de MyScale para realizar análisis y habilitar la búsqueda semántica. Al ejecutar consultas SQL, podemos analizar eventos según temas, ubicaciones y fechas. Entonces, intentemos escribir algunas consultas.

# Consulta SQL Simple

Comencemos obteniendo los 10 mejores resultados de la tabla.

resultados=client.query("""
        SELECT Nombre, Descripción FROM default.Eventos LIMIT 10
    """)
for fila in resultados.named_results():
        print(fila["Nombre"])
        print(fila['Descripción'])

Esta consulta simplemente devolverá los 10 mejores resultados de la tabla eventos.

# Descubrir Eventos por Relevancia Semántica

Intentemos encontrar los 10 mejores eventos próximos con una vibra similar a un evento de referencia, como este: "Uno de los espectáculos más largos en el país - Operando desde 1974 ... ¡AHORA nuestro 50º AÑO! Nuestro Schenectady". Esto se logra comparando los embeddings semánticos de las descripciones de los eventos, asegurando una coincidencia en temas y emociones.

embeddings=get_embedding(["Uno de los espectáculos más largos en el país - Operando desde 1974 ... ¡AHORA nuestro 50º AÑO! Nuestro Schenectady"])
embedding=embeddings[0].embedding
resultados = client.query(f"""
        SELECT Nombre, Descripción,
        distance(Embeddings_de_Descripción, {embedding}) as dist FROM default.Eventos ORDER BY dist LIMIT 10
    """)
for fila in resultados.named_results():
        print("Título del evento  ", fila["Nombre"])
        print("Descripción del evento  ", fila['Descripción'])
        print("Distancia: ", fila["dist"])

# Eventos Populares por Popularidad

Esta consulta clasifica los 10 mejores eventos por el número de asistentes y usuarios interesados, destacando eventos populares desde festivales en grandes ciudades hasta conferencias importantes. Es ideal para aquellos que buscan unirse a reuniones grandes y enérgicas.

resultados = client.query(f"""
        SELECT Nombre, Ciudad, Usuarios_Asistentes, Usuarios_Interesados, Usuarios_Respondieron
        FROM default.Eventos
        ORDER BY Usuarios_Asistentes DESC, Usuarios_Interesados DESC
        LIMIT 10
    """)
for fila in resultados.named_results():
        print("Nombre del Evento  ", fila["Nombre"])
        print("Ciudad ", fila["Ciudad"])
        print("Usuarios Asistentes ", fila["Usuarios_Asistentes"])
        print("Usuarios Interesados ", fila["Usuarios_Interesados"])

# Eventos Populares Locales en Nueva York

Combinando relevancia y popularidad, esta consulta identifica eventos similares en la ciudad de Nueva York relacionados con un evento específico y los clasifica por asistencia, ofreciendo una lista curada de eventos que reflejan la vibrante cultura de la ciudad y atraen el interés local.

embeddings=get_embedding(["Uno de los espectáculos más largos en el país - Operando desde 1974 ... ¡AHORA nuestro 50º AÑO! Nuestro Schenectady"])
embeddi=embeddings[0].embedding
resultados = client.query(f"""
        SELECT Nombre,Ciudad, Descripción, Usuarios_Asistentes,distance(Embeddings_de_Descripción, {embeddi}) as dist
        FROM default.Eventos
        WHERE Ciudad LIKE '%Nueva York%' and dist < 1.5
        ORDER BY Usuarios_Asistentes DESC,dist
        LIMIT 10
    """)
for fila in resultados.named_results():
        print("Nombre del Evento  ", fila["Nombre"])
        print("Descripción ", fila["Descripción"])
        print("Usuarios Asistentes ", fila["Usuarios_Asistentes"])

# Principales Organizadores de Eventos

Esta consulta clasifica los 10 principales organizadores de eventos por el número total de asistentes y usuarios interesados, destacando aquellos que se destacan en la creación de eventos atractivos y en la atracción de grandes audiencias. Proporciona información para planificadores de eventos y asistentes interesados en eventos de primer nivel.

# ¿Qué cliente ha atraído al mayor número de usuarios?
resultados = client.query(f"""
       SELECT Organizado_Por, SUM(Usuarios_Asistentes + Usuarios_Interesados) AS Total_Usuarios
        FROM default.Eventos
        GROUP BY Organizado_Por
        ORDER BY Total_Usuarios DESC
        Limit 10
    """)
for fila in resultados.named_results():
        print("Nombre del Evento  ", fila["Organizado_Por"])
        print("Total de Usuarios ", fila["Total_Usuarios"])

# Implementar RAG

Anteriormente, hemos explorado MyScale para el análisis de datos, destacando sus capacidades para mejorar nuestros flujos de trabajo de datos. A partir de ahora, daremos un paso más al implementar Retrieval-Augmented Generation (RAG), un marco innovador que combina una base de conocimientos externa con LLMs. Este paso te ayudará a comprender mejor tus datos y encontrar información más detallada. A continuación, verás cómo utilizar RAG con MyScale, lo que hará que trabajar con datos sea más interesante y productivo.

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

consulta="¿Puedes sugerirme algunos eventos relacionados con el baloncesto?"
# Utiliza el método get_embedding definido anteriormente, acepta una lista de oraciones
embeddings=get_embedding([consulta])
embeddings=embeddings[0].embedding
resultados = client.query(f"""
        SELECT Nombre, Ciudad, Usuarios_Asistentes, Descripción, distance(Embeddings_de_Descripción, {embeddings}) as dist
        FROM default.Eventos
        ORDER BY Usuarios_Asistentes DESC,dist
        LIMIT 5
    """)
PROMPT_TEMPLATE = """
Tu objetivo es formular una respuesta a una pregunta utilizando solo la información proporcionada a continuación:
{context}
---
Tu tarea es analizar cuidadosamente el contexto proporcionado y proporcionar una respuesta a la siguiente pregunta basándote únicamente en la información dada:
{question}
"""
# Combina las descripciones de los mejores resultados.
descripciones = [fila["Descripción"] for fila in resultados.named_results()]
contexto_texto = "\n\n---\n\n".join(descripciones)
plantilla_prompt = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
prompt = plantilla_prompt.format(context=contexto_texto, question=consulta)
modelo = ChatOpenAI(openai_api_key="tu_clave_de_api_de_openai_aquí")
respuesta_texto = modelo.predict(prompt)
respuesta_formateada = f"Respuesta: {respuesta_texto}\n"
print(respuesta_formateada)

A lo largo de este blog, hemos observado que MyScale es mucho más que una base de datos vectorial que se puede utilizar para desarrollar todo tipo de aplicaciones. Podemos utilizarlo como una base de datos SQL simple o para aplicaciones de IA avanzadas, cubriendo la mayoría del dominio de desarrollo. Te animamos a que al menos lo pruebes y explores las funciones avanzadas registrándote en el nivel gratuito y obteniendo 5 millones de almacenamiento de vectores gratuitos.

# Conclusión

Hemos explorado las capacidades y funcionalidades de MyScale con Apify Scraper a través del proceso de desarrollo de una aplicación de análisis de eventos. MyScale ha demostrado sus excepcionales capacidades en la búsqueda vectorial de alto rendimiento al retener todas las funcionalidades de las bases de datos SQL, lo que ayuda a los desarrolladores a realizar búsquedas semánticas utilizando la sintaxis SQL familiar con una velocidad y precisión mucho mejores.

Las capacidades de MyScale no se limitan a esta aplicación, puedes adoptarla para desarrollar cualquier aplicación de IA utilizando el método RAG (opens new window). MyScale superó a otras bases de datos vectoriales (opens new window) en términos de costo, velocidad y precisión, así que ¿por qué no probarlo en tu próxima aplicación?

Si tienes algún comentario o sugerencia, no dudes en comunicarte con nosotros en el servidor de MyScale en Discord (opens new window) o en Twitter (opens new window).

Start building your Al projects with MyScale today

Free Trial
Contact Us