Ajuste de curvas para cada columna en Pandas + extrapolar valores

Tengo un conjunto de datos con unas 300 columnas, cada una de ellas dependiente de la profundidad. La versión simplificada de Pandas DataFrame se vería así:

import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy_optimize import curve_fit df1 = pd.DataFrame({'depth': [1.65, 2.15, 2.65, 3.15, 3.65, 4.15, 4.65, 5.15, 5.65, 6.15, 6.65, 7.15, 7.65, 8.15, 8.65], '400.0': [13.909261, 7.758734, 3.513627, 2.095409, 1.628918, 0.782643, 0.278548, 0.160153, -0.155895, -0.152373, -0.147820, -0.023997, 0.010729, 0.006050, 0.002356], '401.0': [14.581624, 8.173803, 3.757856, 2.223524, 1.695623, 0.818065, 0.300235, 0.173674, -0.145402, -0.144456, -0.142969, -0.022471, 0.010802, 0.006181, 0.002641], '402.0': [15.253988, 8.588872, 4.002085, 2.351638, 1.762327, 0.853486, 0.321922, 0.187195, -0.134910, -0.136539, -0.138118, -0.020945, 0.010875, 0.006313, 0.002927], '403.0': [15.633908, 8.833914, 4.146499, 2.431543, 1.798185, 0.874350, 0.333470, 0.192128, -0.130119, -0.134795, -0.136049, -0.019307, 0.012037, 0.006674, 0.003002], '404.0': [15.991816, 9.066159, 4.283401, 2.507818, 1.831721, 0.894119, 0.344256, 0.196415, -0.125758, -0.133516 , -0.134189, -0.017659, -0.013281,0.007053, 0.003061], '405.0': [16.349725, 9.298403, 4.420303, 2.584094, 1.865257, 0.913887, 0.355041, 0.200702, -0.121396, -0.132237, -0.132330, -0.016012, 0.014525, 0.007433, 0.003120] }) 

Lo que necesito hacer es estimar la K en la siguiente ecuación. Básicamente cada columna corresponde a un perfil I (z) . El I (0) se debe calcular, para lo cual utilicé curve_fit , como referencia Estoy usando este post útil: https://stackoverflow.com/a/15369787/7541421

exponencial_eq

 x = df1.depth # Column values as a function of depth y = df1['400.0'] plt.plot(x, y, 'ro',label="Original Data") def func(def func(x, I0, k): # a = I0, b = k return I0 * np.exp(-k*x) popt, pcov = curve_fit(func, x, y) print ("E0 = %s , k = %s" % (popt[0], popt[1])) plt.plot(x, func(x, *popt), label="Fitted Curve") 

introduzca la descripción de la imagen aquí

¿Se podría hacer esto para cada columna por separado y de alguna manera guardarse como un nuevo DataFrame ?

Además, el nuevo DataFrame debe propagarse a los valores hacia z=0 para ciertas cuotas dz . En este caso, me falta [0.15, 0.65, 1.15] en mi columna de depth . Así que para cada z necesito obtener por cada columna el I(z) de la función.

¿Cómo puedo automatizarlo ya que cada conjunto de datos tiene un rango de profundidad diferente en mi caso?

PS Como alternativa, tal como se ha analizado originalmente en esta publicación, se puede aplicar un ajuste de regresión lineal trasformado por registro, para lo cual la solución se escribe en una respuesta a continuación.

Se han hecho algunos cambios después de la conversación con el autor principal de esta respuesta y con su aprobación.

En primer lugar, dado que estamos tratando con cantidades de transformación de log, es necesario encontrar el rango de valores que corresponden a valores no negativos por columna.

 negative_idx_aux = df_drop_depth.apply(lambda x:(x<0).nonzero()[0][:1].tolist()) negative_idx = [item for sublist in negative_idx_aux for item in sublist] if len(negative_idx) > 0: max_idx = max_idx = np.min(negative_idx) else: max_idx = None 

En comparación con el original, solo fusiono los bucles para obtener tanto la pendiente como la intersección.

 iz_cols = df1.columns.difference(['depth']) slp_int = {} for c in iz_cols: slope, intercept, r_value, p_value, std_err = stats.linregress(df1['depth'][0:max_idx],np.log(df1[c][0:max_idx])) slp_int[c] = [intercept, slope] slp_int = pd.DataFrame(, index = ['intercept', 'slope']) 

Exponer la intercepción nos da el valor de I en la superficie:

 slp_int.loc['intercept'] = np.exp(slp_int.loc['intercept']) 

La última parte del post se ha corregido debido a una mala interpretación del concepto final. Ahora se vuelve a crear el dataframe, con nuevos valores para las profundidades de la superficie (por encima del rango de profundidad de df1 , manteniendo el df1 para los valores de abajo).

Primero se recrea un rango completo entre z = 0 y el valor máximo de la columna de profundidad, con un step asignado y el valor en z = 0 :

 depth = np.asarray(df1.depth) depth_min = np.min(depth) ; depth_min_arr = np.array([depth_min]) step = 0.5 missing_vals_aux = np.arange(depth_min - step, 0, -step)[::-1] missing_vals = np.concatenate(([0.], missing_vals_aux), axis=0) depth_tot = np.concatenate((missing_vals, depth), axis=0) df_boundary = pd.DataFrame(columns = iz_cols) df_up = pd.DataFrame(columns = iz_cols) 

Cree un dataframe con el rango de las cuotas de profundidad propagadas hacia arriba:

 for c in iz_cols: df_up[c] = missing_vals 

Rellene los datos con los parámetros de regresión obtenidos:

 upper_df = slp_int.loc['intercept']*np.exp(slp_int.loc['slope']*df_up) upper_df['depth'] = missing_vals 

Combine el df1 y el upper_df para obtener un perfil completo:

 lower_df = df1 lower_df['depth'] = depth df_profile_tot = upper_df.append(lower_df, ignore_index=True)