Análisis de Datos
Los flujos de trabajo de análisis extraen datos de Solana desde instantáneas RPC, exportaciones de indexadores o APIs de proveedores a pandas o Polars para agregación, uniones y visualización. Python es el pegamento predeterminado entre los datos de la cadena Agave 4.1.1 y los notebooks, dashboards e informes por lotes.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
import polars as pl
df = pl.DataFrame(
{
"signature": [],
"slot": [],
"fee_lamports": [],
"success": [],
}
)
summary = df.group_by("success").agg(pl.col("fee_lamports").mean())Cuándo usar esto:
- Análisis exploratorio en Jupyter sobre muestras de transacciones.
- Métricas diarias de tesorería o protocolo (volumen, tarifas, firmantes únicos).
- Unir eventos on-chain con datos off-chain de CRM o de producto.
- Construir conjuntos de entrenamiento para monitoreo y detección de anomalías.
Ejemplo de Trabajo
import asyncio
import polars as pl
from solders.pubkey import Pubkey
from solana.rpc.async_api import AsyncClient
PROGRAM = Pubkey.from_string("<PROGRAM_ID>")
RPC = "https://api.devnet.solana.com"
async def collect_signatures(limit: int = 200):
async with AsyncClient(RPC) as client:
resp = await client.get_signatures_for_address(PROGRAM, limit=limit)
rows = [
{
"signature": str(item.signature),
"slot": item.slot,
"err": item.err is not None,
"block_time": item.block_time,
}
for item in resp.value
]
return pl.DataFrame(rows)
async def enrich_with_fees(df: pl.DataFrame):
async with AsyncClient(RPC) as client:
fees = []
for sig in df["signature"].to_list():
tx = await client.get_transaction(sig, max_supported_transaction_version=0)
meta = tx.value.transaction.meta if tx.value else None
fees.append(meta.fee if meta else None)
return df.with_columns(pl.Series("fee_lamports", fees))
async def main():
df = await collect_signatures()
df = await enrich_with_fees(df)
print(df.group_by("err").agg(pl.len(), pl.col("fee_lamports").mean()))
asyncio.run(main())Lo que esto demuestra:
- El historial de firmas inicia un frame de Polars sin dependencias pesadas.
- El enriquecimiento por transacción recupera campos de metadatos de tarifas.
- La agrupación agrega promedios de tarifas de éxito frente a fracaso.
- RPC asíncrono adecuado para scripts de enriquecimiento por lotes.
Inmersión Profunda
Cómo Funciona
- Extraer: RPC (
getSignaturesForAddress,getTransaction), exportaciones de Geyser o tablas de almacén de datos. - Normalizar: esquemas estables (firma, slot, program_id, instruction_index, amount).
- Transformar: pandas/Polars para uniones, funciones de ventana, remuestreo por slot/tiempo.
- Cargar: Parquet a almacenamiento de objetos, DuckDB, BigQuery o herramientas de visualización.
Compromisos de Fuentes de Datos
| Fuente | Frescura | Volumen | Acceso Python |
|---|---|---|---|
| Paginación RPC | Tiempo real | Bajo/medio | solana-py asyncio |
| API del indexador | Minutos | Alto | httpx + Polars |
| Plugin Geyser | Streaming | Muy alto | Consumidor gRPC (a menudo Rust) -> Parquet |
| Conjuntos de datos públicos | Diario | Histórico | read_parquet |
Notas de Python
# Preferir Polars para escaneos grandes
df = pl.scan_parquet("s3://bucket/txs/*.parquet").filter(pl.col("slot") > 250_000_000).collect()
# Solo cantidades enteras
df = df.with_columns((pl.col("amount_raw") / 10**decimals).alias("amount")) # vigilar política de flotantes
# Interoperabilidad con pandas
pdf = df.to_pandas()Errores Comunes
- Relleno de RPC a escala - lento y limitado por tasa. Solución: exportación masiva del indexador o proveedor.
- Cantidades de tokens flotantes - pérdida de precisión en Mints de 9 decimales. Solución: mantener cadenas/enteros
u64sin procesar en los frames. - Bucles
getTransactionsin límites - costosos. Solución: agrupar firmas; almacenar en caché respuestas en Parquet. - Deriva de esquema entre versiones de programa - rompe uniones. Solución: columna de versión del mapa de IDL/discriminador.
- Confusión de zona horaria en
block_time- segundos Unix UTC. Solución: conversión explícita adatetimeen Polars.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| dbt + almacén de datos | Métricas de toda la organización | Vista rápida en notebook |
| Indexador Rust | Streaming de subsegundo | Equipo solo de analistas |
| Flipside / Dune SQL | Dashboards estándar | Diseños de cuentas personalizados |
@solana/kit en navegador | Vistas específicas del usuario | Historial de TB |
Preguntas Frecuentes
¿pandas o Polars?
Polars para escaneos locales grandes; pandas está bien para muestras pequeñas y gráficos de matplotlib.
¿Cómo obtener todas las transacciones del programa?
Preferir un indexador; getSignaturesForAddress de RPC en el ID del programa es incompleto para instrucciones internas.
¿Decodificar eventos Anchor?
Analizar logs con el layout de eventos de IDL o usar un indexador que emita eventos estructurados.
¿Almacenar sin procesar o analizado?
Almacenar bytes sin procesar + columnas analizadas para reproducir cuando cambian los layouts.
¿Calidad de los datos de Devnet?
Tratar solo como prueba - las distribuciones difieren de mainnet.
¿Programación?
Airflow/Prefect cron con particiones Parquet idempotentes por fecha de slot.
¿PII?
Las direcciones de billetera pueden ser sensibles - clasificar los conjuntos de datos en consecuencia.
¿Análisis de grafos?
Exportar listas de aristas a NetworkX desde frames de transferencia.
¿Dashboards en tiempo real?
Transmitir a través de websockets a un búfer; micro-lotes a Polars cada N segundos.
¿Control de costos?
Almacenar en caché respuestas RPC; eliminar duplicados de firmas antes de getTransaction.
Relacionado
- Lectura de Datos de Cadena - Lecturas RPC y DAS
- Construcción de un Indexador - pipelines de producción
- Paginación y Eficiencia - Paginación RPC
- Mejores Prácticas de Python para Solana - higiene del servicio
Versiones de Stack: Esta página se escribió para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, y LiteSVM 0.6.x.