Determinacinó de Carbono Activo en Suelo
Table of Contents
1. Método de Weil adaptado por Culman
1.1. Curva de calibración del colorímetro
Para determinar carbono activo utilizando la metodología propuesta por Weil et al. (2003), se mide absorbancia a 530nm (verde) de una muestra de suelo tratada con una solución de \(KMnO_4\). La determinación de concentración de carbono activo se utiliza la siguiente expresión:
\begin{equation} POXC \left[ \frac{mg}{kg_{soil}} \right] = \left[0.02 [\frac{mol}{L}] - (a + b \times Abs) \right] \times 9000 [\frac{mg C}{mol}] \times 0.02 [\frac{rx solution}{W_t}] \end{equation}donde:
- \(a\): es la ordenada al origen de la curva de calibración
- \(b\): es la pendiente de la curva de calibración
- \(Abs\): es la absorbancia de la muestra de suelo
- \(W_t\): es el peso de la muestra de suelo en \(kg\)
La curva de calibración se construye midiendo absorbancia para concentraciones conocidas de \(KMnO_4\). Se prepararon soluciones con las concentraciones detalladas en la tabla a continuación.
1.1.1. Carga de datos
Los datos se leen de un archivo CSV, con punto y coma (;
) como separadores de campo y comas (,
) como separador decimal. Se asume que la primer fila contiene los encabezados.
iloc se utiliza para seleccionar sólo algunas filas de la tabla de datos.
import numpy as np import pandas as pd data = pd.read_csv('CalibraciónPermanganato_M5.csv', sep=';', decimal=',').iloc[[0,1,2,3]] print(data)
Concentración Medicion Abs FCA Medicion Abs FCEN Medicion Abs Espectrofotometro 0 0.005 0.110 0.168 0.107 1 0.010 0.210 0.261 0.213 2 0.015 0.313 0.355 0.325 3 0.020 0.421 0.456 0.435
Esto simplemente imprime las columnas de la tabla de Pandas.
print(data.columns)
Index(['Concentración', 'Medicion Abs FCA', 'Medicion Abs FCEN', 'Medicion Abs Espectrofotometro'], dtype='object')
1.1.2. Cálculo de los parámetros de la regresión lineal
A partir de los datos anteriores calculamos la regresión lineal con el paquete stats para obtener la pendiente y la ordenada al origen, necesarias para calcular la concentración de carbono activo:
from scipy import stats conc = data['Concentración'] #absor = data['Medicion Abs Espectrofotometro'] absor = data['Medicion Abs FCA'] result = stats.linregress(absor, conc) print(f'Pendiente = {result.slope}\nOrdenada = {result.intercept}')
Pendiente = 0.048247983457834244 Ordenada = -0.00021334364113932344
print(result)
LinregressResult(slope=0.048247983457834244, intercept=-0.00021334364113932344, rvalue=0.9998490972373407, pvalue=0.00015090276265927738, stderr=0.000592757309912432, intercept_stderr=0.0001706199173031585)
- Análisis de errores: intervalos de confianza de la regresión lineal
Ahora calculamos los intervalos de confianza de los parámetros de la regresión lineal (pendiente y ordenada), con un nivel de significancia \(\gamma\) determinado [1].
from scipy.stats import t import sigfig as sf gamma = (100-95)/100 #Nivel de confianza del 95% igual a 2*sigma #gamma = (100-68)/100 #Nivel de confianza del 95% igual a 1*sigma t_n2 = t.ppf(1-gamma/2, 4-2) # pendiente_conc_abs = 1/result.slope # ordenda_conc_abs = -result.intercept/result.slope pendiente_conc_abs = result.slope ordenada_conc_abs = result.intercept err_pendiente = (result.stderr)*t_n2 err_ordenada = (result.intercept_stderr)*t_n2 #print(f'Pendiente = {pendiente_conc_abs} +- {err_pendiente}\nOrdenada = {ordenda_conc_abs} +- {err_ordenada}') print(f'Pendiente = {sf.round(pendiente_conc_abs, err_pendiente)}') print(f'Ordenada = {sf.round(ordenada_conc_abs, err_ordenada)}')
Pendiente = 0.048 ± 0.003 Ordenada = -0.0002 ± 0.0007
- Interpretación gráfica del intervalo de confianza de la regresión lineal
El intervalo de confianza representa toda la familia de rectas posibles cuyas pendientes se encuentran en el intervalo de confianza \((pendiente\_conc\_abs \pm err\_pendiente)\) y ordenadas en el intervalo \((ordenda\_conc\_abs \pm err\_ordenada)\)
import matplotlib.pyplot as plt fig=plt.figure(figsize=(8,5)) plt.plot(absor, conc, 'o') y = pendiente_conc_abs * absor + ordenada_conc_abs y1 = (pendiente_conc_abs + err_pendiente) * absor + ordenada_conc_abs - err_ordenada y2 = (pendiente_conc_abs - err_pendiente) * absor + ordenada_conc_abs + err_ordenada plt.plot(absor, y, '-r') plt.plot(absor, y1, '-g') plt.plot(absor, y2, '-g') plt.xlabel("Absorbancia") plt.ylabel("Concentración molar") plt.grid() fig.tight_layout() fname = 'myfig.png' plt.savefig(fname) fname # return this to org-mode
También es posible hacer el gráfico de la regresión lineal y el intervalo de confianza con el paquete Seaborn
import seaborn as sns fig_sns=plt.figure(figsize=(8,5)) ax = sns.regplot(x=absor, y=conc) ax.set_xlabel("Absorbancia") ax.set_ylabel("Concentración molar") ax.grid() fname = 'myfig_sns.png' fig_sns = ax.get_figure() fig_sns.savefig(fname) fname # return this to org-mode
1.2. Análisis de muestras de suelo
Calculamos ahora la concentración de carbono activo utilizando la ecuación propuesta por Weil et al. (2003):
\begin{equation} POXC \left[ \frac{mg}{kg_{soil}} \right] = \left[0.02 [\frac{mol}{L}] - (a + b \times Abs) \right] \times 9000 [\frac{mg C}{mol}] \times 0.02 [\frac{rx solution}{W_t}] \end{equation}donde:
- \(a\): es la ordenada al origen de la regresión lineal
- \(b\): es la pendiente de la regresión lineal
- \(Abs\): es la absorbancia de la muestra de suelo
- \(W_t\): es el peso de la muestra de suelo en \(kg\)
Curva de calibración 2 (FCA) | |
---|---|
Pendiente | 19.62 |
Ordenada | -0.002 |
Error Pendiente | 0.44 |
Error Ordenada | 0.006 |
Las medidas de absorbancia para 4 muestras de suelo están dadas en la tabla 2.
Muestras | Abosrbancias FCA |
---|---|
A2 | 0.29 |
2 | 0.237 |
6 | 0.232 |
19 | 0.122 |
Nota: los valores de pendiente y ordenada de la tabla 1 corresponden a la función \(Absorbancia(concentración)\). Pero para el cálculo de POXC necesitamos la función inversa. Podemos calcular la regresión lineal intercambiando las variables o, calcular los parámetros pendiente y ordenada para la función inversa a partir de los dados en la tabla 1:
\begin{equation} Absorbancia = b \times concentración + a \end{equation} \begin{equation} Concentración = \frac{Absorbancia}{b} - \frac{a}{b} \end{equation}entonces
\begin{equation} b' = 1/b \end{equation} \begin{equation} a' = -a/b \end{equation}Vamos a continuar utilizando los parámetros obtenidos para la curva de calibración de Culman, por lo que no es necesario calcular la función inversa. Para la muestra A2 el valor la concentración de carbono activo es:
import numpy as np a = ordenada_conc_abs b = pendiente_conc_abs da = err_ordenada db = err_pendiente W_t = 0.0025 Abs_A2 = 0.29 POXC = (0.02 - (a + b*Abs_A2)) * 9000 * 0.02 / W_t print(f'POXC = {sf.round(POXC, 3)} [mg/kg]')
POXC = 448.0 [mg/kg]
1.3. Cálculo del error en la concentración de carbono activo
Para calcular el error final en \(POXC\) hay que hacer propagación de errores, teniendo en cuenta los errores en la pendiente y la ordenada al origen de la regresión lineal:
\begin{equation} \Delta POXC = \sqrt{ {\left( \frac{\partial POXC}{\partial{a}} \times \Delta a \right)}^2 + {\left( \frac{\partial POXC}{\partial{b}} \times \Delta b \right)}^2} \end{equation}donde:
- \(\frac{\partial POXC}{\partial{a}}\) es la derivada parcial de \(POXC\) respecto de \(a\)
- \(\frac{\partial POXC}{\partial{b}}\) es la derivada parcial de \(POXC\) respecto de \(b\)
- \(\Delta a\) es el error en \(a\)
- \(\Delta b\) es el error en \(b\)
La ecuación 4 sólo tiene en cuenta los errores en los parámetros de ajuste (ordenada y pendiente), despreciando los errores en la Absorbancia (\(Abs\)) y la masa de la muestra de suelo (\(W_t\)). Esto es así ya que los errores relativos en estas medidas es menor que el error en los parámetros de ajuste.
operando en la ecuación 1 se obtiene que:
\begin{equation} \frac{\partial POXC}{\partial{a}} = 9000 \times 0.02 / W_t \end{equation} \begin{equation} \frac{\partial POXC}{\partial{b}} = Abs \times 9000 \times 0.02 / W_t \end{equation}Aplicando el resultado anterior a la muestra de suelo *A2“ resulta:
dPOXC_da = 9000 * 0.02/W_t dPOXC_db = Abs_A2 * 9000 * 0.02/W_t dPOXC = np.sqrt( (dPOXC_da * da)**2 + (dPOXC_db * db)**2 ) print(f'POXC = {sf.round(POXC, dPOXC)}')
POXC = 450 ± 80
El intervalo de confianza es:
POXC = (370.0, 520.0)
El error relativo porcentual es:
\(\Delta POXC = 17\%\)
1.4. Interpretación gráfica de la propagación del error
Ahora vamos a graficar la función resultante \(POXC vs Abs\), para ver gráficamente cómo se propagan los errores. Para ello vamos a generar una serie de datos aleatorios de pendientes y ordenadas según los intervalos de confianza calculados y luego, sobre esa nueva serie de datos, calculamos la regresión lineal.
from scipy import stats conc = data['Concentración'] #absor = data['Medicion Abs Espectrofotometro'] absor = data['Medicion Abs FCA'] pendientes = (np.random.randn(4)*db)+b ordenadas = (np.random.randn(4)*da + a) def calc_poxc(absorbance): return (0.02 - (ordenadas + pendientes*absorbance)) * 9000 * 0.02 / W_t poxc = calc_poxc(absor) result = stats.linregress(absor, poxc) print( f'Pendiente = {sf.round(result.slope, (result.stderr)*t_n2)}')
Pendiente = -3500 ± 800
Ahora graficamos la familia de rectas encontradas y vemos si los intervalos de confianza se condicen con el error calculado por propagación:
fname = './myfig_poxc_reg.png' fig=plt.figure(figsize=(8,5)) pendiente = result.slope ordenada = result.intercept err_pendiente = (result.stderr)*t_n2 err_ordenada = (result.intercept_stderr)*t_n2 plt.plot(absor, poxc, 'o') y = pendiente * absor + ordenada y1 = (pendiente + err_pendiente) * absor + ordenada - err_ordenada y2 = (pendiente - err_pendiente) * absor + ordenada + err_ordenada plt.plot(absor, y, '-r') plt.plot(absor, y1, '-g') plt.plot(absor, y2, '-g') plt.xlabel("Absorbancia") plt.ylabel("POXC [mg/Kg]") plt.grid() fig.tight_layout() plt.savefig(fname) fname # return this to org-mode
2. Protocolo Mc Knight
Este protocolo comparte la misma metodología que aquel propuesto por Culman, excepto en: la alícuota de 0,5 mL de la muestra tratada se diluye en 30 mL de agua destilada; no se realiza una curva de calibración, sino que solo se usa el valor de absorbancia del Permanganato de Potasio y el valor de absorbancia de la muestra.
\begin{equation} POXC[mg/kg]=(1-\frac{abs_{muestra}}{abs_{KMnO4}}*0,02M)* 0,02 * 9000)/ 0,0025 \end{equation}2.1. Análisis de errores
En este caso las fuentes de error son las absorbancias tanto de la muestra como del permanganato. Por lo tanto, el erro en \(POXC\) viene dado por:
\begin{equation} \Delta POXC = \sqrt{ {\left( \frac{\partial POXC}{\partial{abs_{muestra}}} \times \Delta abs_{muestra} \right)}^2 + {\left( \frac{\partial POXC}{\partial{abs_{KMnO4}}} \times \Delta abs_{KMnO4} \right)}^2} \end{equation}donde:
- \(\frac{\partial POXC}{\partial{abs_{muestra}}}\) es la derivada parcial de \(POXC\) respecto de \(abs_{muestra}\)
- \(\frac{\partial POXC}{\partial{abs_{KMnO4}}}\) es la derivada parcial de \(POXC\) respecto de \(abs_{KMnO4}\)
- \(\Delta abs_{muestra}\) es el error en \(abs_{muestra}\)
- \(\Delta abs_{KMnO4}\) es el error en \(abs_{KMnO4}\)
Los errores en \(abs_{muestra}\) y \(abs_{KMnO4}\) son los que corresponden al instrumento utilizado. Si existen razones para suponer que hay otras fuentes de error (por ejemplo, de método al prepara las dilusiones), es necesario estimar esos errores haciendo estadística. Es decir, midiendo varias veces la misma muestra, y calcular el valor promedio y desvación estándar de la absorbancia.
3. Apéndice
Si lo que tenemos es la función inversa \(abs(conc)\) necesitamos la función inversa, por lo que es conveniente calcular la regresión al revés, considerando la absorbancia como la variable independiente. Comparamos los resultados para demostrar que da lo mismo.
print('La función original....') print(f'Pendiente = {sf.round(b, db)}') result_inv = stats.linregress(conc, absor) err_pendiente = (err_pendiente)*t_n2 err_ordenada = (err_ordenada)*t_n2 b = result_inv.slope a = result_inv.intercept db = (result_inv.stderr)*t_n2 da = (result_inv.intercept_stderr)*t_n2 print('Resultado de la regresión calculada con absorbancia como variable independiente') print(f'Pendiente_inv = {sf.round(result_inv.slope, (result_inv.stderr)*t_n2)}') print('Ahora calculamos los parámetros de la función inversa y sus errores:') b_inv = 1/b a_inv = -a/b err_b_inv = 1/b**2 * db err_a_inv = np.sqrt((-1/b * da)**2 + (-a/b**2 * db)**2) print( f'Pendiente = {sf.round(b_inv, err_b_inv)}') print('Da exactamente lo mismo!!!')
La función original.... Pendiente = 0.048 ± 0.003 Resultado de la regresión calculada con absorbancia como variable independiente Pendiente_inv = 21 ± 1 Ahora calculamos los parámetros de la función inversa y sus errores: Pendiente = 0.048 ± 0.003 Da exactamente lo mismo!!!
4. Referencias
- John R. Taylor, “Introduction To Error Analysis: The Study of Uncertainties in Physical Measurements”, University Science Books, 1997.