Gráfico de Burbujas en Python: Np como Tamaño y Wp como Color
Visualiza la ubicación, el aceite acumulado y el agua acumulada de 16 pozos con Matplotlib
Yacimientos
Producción
Python
Visualización
Crea un mapa de burbujas en Python con los datos del ejemplo en Excel: coordenadas X–Y, área proporcional a Np y color según Wp. Incluye fragmentos de código paso a paso, dataset descargable e interpretación.
En el post de gráfico de burbuja en Excel representamos la ubicación de los pozos y su producción acumulada mediante el tamaño de las burbujas. En esta versión vamos a reproducir el ejemplo en Python y agregar una variable: la producción acumulada de agua, Wp, mediante el color.
Así podremos comparar aceite y agua acumulados sin separar los pozos en gráficos distintos:
Cuatro variables en una sola visualización. MMB significa millones de barriles.
Elemento del gráfico
Variable
Unidad
Posición horizontal
Coordenada X / Este
m
Posición vertical
Coordenada Y / Norte
m
Área de la burbuja
Np: aceite acumulado
MMB
Color de la burbuja
Wp: agua acumulada
MMB
Visita el canal @rigopetrodata
Tutoriales de R, Python y Excel para ingeniería petrolera
Usaremos los mismos 16 pozos y valores del archivo Excel original. Para simplificar la lectura desde Python, también se incluye una copia en CSV con nombres cortos de columnas: Pozo, X, Y, Np, Wp y Gp. El cambio de encabezados no modifica los datos.
Primeros cuatro registros. Gp se conserva en el archivo, aunque no participa en este gráfico. MMMPC significa miles de millones de pies cúbicos.
Pozo
X (m)
Y (m)
Np (MMB)
Wp (MMB)
Gp (MMMPC)
Pozo-1
569586.39
2144591.55
28.19
0.71
57.68
Pozo-2
569041.04
2145174.40
43.27
1.70
86.92
Pozo-3
569763.04
2144227.42
1.53
0.00
4.18
Pozo-4
570100.85
2144038.94
30.00
5.75
61.97
Al sumar los 16 registros del Excel obtenemos 313.87 MMB de Np, 28.81 MMB de Wp y 653.34 MMMPC de Gp. Estos totales se calcularon directamente del archivo descargable; para este tutorial tomamos los valores del archivo como referencia.
Coordenadas y fecha de corte
El post original describe X e Y como coordenadas UTM en metros, pero el archivo no especifica zona UTM, datum ni fecha de corte de los acumulados. Aquí se muestran las coordenadas tal como están, con igual escala en ambos ejes. Para superponer una cartografía real hay que confirmar el sistema de referencia; para comparar un campo propio, usar una misma fecha de corte.
Paso 1 — Leer y revisar el dataset
Descarga el CSV al final del artículo y colócalo en la carpeta de tu notebook. Ejecuta los fragmentos en orden, dentro de la misma sesión. Necesitamos pandas, NumPy y Matplotlib; si aún no los tienes, instálalos con python -m pip install pandas numpy matplotlib.
Ver código
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdf = pd.read_csv("produccion_pozos.csv")columnas = ["X", "Y", "Np", "Wp", "Gp"]df[columnas] = df[columnas].apply(pd.to_numeric, errors="raise")if df.empty ornot np.isfinite(df[columnas].to_numpy()).all():raiseValueError("Revisa los datos: hay valores faltantes o no finitos.")if df["Pozo"].isna().any() or df["Pozo"].duplicated().any():raiseValueError("Revisa los nombres de los pozos.")if (df[["Np", "Wp", "Gp"]] <0).any().any() or df["Np"].max() <=0:raiseValueError("Revisa los acumulados: deben ser no negativos y Np debe tener valores positivos.")print(df.head().to_string(index=False))print("\nTotales del campo:")print(df[["Np", "Wp", "Gp"]].sum().round(2))
Pozo X Y Np Wp Gp
Pozo-1 569586.39 2144591.55 28.19 0.71 57.68
Pozo-2 569041.04 2145174.40 43.27 1.70 86.92
Pozo-3 569763.04 2144227.42 1.53 0.00 4.18
Pozo-4 570100.85 2144038.94 30.00 5.75 61.97
Pozo-5 571025.66 2143955.86 0.95 0.00 2.10
Totales del campo:
Np 313.87
Wp 28.81
Gp 653.34
dtype: float64
Un dato faltante no se convierte en cero: primero debe revisarse en la fuente.
Paso 2 — Representar Np con el área de burbuja
El argumento s de scatter controla el área en puntos cuadrados. Usaremos \(s_i=s_{max}N_{p,i}/N_{p,max}\): un pozo con el doble de Np tendrá el doble de área, no el doble de diámetro.
Ver código
area_max =1800areas = area_max * df["Np"] / df["Np"].max()fig, ax = plt.subplots(figsize=(10, 7))ax.scatter(df["X"], df["Y"], s=areas, color="#2C7FB8", edgecolors="#273746", linewidths=0.6)ax.set_xlabel("Coordenada X / Este (m)")ax.set_ylabel("Coordenada Y / Norte (m)")ax.set_title("Producción acumulada de aceite por pozo")ax.set_aspect("equal", adjustable="box")ax.ticklabel_format(style="plain", useOffset=False)ax.margins(0.15)ax.grid(alpha=0.2)ax.set_axisbelow(True)plt.tight_layout()plt.show()
Figura 1: Ubicación de los pozos y aceite acumulado representado por el área.
area_max cambia únicamente la presentación. En el siguiente paso agregamos las etiquetas y las leyendas para interpretar los valores.
Paso 3 — Agregar Wp como color, etiquetas y leyendas
Pasamos Wp al argumento c y usamos la paleta secuencial viridis. La escala comienza en cero, por lo que los pozos sin agua acumulada también aparecen. Una barra de color representa Wp y una leyenda de círculos representa Np.
Reutilizamos df, areas y area_max del paso anterior. Agregamos el color con c=df["Wp"], una barra de color y los nombres con un mismo desplazamiento para todos los pozos.
Ver código
fig, ax = plt.subplots(figsize=(10, 7))# Área: Np. Color: Wp.puntos = ax.scatter( df["X"], df["Y"], s=areas, c=df["Wp"], cmap="viridis", vmin=0, edgecolors="black")fig.colorbar(puntos, ax=ax, label="Wp (MMB)")# Nombres de los pozos.for pozo in df.itertuples(): ax.annotate(pozo.Pozo, (pozo.X, pozo.Y), xytext=(5, 5), textcoords="offset points", fontsize=9)# Círculos de referencia con la misma escala de área.for np_ref in [10, 30, 60]: ax.scatter([], [], s=area_max * np_ref / df["Np"].max(), color="lightgray", edgecolors="black", label=str(np_ref))ax.legend(title="Np (MMB)", loc="upper right", labelspacing=2.5)ax.set_xlabel("Coordenada X / Este (m)")ax.set_ylabel("Coordenada Y / Norte (m)")ax.set_title("Producción acumulada: tamaño = Np, color = Wp")ax.set_aspect("equal", adjustable="box")ax.ticklabel_format(style="plain", useOffset=False)ax.margins(0.15)plt.tight_layout()plt.show()
Figura 2: Mapa de producción: área proporcional a Np y color según Wp.
Leer ambas escalas
El color indica agua acumulada y el área indica aceite acumulado. Los círculos de la leyenda representan 10, 30 y 60 MMB de Np. Todos los pozos de este ejemplo tienen Np positivo; un Np igual a cero tendría área cero. Los ajustes finos de etiquetas y presentación pueden trabajarse después, en un video o curso.
¿Cómo interpretar el resultado?
Pozo-10 tiene la burbuja más grande: 63.20 MMB de Np. Su Wp es 2.29 MMB, por lo que no aparece con el color del máximo de agua.
Pozo-4 acumula 30.00 MMB de Np y 5.75 MMB de Wp. Aunque su burbuja es menor que la de Pozo-10, alcanza el extremo superior de la escala de color. El gráfico permite reconocer que el mayor acumulado de aceite y el mayor acumulado de agua corresponden a pozos distintos.
Pozo-6 también destaca por agua, con 4.90 MMB de Wp frente a 16.72 MMB de Np. Los pozos 3, 5, 12 y 16 registran Wp igual a cero en el archivo y aparecen en el extremo inferior de la escala.
Wp no es corte de agua
El color representa un volumen acumulado, no una fracción ni una tasa actual. Un Wp alto puede relacionarse con más tiempo de producción o con mayores volúmenes manejados; por sí solo no demuestra irrupción de agua ni canalización.
Para evaluar la situación actual se necesita el historial y, cuando corresponda, el corte de agua calculado con tasas compatibles: \(f_w=q_w/(q_o+q_w)\). Tampoco debe confundirse ese indicador con la fracción acumulada \(W_p/(N_p+W_p)\).
Este mapa sirve para seleccionar pozos que merecen una revisión del historial. Puedes complementarlo con el dashboard de producción y comparar periodos de operación, condiciones de medición y comportamiento temporal antes de proponer una explicación.
Adaptarlo a otros campos
Mantén una fila por pozo y acumulados en las mismas unidades y fecha de corte.
Para usar Gp como tamaño, cambia la variable de área y su leyenda, incluyendo las unidades.
Para comparar fechas o campos, fija la misma referencia de tamaño y los mismos límites de color en todos los mapas. Normalizar cada figura por separado puede hacer que volúmenes distintos parezcan iguales.
Si los ceros significan “sin dato” en tu fuente, corrígelos antes del gráfico; el código los interpreta como valores reales.