En sectores como el financiero, los conjuntos de datos de series temporales de miles de millones o billones de filas no son la excepción, sino la norma. Sin embargo, a menudo resulta difícil obtener información valiosa sobre estos conjuntos de datos masivos, simplemente porque son muy difíciles de manejar.

Son lentos de cargar y de consultar, y mantenerlos en tiempo real puede ser un dolor de cabeza.
Por eso se diseñó kdb+: es la base de datos de series temporales más rápida y el estándar de la industria para gestionar conjuntos de datos masivos, gracias a su velocidad ultrarrápida y sus capacidades de transmisión en tiempo real, especialmente cruciales en finanzas, donde cada microsegundo cuenta.

Pero a pesar de la velocidad de kdb+, un desafío es que sigue siendo difícil obtener información rápidamente de los datos. Una vez escritas las consultas, se ejecutan con extrema rapidez, pero escribirlas puede requerir mucho tiempo.

En este artículo, explicaré cómo crear una función sencilla llamada query_and_plot, que permite consultar un conjunto de datos masivo y obtener gráficos e información prácticamente al instante.
Imagina tener una pregunta sobre tu conjunto de datos y obtener un gráfico:

query_and_plot(“Graficar los rendimientos acumulados de TSLA y AAPL”)

¡Y listo! Obtienes un gráfico:

image 22

Esto es lo que haremos hoy. El resultado final es el anterior, pero al dividirlo en partes no es muy complejo:

image 23

Si quieres seguir el proceso o simplemente acceder al código completo, consúltalo aquí.

Tabla de contenidos

Lectura y carga de datos

Primero, cargaremos un conjunto de datos bursátiles de muestra que contiene 3 millones de registros de series temporales de minutos en kdb+ mediante PyKX, que proporciona una interfaz de Python para kdb+:

import pykx as kx

# Load a sample CSV and push it into q as 'trades'
pandas_tbl = pd.read_csv(
'https://huggingface.co/datasets/opensporks/stocks/resolve/main/stocks.csv',
parse_dates=['timestamp']
)
tbl = kx.toq(pandas_tbl) # DataFrame → q Table
tbl = tbl.rename(columns={"close": "closePrice"})
kx.q['trades'] = tbl # assign to q global

Para ejecutar pykx, necesitamos un archivo de licencia personal gratuito, que puede obtenerse aquí.

Podemos escalarlo fácilmente a entre 100 millones y 10 mil millones de filas; el resto de la lógica sería exactamente la misma.

Ahora tenemos nuestros datos cargados en kdb+ como una tabla llamada «trades». Examinemos con qué estamos trabajando:

# get table description and save as text stored in a variable:
table_desc = tbl.dtypes
print(table_desc)

# get min and last date
min_date = kx.q('first trades`timestamp').pd()
max_date = kx.q('last trades`timestamp').pd()

print(f"\nMinimum timestamp: {min_date}")
print(f"Maximum timestamp: {max_date}")
image 24

Podemos ver que tenemos algunos tickers de acciones, algunos flotantes y marcas de tiempo. KDB+, por defecto, guarda las marcas de tiempo con una granularidad de nanosegundos.

Construyendo el Traductor de IA: Lenguaje Natural → Q-SQL

La magia surge cuando combinamos la velocidad de kdb+ con la comprensión del lenguaje natural de la IA. Crearemos un traductor que convierte preguntas en inglés simple en consultas q-SQL. q-SQL es una sintaxis mejorada sobre el lenguaje q, un lenguaje de programación y de consulta desarrollado específicamente para trabajar con kdb+. Los LLM aún no son especialmente buenos escribiendo q.

Afortunadamente, qSQL cumple en cierta medida con ANSI, así que si le explicamos a un LLM las diferencias entre qSQL y SQL, podemos generar código de consulta válido:

class TranslateResponse(BaseModel):
qsql: str

def translate_to_qsql(nl_query: str, table: str, schema: str) -> str:
system = (
SYSTEM_PROMPT
)
user = (
f"Table: {table}\n"
f"Schema: {schema}\n"
f"Question: {nl_query}"
)
resp = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user},
],
response_format=TranslateResponse,
)
return resp.parsed.qsql

El indicador del sistema contiene instrucciones detalladas sobre la sintaxis de q-SQL y las mejores prácticas, lo que garantiza que la IA genere consultas válidas y eficientes para kdb+. Para ver el indicador completo del sistema, consulta el cuaderno de Colab. ¡Es una maravilla! Todavía estoy buscando la opción óptima y estoy abierto a recibir comentarios.

Visualización automatizada con IA

Una vez que tengamos los resultados de la consulta, necesitamos visualizarlos. De nuevo, utilizaremos IA para generar automáticamente gráficos adecuados según la estructura de datos:

def visualize_df_with_plotly(df, model="gpt-4o", sample_size=20, temperature=0):
"""
Given a pandas DataFrame df and an OpenAI-compatible client,
infers a schema and sample, asks the model to generate Plotly Express code,
and then executes and displays the resulting chart.
"""
# 1. Build schema and sample, converting timestamps to strings
sample_df = df.head(sample_size).copy()
for col, dtype in sample_df.dtypes.items():
if "datetime" in str(dtype).lower() or "timestamp" in str(dtype).lower():
sample_df[col] = sample_df[col].astype(str)
sample = sample_df.to_dict(orient="list")
schema_auto = {col: str(dtype) for col, dtype in df.dtypes.items()}

# 2. Prepare prompts
system = (
"You're an expert Python data-visualizer. "
"Given a DataFrame schema and sample data, "
"write self-contained code using Plotly Express that "
"takes the df and, creates a fig named fig, "
"Write code that assigns df to fig, "
"but do not call fig.show(). I will handle displaying. "
"Feel free to show any other aggregations and print as numbers as you feel is needed as long as the figure is made"
"GIVE ME ONLY CODE AND NOTHING ELSE."
"Do not rewrite the values in the df, assume it is global in a dataframe called df"
"Think about what the user wants in the query and provide it"
"Never hardcode values"
)
user = (
f"Schema: {json.dumps(schema_auto)}\n\n"
f"Sample data: {json.dumps(sample, default=str)}"
)
# 3. Call the chat completion API
resp = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user}
],
temperature=temperature,
)
# 4. Extract the code from the response
chart_code = resp.choices[0].message.content
if chart_code.startswith("```"):
chart_code = chart_code.split("\n", 1)[1].rsplit("\n```", 1)[0]
print(chart_code)
# 5. Execute the generated code
local_vars = {"df": df, "px": px}
exec(chart_code, {}, local_vars)
# 6. Display the figure
fig = local_vars.get("fig")
if fig:
fig.show()
else:
raise RuntimeError("The generated code did not produce a fig object.")

Ahora necesitamos una función que tome un df de pandas y lo convierta en un gráfico. Consideré usar pandas-ai, una solución más robusta y con más funciones que permite a los agentes extraer información más detallada de los df de pandas, pero decidí implementar yo mismo una función de visualización para simplificar.

Integrando todo: query_and_plot

Ahora combinamos todo en una única y potente función:

def query_and_plot(nl_query: str, table: str = "trades") -> None:
"""
Given a natural-language question about the kdb+ table,
translates it to q-SQL, runs it, and renders an appropriate chart.
"""
# --- translate to q-SQL ---
# derive schema from the table in q
q_tbl = tbl
schema = str(q_tbl.dtypes)
print(schema)

# call the existing translator (assumes translate_to_qsql is defined globally)
qsql = translate_to_qsql(nl_query, table, schema)
print("🔧 Generated q-SQL:\n", qsql)
# --- execute query ---
result = kx.q.sql(qsql)
df = result.pd()
print("Resulting df After AI Query")
display(df.head())
print("\n── Resulting df Description ──")
display(df.describe(include="all"))
visualize_df_with_plotly(df)

Esta función integra todo.

Toma una consulta en lenguaje natural y la convierte en un gráfico con las herramientas que definimos anteriormente.

Tomamos una tabla kdb+ y extraemos el esquema. Pasamos el esquema, la consulta en lenguaje natural y la tabla a nuestra función translate_to_qsql, que devuelve qsql a la función exectute. Esto nos proporciona los datos que necesitamos para graficar, y es la razón principal por la que usamos kdb+: esta consulta podría tardar horas o fallar en Pandas si tenemos miles de millones de filas, dependiendo de lo que queramos hacer.

Ahora que hemos extraído algunos datos, los convertimos en un df de Pandas para poder graficarlos.

Finalmente, visualizamos el df con nuestra función visualize_df_with_plotly, que lo toma y genera código LLM para crear un gráfico, que luego muestra.

Ejemplos prácticos

Veamos esto en acción con algunas consultas prácticas:

Ejemplo 1: Rendimientos acumulados

query_and_plot("Chart the cumulative returns for TSLA and AAPL")

Esto genera:

  • Una consulta q-SQL que calcula los retornos acumulados.
  • Un gráfico multilineal que muestra el rendimiento a lo largo del tiempo.
  • Gestiona automáticamente el análisis de fechas y los cálculos de porcentajes.
image 25

Ejemplo 2: Análisis estadístico

query_and_plot("what is the average price for each stock?")

Esto produce:

  • Una consulta de agrupación para calcular promedios por símbolo bursátil.
  • Un gráfico de barras o una visualización de tabla adecuados.
  • Etiquetado y formato claros.
Temporales

De manera similar, podemos obtener la fecha con el precio de cierre más alto para cada acción:

query_and_plot("Which day had the highest closing price for each stock?")
image 27

Por qué es importante

  1. Velocidad a escala: Si bien este ejemplo utiliza un conjunto de datos pequeño, kdb+ puede gestionar miles de millones de filas con la misma facilidad. La misma función query_and_plot funciona independientemente de si se tienen 1000 o 1 000 000 000 de filas.
  2. Accesibilidad: Los expertos en el dominio que no sean expertos en q-SQL ahora pueden explorar los datos mediante lenguaje natural. Esto democratiza el acceso a análisis de series temporales de alto rendimiento.
  3. Iteración rápida: En lugar de escribir, depurar y optimizar consultas manualmente, puede explorar conjuntos de datos de forma conversacional, obteniendo retroalimentación visual instantánea.
  4. Ahorro económico: El tiempo de los desarrolladores q y los científicos de datos es invaluable. Si pueden hacer las cosas con mayor rapidez, esto podría suponer un ahorro de millones de dólares incluso para un pequeño equipo de desarrolladores cuantitativos.

Conclusión

La mayoría de las consultas tuvieron que volver a ejecutarse varias veces hasta generar un gráfico y código SQL válidos, pero esto sigue siendo mucho más sencillo que escribirlas yo mismo. Creo que con mejores indicaciones, muchos de estos errores desaparecerían. También creo que, si lo convirtiera en una herramienta, probaría 10 consultas diferentes, posiblemente con diferentes LLM, para ver cuál funcionaba correctamente.

Mi consejo es usar modelos grandes si es posible; los pequeños no son suficientes por ahora.
Al combinar el rendimiento de kdb+ con las capacidades de lenguaje natural de la IA, hemos creado una potente herramienta para el análisis de series temporales. Este enfoque escala de megabytes a petabytes, lo que hace que los análisis sofisticados sean accesibles para cualquiera que pueda formular una pregunta en un lenguaje sencillo.

El sector financiero procesa millones de operaciones por segundo; ahora los analistas pueden consultar y visualizar estos datos tan rápido como llegan. Lo que antes requería horas de escritura de consultas y configuración de gráficos ahora se realiza en segundos con una simple pregunta.

A medida que los conjuntos de datos crecen exponencialmente, herramientas como esta se vuelven no solo convenientes, sino esenciales para mantenerse competitivo en las industrias basadas en datos.

Me gustaría saber si alguien más ha experimentado con el análisis generativo de datos a esta escala o con la escritura de consultas Q con LLM.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *