Análisis datos Agencia Nacional de Hidrocarburos - ANH
from pathlib import Path
from datetime import date , datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from scipy.optimize import curve_fit
warnings.filterwarnings("ignore")
Luego de procesar estos datos se creará un df que contenga el compilado de todos los datos oficiales y blind test en el númeral
El script de lectura para ambos archivos es el mismo
def lectyproc(ruta):
#Se quiere que este código perdure en el tiempo, por tanto, se genera el código para que lea todos los excel hasta el año actual
today = date.today()
#Los datos de producción se obtienen desde el año 2013
primeraño=2013
añoactual=today.year
listado_años=[]
#Un rango de fechas desde el 2013 hasta el año actual
años = range(primeraño,añoactual+1,1)
#Se crea una función para leer los nombres de los archivos que estan ubicados en el directorio
def ls(ruta = Path.cwd()):
return [arch.name for arch in Path(ruta).iterdir() if arch.is_file()]
#Se establece la ruta dentro del directorio actual
files=ls(ruta)
for año in años:
for file in files:
añotemp = file[file.find('2'):file.find('2')+4]
if int(año) == int(añotemp):
#Se leen los documentos de excel que están en el directorio especificado. Se limita las columnas de la A a la Q
globals()["año" + str(año)] = pd.read_excel(ruta+file,usecols='A:Q')
#Se eliminan las filas con valores nulos, para así quitar encabezados y totalizadores y la primera fila pasa a ser el encabezado del data frame
if globals()["año" + str(año)].isnull().sum().sum() != 0:
globals()["año" + str(año)]=globals()["año" + str(año)].dropna().reset_index(drop=True)
globals()["año" + str(año)]=globals()["año" + str(año)].rename(columns= globals()["año" + str(año)].iloc[0]).drop(0)
#Hago una lista con los nombres de las columnas del dataframe
globals()["año" + str(año)].columns = globals()["año" + str(año)].columns.str.upper()
globals()["año" + str(año)].columns = globals()["año" + str(año)].columns.str.strip()
globals()["column" + str(año)]=globals()["año" + str(año)].columns
#hallo la posición de la columna que contiene a Enero
for col in range(0,len( globals()["column" + str(año)])):
if globals()["column" + str(año)][col].lower() == 'enero':
j=col
#Paso de formato wide a long sabiendo cual es la columna que contiene a enero, lugar desde el cual queremos hacer el pivote
globals()["table" + str(año)] = pd.melt(globals()["año" + str(año)], id_vars=globals()["column" + str(año)][0:j], var_name="MES", value_name="ACEITE")
for columna1 in globals()["column" + str(año)]:
if columna1 == 'EMPRESA':
globals()["table" + str(año)] = globals()["table" + str(año)].rename(columns={'EMPRESA': 'OPERADORA'})
if columna1 == 'CUENCA':
globals()["table" + str(año)] = globals()["table" + str(año)].rename(columns={'CUENCA': 'MUNICIPIO'})
globals()["table" + str(año)]['MUNICIPIO'] = 'No encontrado'
#Ahora convertiré el mes a número para tener datos secuenciales MM-YYYY
def mesnum(mes):
if mes.lower() == 'enero':
mesn = 1
if mes.lower() == 'febrero':
mesn = 2
if mes.lower() == 'marzo':
mesn = 3
if mes.lower() == 'abril':
mesn = 4
if mes.lower() == 'mayo':
mesn = 5
if mes.lower() == 'junio':
mesn = 6
if mes.lower() == 'julio':
mesn = 7
if mes.lower() == 'agosto':
mesn = 8
if mes.lower() == 'septiembre':
mesn = 9
if mes.lower() == 'octubre':
mesn = 10
if mes.lower() == 'noviembre':
mesn = 11
if mes.lower() == 'diciembre':
mesn = 12
return mesn
globals()["table" + str(año)]['MM'] = globals()["table" + str(año)]['MES'].apply(mesnum)
globals()["table" + str(año)]['YYYY'] = año
#Concateno el mes y el año
def concat(*args):
strs = [str(arg) for arg in args if not pd.isnull(arg)]
return '/'.join(strs) if strs else np.nan
np_concat = np.vectorize(concat)
globals()["table" + str(año)]['MM-YYYY'] = np_concat(globals()["table" + str(año)]['MM'], globals()["table" + str(año)]['YYYY'])
#Realizo formato de fecha
globals()["table" + str(año)]['FECHA'] = globals()["table" + str(año)]['MM-YYYY'].apply(lambda x: datetime.strptime(x, '%m/%Y'))
#Borraré columnas que creé para generar la fecha
globals()["table" + str(año)].drop(['MM','MM-YYYY'], axis = 'columns', inplace=True)
listado_años.append(globals()["table" + str(año)])
table= pd.concat(listado_años, ignore_index=True)
return table
table_comp=lectyproc("Datos/Datos oficiales/")
table_comp.to_csv('Tabla general datos oficiales.csv',index=False)
table_comp.to_excel('Tabla general datos oficiales.xlsx',index=False)
table_comp
table_comp[table_comp['YYYY']==2019]['ACEITE'].sum()
blind_table_comp=lectyproc("Datos/Blind data/")
blind_table_comp
blind_table_comp[blind_table_comp['YYYY']==2019]['ACEITE'].sum()
blind_table_comp.to_csv('Tabla general datos ciegos.csv',index=False)
blind_table_comp.to_excel('Tabla general datos ciegos.xlsx',index=False)
from pathlib import Path
from datetime import date , datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from scipy.optimize import curve_fit
from sqlalchemy import create_engine, text
warnings.filterwarnings("ignore")
#Creando la conexión con las bases de datos postgres local Nota: postgres deja en minuscula los encabezados, por eso toca pasarlos a mayuscula de nuevo aunque esto ya se hiciera en el archivo de lectura
#engine=create_engine(f'postgresql://postgres:postgres@localhost:5432/HACKATON', max_overflow=20)
#table_comp = pd.read_sql_table('datosoficiales',engine)
#table_comp.columns = table_comp.columns.str.upper()
#Si quiere probar los resultados con postgres, descomente las tres lineas anteriores y deje como comentario la siguiente
table_comp = pd.read_csv('Datos/Tablas resumen/Tabla general datos oficiales.csv')
table_comp
table2020=table_comp[table_comp['YYYY']==2020].reset_index(drop=True)
#Agrupo por campo
campo2020 = pd.pivot_table(table2020, values='ACEITE', index=['CAMPO'], aggfunc=np.sum).sort_values(by='ACEITE', ascending=False, na_position='first')
#Al agrupar también ordené de mayor a menor, por tanto el top 5 es igual a las primeras 5 filas
top2020campos=campo2020[0:5]
campo2020
top2020campos
plot = top2020campos.plot(kind='bar', title='Top 5 de campos de mayor producción de petróleo en 2020')
table2018=table_comp[table_comp['YYYY']==2018].reset_index(drop=True)
campo2018cas= table2018[table2018['DEPARTAMENTO']=='CASANARE'].drop('DEPARTAMENTO',axis=1).reset_index()
#Se divide en 12 porque en formato largo aparecera 12 veces cada conteo, uno por cada mes
campo2018cascount=(campo2018cas.groupby('OPERADORA')['CAMPO'].count()/12).to_frame()
campo2018cascount=campo2018cascount[campo2018cascount['CAMPO']>=5].sort_values(['CAMPO'],ascending=False)
campo2018cascount
plot = campo2018cascount.plot(kind='bar', title='Operadoras que resportaron producción en más de 5 campos de Casanare en 2018')
contrato2018 = pd.pivot_table(table2018, values='ACEITE', index=['CONTRATO'], aggfunc=np.sum).sort_values(by='ACEITE', ascending=False, na_position='first')
#Divide en 1millon para dejar las unidades en MMstb
top2018contratos=contrato2018[0:5]/1000000
top2018contratos
plot = top2018contratos.plot(kind='bar', title='Top 5 de contratos de mayor producción de petróleo en 2018')
table2019=table_comp[table_comp['YYYY']==2019].reset_index(drop=True)
operadora2019 = pd.pivot_table(table2019, values='ACEITE', index=['OPERADORA','MES'], aggfunc=np.sum).sort_values(by='ACEITE', ascending=False, na_position='first').reset_index(level='MES')
operadora2019ago=operadora2019[operadora2019['MES']=='AGOSTO'].sort_values(by='ACEITE', ascending=False, na_position='first').drop('MES',axis=1)
topoperadora2019ago=operadora2019ago[0:10]
operadora2019
topoperadora2019ago
plot = topoperadora2019ago.plot(kind='bar', title='Top 10 de operadoras con mayor producción de petróleo en agosto de 2019')
#Lista para realizar el filtro
T1=['enero','febrero','marzo']
T2=['abril','mayo','junio']
#Agrupo por mes
meses2019 = pd.pivot_table(table2019, values='ACEITE', index=['MES'], aggfunc=np.sum).sort_values(by='ACEITE', ascending=False, na_position='first').reset_index(level='MES')
#Aplico formato al texto para realizar el filtro correctamente
meses2019['MES']=meses2019['MES'].apply(lambda x: x.lower())
#Aplico los filtros de los trimestres y guardo los resultados
T12019=meses2019[meses2019['MES'].isin(T1)].reset_index(drop=True)
T22019=meses2019[meses2019['MES'].isin(T2)].reset_index(drop=True)
#Agrupo por mes
meses2020 = pd.pivot_table(table2020, values='ACEITE', index=['MES'], aggfunc=np.sum).sort_values(by='ACEITE', ascending=False, na_position='first').reset_index(level='MES')
#Aplico formato al texto para realizar el filtro correctamente
meses2020['MES']=meses2020['MES'].apply(lambda x: x.lower())
#Aplico los filtros de los trimestres y guardo los resultados
T12020=meses2020[meses2020['MES'].isin(T1)].reset_index(drop=True)
T22020=meses2020[meses2020['MES'].isin(T2)].reset_index(drop=True)
#Genero un data frame para guardas los resultados de los filtros y agrupaciones
trim1920 = pd.DataFrame({"Año": [2019,2020], "Trimestre 1": [T12019['ACEITE'].sum(),T12020['ACEITE'].sum()], "Trimestre 2": [T22019['ACEITE'].sum(),T22020['ACEITE'].sum()]})
#Subo a indice la columna año
trim1920.set_index("Año")
coloresMedallas = ['#FFD700','#C0C0C0']
trim1920.plot(kind = 'bar',
width=0.8,
figsize=(10,4),
color = coloresMedallas);
from pathlib import Path
from datetime import date , datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from scipy.optimize import curve_fit
from sqlalchemy import create_engine, text
warnings.filterwarnings("ignore")
#Creando la conexión con las bases de datos postgres local Nota: postgres deja en minuscula los encabezados, por eso toca pasarlos a mayuscula de nuevo aunque esto ya se hiciera en el archivo de lectura
#engine=create_engine(f'postgresql://postgres:postgres@localhost:5432/HACKATON', max_overflow=20)
#blind_table_comp = pd.read_sql_table('datosciegos',engine)
#blind_table_comp.columns = blind_table_comp.columns.str.upper()
#Si quiere probar los resultados con postgres, descomente las dos lineas anteriores y deje como comentario la siguiente
blind_table_comp = pd.read_csv('Datos/Tablas resumen/Tabla general datos ciegos.csv')
#Aunque en el documento de lectura se asignó a esta columna un tipo de dato datetime, al descargarlo a csv y leer el csv, esto no se conservó y toca cambiar el formato de string a date time
blind_table_comp['FECHA'] = pd.to_datetime(blind_table_comp['FECHA'])
blind_table_comp
blind_table2019=blind_table_comp[blind_table_comp['YYYY']==2019].reset_index(drop=True)
#Si se desea inspeccionar otro campo y otro mes, solo debe cambiar estas variables
campo='1f d2689f'
mes='JULIO'
campo2019_7 = blind_table2019[blind_table2019['CAMPO']==campo.lower()][blind_table2019['MES']==mes.upper()].reset_index(drop=True)
print('Caudal de producción del campo', campo.upper(), 'en Julio de 2019:',campo2019_7['ACEITE'][0])
blind_table2019
blind_table2019[blind_table2019['CAMPO']==campo.lower()]
by_field=sns.barplot(x='MES', y='ACEITE', hue='CAMPO', data=blind_table2019[blind_table2019['CAMPO']==campo.lower()], palette=sns.color_palette("RdBu", n_colors=7))
for item in by_field.get_xticklabels():
item.set_rotation(45)
#Si se desea inspeccionar otro campo y otro mes, solo debe cambiar estas variables
operadora = '2fe52430'
mes = 'FEBRERO'
operadora2019_2 = blind_table2019[blind_table2019['OPERADORA']==operadora.lower()][blind_table2019['MES']==mes.upper()].reset_index(drop=True)
print('Caudal de producción de la operadora', operadora.upper(), 'en Febrero de 2019:',operadora2019_2['ACEITE'].sum())
blind_table2019[blind_table2019['OPERADORA']==operadora.lower()][blind_table2019['MES']==mes.upper()]
by_field=sns.barplot(x='CAMPO', y='ACEITE', hue='MES', data=blind_table2019[blind_table2019['OPERADORA']==operadora.lower()][blind_table2019['MES']==mes.upper()], palette=sns.color_palette("RdBu", n_colors=7))
for item in by_field.get_xticklabels():
item.set_rotation(45)
blind_table2018=blind_table_comp[blind_table_comp['YYYY']==2018].reset_index(drop=True)
#Agrupo por departamento
departamento2018 = pd.pivot_table(blind_table2018, values='ACEITE', index=['DEPARTAMENTO'], aggfunc=np.sum).sort_values(by='ACEITE', na_position='first').reset_index()
departamento2018
by_field=sns.barplot(x='DEPARTAMENTO', y='ACEITE', data=departamento2018, palette=sns.color_palette("RdBu", n_colors=7))
for item in by_field.get_xticklabels():
item.set_rotation(45)
depcampo = pd.pivot_table(blind_table_comp, values='ACEITE', index=['DEPARTAMENTO','CAMPO','YYYY'], aggfunc=np.mean).sort_values(by='ACEITE', ascending=False, na_position='first').reset_index()
depcampo.drop(columns=['CAMPO','YYYY'],inplace=True)
summary_blind_table=depcampo.groupby('DEPARTAMENTO').describe().unstack(1)
print(summary_blind_table)
sns.set_theme(style="whitegrid")
ax = sns.violinplot(x="DEPARTAMENTO", y="ACEITE",
data=depcampo, palette="muted")
Según los valores de desviación estandar, y el "violin plot" se observa claramente que el departamento cf33cb8a es el que tiene mayor variación en la producción promedio anual.
#Grafica de los datos de producción en el tiempo Nota: Se realiza para que pueda ser graficado distintos campos al tiempo
def RegularPlot(df, wells, units):
fig, ax = plt.subplots(figsize=(15,8))
plt.xlabel('Fecha')
plt.ylabel('ACEITE '+ units)
for well in wells:
df_filtered = df[df['CAMPO']==well]
rate = df_filtered['ACEITE']
date = df_filtered['FECHA']
ax.plot(date, rate, 'o', label=well)
ax.legend(shadow=True, fancybox=True)
return plt
#Se normaliza la grafica anterior con pasos mensuales
def NormalisedData(df, wells):
norm_data = {}
for well in wells:
df_filtered = df[df['CAMPO']==well]
start_date = min(df_filtered['FECHA'])
rate = df_filtered['ACEITE']
time = df_filtered['FECHA'] - start_date
time = time.dt.days
norm_data[well] = {
'rate': rate,
'time': time
}
return norm_data
def NormalisedPlot(df, wells):
fig, ax = plt.subplots(figsize=(15, 8))
plt.xlabel('DIAS')
plt.ylabel('ACEITE')
for well in wells:
df_filtered = df[df['CAMPO']==well]
start_date = min(df_filtered['FECHA'])
rate = df_filtered['ACEITE']
time = df_filtered['FECHA'] - start_date
time = time.dt.days
ax.plot(time, rate, 'o', label=well)
ax.legend(shadow=True, fancybox=True)
return plt
campo = ['51cbb05d']
dfcampo = blind_table_comp.drop(columns=['DEPARTAMENTO','MUNICIPIO','OPERADORA','CONTRATO','YYYY','MES']).reset_index(drop=True)
plot_data = RegularPlot (dfcampo, campo, 'BOPM')
normalised_data = NormalisedData(dfcampo, campo)
normalised_plot = NormalisedPlot(dfcampo, campo)
#Se dejará todo expresado de tal manera que si quiere realizar un tiempo de prueba y otro tiempo de testeo de la ecuación, solo deba cambiar "datat" y quitar el númeral de las dos lineas de código comentada
#Esto se hace ya que es algo común en métodos de machine learning
def arps(t, decline): #Definimos la función de ARPS
#Declinación hiperbolica
#Se dejó el b factor como 0.5 según las indicaciones
values = initialrate / ((1 + 0.5 * decline * t) ** (1 / 0.5))
return values
fitdict2={}
for well in campo:
X_p = normalised_data[well]['time']
Y_p = normalised_data[well]['rate']
X_arps=X_p[:int(len(X_p))]
Y_arps=Y_p[:int(len(Y_p))]
#Como tasa inicial se lee la primera tasa de producción, la cual debido al formato, corresponde al dia 30
initialrate = Y_arps[30]
#Se ajusta la nuve de puntos a la función de la ecuación de Arps
popt, pcov = curve_fit(arps, X_arps, Y_arps, bounds=([0],[0.1]))
#Diccionario que guarda las variables que mejor se ajustan a la ecuación de Arps
fitdict2[well]={
'decline rate': popt[0]
}
#Porcentaje de los datos que hubieran sido tomados como entrenamiento
datat=0
time_predict=[]
rate_predict=[]
arps_predict={}
for well in campo:
X_p = normalised_data[well]['time']
time_train=X_p[:int(len(X_p)*datat)]
time_predict=X_p[int(len(X_p)*datat):] #20% de los datos como prueba
Y_p = normalised_data[well]['rate']
rate_test=Y_p[int(len(Y_p)*datat):]
for time in time_predict:
rate_predict=arps(time_predict, fitdict2[well]['decline rate'])
#Descomentar la linea de abajo si quiere realizar split test
#rate_train=arps(time_train, fitdict[well]['beta'], fitdict[well]['initial rate'], fitdict[well]['decline rate'])
arps_predict[well]={
'time':time_predict,
'rate':rate_predict
}
plt.scatter(X_p, Y_p)
plt.plot(time_predict, rate_predict, color='green', linewidth=3)
#Descomentar la linea de abajo si quiere realizar split test
#plt.plot(time_train, rate_train, color='red', linewidth=3)
plt.xlabel('Days')
plt.ylabel('Rate')
plt.title('Arps equation')
plt.show()
print('Se tiene una tasa de declinación de',fitdict2['51cbb05d']['decline rate'])