Relaciones entre variables

Técnicas Experimentales 2021-1

Facultad de Ciencias, UNAM

En este notebooks aprenderemos:

  • Cómo graficar barras de error en Python en la variable dependiente e independiente.
  • Reforzaremos los conceptos aprendidos en el capitulo 3 de Introducción al análisis gráfico de datos experimentales de B. Oda Noda.
  • Aplicaremos los conceptos aprendidos en un ejercicio acerca de la densidad del agua y el aceite.

Por hacer:

  • Lee este notebook, corre y comprende el código en las celdas de las secciones 1 y 2.
  • Resuelve la sección 3.

Por entregar:

  • Este mismo notebook contestado individualmente. En la sección 3.1 deben hacer una gráfica de los datos, en 3.2 deben calcular la pendiente y ordenada al origen y graficar los datos y rectas ajustadas, y en 3.3 contestar las 6 preguntas de reflexión.

1. Cómo graficar incertidumbres

Para graficar barras de error o incertidumbre en los datos usamos la función de matplotlib.pytplot errorbar. Esta función toma como argumentos la variable independiente X, la variable dependiente Y y opcionalmente la incertidumbre en y yerr y en x xerr. Las incertidumbres pueden ser un solo valor, en cuyo caso Python asignará ese valor de incertidumbre a todos los datos; o puede ser un arreglo del mismo tamaño que X y Y en donde cada elemento de xerr y yerr corresponde a la incertidumbre de cada punto. Si no se especifica xerr o yerr, el default es que no haya barras de error. Veamos un ejemplo.

De la sigueinte tabla de mediciones:

(X $\pm$ 0.2) s (Y $\pm$ 5) cm
0.2 20
1.2 40
3.2 60
4.4 80
6.0 100
7.6 120
8.6 140

Definimos los arreglos correspondientes como:

In [1]:
import numpy as np
X = np.array([0.2,1.2,3.2,4.4,6.0,7.6,8.6])  # Esta es nuestra variable dependiente X
Y = np.array([20,40,60,80,100,120,140]) # Esta es la variable independiente Y
In [2]:
dX = 0.2 # s, incertidumbre en X
dY = 5 # cm, incertidumbre en Y
In [3]:
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15,5))
ax1= fig.add_subplot(1,3,1)
ax2= fig.add_subplot(1,3,2)
ax3= fig.add_subplot(1,3,3)

# En esta llamada a la función errorbar asignamos al argumento 
# yerr el valor de dY y a xerr el de dX. 
ax1.errorbar(X, Y, yerr=dY, xerr=dX) 
ax1.set_xlabel('Tiempo (s)')
ax1.set_ylabel('Desplazamiento (cm)')
ax1.set_title('Incertidumbre en X y Y')

# En esta llamada solo especificamos el valor de yerr, por lo que no 
# habrá barras de error en x.
ax2.errorbar(X, Y, yerr=dY)
ax2.set_xlabel('Tiempo (s)')
ax2.set_title('Incertidumbre en Y')

# En esta llamada solo especificamos el valor de xerr, por lo que no 
# habrá barras de error en y.
ax3.errorbar(X, Y, xerr=dX)
ax3.set_xlabel('Tiempo (s)')
ax3.set_title('Incertidumbre en X')
Out[3]:
Text(0.5, 1.0, 'Incertidumbre en X')

Podemos cambiar la estética de las gráficas. En general, no queremos las líneas que unen a los puntos porque pueden malinterpretarse como ajustes. Aquí hay algunos ejemplos de opciones para graficar:

In [4]:
fig = plt.figure(figsize=(15,5))
ax1= fig.add_subplot(1,3,1)
ax2= fig.add_subplot(1,3,2)
ax3= fig.add_subplot(1,3,3)

# En esta llamada 'capsize' cambia el tamaño de las líneas en los extremos 
# de las barras de error, 'elinewidth' controla el grosor de las barras de error 
# y 'linewidth' el de las líneas que unen los puntos. Cuando 'linewidth=0', no hay líneas. 
# Nota que no hay marcador por default.
ax1.errorbar(X, Y, yerr=dY, xerr=dX, 
             capsize=5, elinewidth=2, linewidth=0)
ax1.set_xlabel('Tiempo (s)')
ax1.set_ylabel('Desplazamiento (cm)')
ax1.set_title('Sin líneas ni marcadores')

# Otros controles estéticos: 'marker' cambia la figura del marcador. 
# En este caso 'o' pone bolitas; 'markerfacecolor' controla el color del relleno 
# del marcador y 'ecolor' el color de las barras de error.
ax2.errorbar(X, Y, yerr=dY, xerr=dX, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markerfacecolor='g',ecolor='r')
ax2.set_xlabel('Tiempo (s)')
ax2.set_title('Marcador verde, barras rojas')

# Aquí hay mas opciones: 'markeredgecolor' controla el color del borde del marcador (negro) y
# la opción 'd' para el marcador es un rombo.
ax3.errorbar(X, Y, yerr=dY, xerr=dX, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='d', markersize=12, 
             markerfacecolor='y',markeredgecolor='k',ecolor='k')
ax3.set_xlabel('Tiempo (s)')
ax3.set_title('Marcador rombo, tamaño 12')
plt.show()

2. Calcular la pendiente y la ordenada al origen

Después de inspeccionar la gráfica de las variables X y Y, la relación entre estas parece lineal, por lo cual podemos calcular la pendiente y la ordenada al origen. En la lectura 4 aprendiste que puedes calcular la pendiente y ordenada al origen a partir de la línea que "mejor se ajusta" a los datos. Pero el método gráfico descrito el libro implica que "a ojo" debemos graficar y escoger la mejor recta, la que pase por todos los intervalos de incertidumbre. Esto es un proceso subjetivo y poco riguroso que se vuelve más difícil conforme el número de mediciones aumenta. Sin embargo, el método gráfico es útil para hacer cálculos rápidos y funciona bien con poco datos. Para calcular la recta que mejor se ajusta a los datos aprenderemos el método de ajuste de una recta por mínimos cuadrados.

OJO: Debido a que no es necesario graficar los datos para realizar un ajuste por mínimos cuadrados, se puede caer en errores graves como tratar de ajustar una recta a un conjunto de mediciones cuya relación no es lineal. Por eso es muy importante graficar los datos y asegurarse de que la relación entre las variables es lineal antes de aplicar el método de mínimos cuadrados.

2.1 Ajuste de rectas de la forma $y=mx+b$ por mínimos cuadrados

La idea detrás de este método es que queremos encontrar la pendiente $m$ y la ordenada al origen $b$ que nos dan la recta que minimiza la suma de los cuadrados de las distancias entre los puntos experimentales y la recta ajustada:

min_cuadrados

Es decir, que cuando sumemos el cuadrado de todas las distancias de los puntos a la recta (líneas azules) el valor que obtengamos sea el más pequeño posible (este tipo de problemas se llaman problemas de optimización).

Se pueden ajustar todo tipo de funciones por este método, no sólo rectas, pero para el caso particular de la recta, se obtiene que la pendiente y la ordenada al origen de la recta que minimiza el cuadrado de las distancias se calcula como:

\begin{align} \tag{1} m =\frac{N \sum(x_iy_i) − \sum x_i\sum y_i}{N \sum(x_i^2) − (\sum x_i)^2} \label{eq1}\\ \end{align}\begin{align} b = \frac{\sum y_i − m \sum x_i}{N} \tag{2} \end{align}

en donde $N$ es el número de mediciones o puntos, $x_i$, $y_i$ son las mediciones y las sumas ($\sum$) son sobre todas las mediciones.

Debemos tener cuidado con los puntos atípicos que claramente están mal o son outliers porque pueden arruinar nuestro ajuste. Si ves un punto que claramente está fuera del rango de valores respecto a los demás datos obtenidos puedes no considerarlo en tus cálculos.

Utilizando los vectores X y Y que definimos anteriormente, calculemos la pendiente y ordenada al origen que mejor se ajustan a nuestros resultados. Para eso calculamos todas las sumas que necesitaremos en cada término de las ecuaciones para $m$ y $b$:

In [5]:
N = 7 # numero de mediciones
sum_xy = np.sum(X*Y) # suma de todos los Xi*Yi
sum_x = np.sum(X) # suma de todas las X
sum_y = np.sum(Y) # suma de todas las Y
sum_x2 = np.sum(X**2) # suma de todas las Xˆ2

m = ((N*sum_xy) - (sum_x*sum_y)) / ((N*sum_x2) - (sum_x**2)) # calculamos m usando la eq. 1
b = (sum_y - (m*sum_x)) / N # calculamos m usando la eq. 2
Y_ajuste = m*X+b # Calculamos algunos valores de la recta ajustada 
In [6]:
fig = plt.figure(figsize=(5,5))
ax1= fig.add_subplot(1,1,1)

ax1.plot(X, Y_ajuste, 'r', label='recta min. cuadrados') # Graficar recta ajustada 

ax1.errorbar(X, Y, yerr=dY, xerr=dX, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markersize=8, 
             markerfacecolor='0.5',markeredgecolor='k',ecolor='k', label='mediciones')

ax1.set_xlabel('Tiempo (s)')
ax1.set_ylabel('Desplazamiento (cm)')
ax1.legend()
plt.show()

Algunos puntos a notar:

  • Cuando hacemos operaciones con arreglos, Python aplica la regla de hacer las operaciones elemento a elemento si las dimensiones de los arreglos son iguales. Por ejemplo, el resultado de X*Y es un arreglo con las mismas dimensiones que X y Y (1D, vector) cuyo elemento $x_iy_i$ es el producto del i-ésimo elemento de X y el i-ésimo elemento de Y.
  • La función de numpy sum suma todos los elementos de un arreglo si no especificamos a lo largo de qué dimensión queremos la operación. Funciona igual que las funciones mean, min y max que aprendimos anteriormente.
  • Para graficar la recta $y=mx+b$ a partir de los valores calculados de m y bdebemos tabular algunos valores de $y$. En el caso anterior graficamos el arreglo X y el arreglo Y_ajuste que resulta de la operación m*X+b, usando el vector X que ya teníamos.
  • Si tienes duda, puedes revisar las dimensiones de un arreglo usando arreglo.shape().
In [7]:
print('Las dimensiones de la suma de XY son:')
print(sum_xy.shape)
Las dimensiones de la suma de XY son:
()

sum_xy es un escalar (por eso aparece vacío)

In [8]:
print('Las dimensiones de X son:')
print(X.shape)
Las dimensiones de X son:
(7,)

X es un arreglo 1D con 7 elementos

3. Ejercicio de aplicación: El agua y el aceite

El objetivo de esta actividad es encontrar la relación entre el volumen (variable independiente, $V$) y la masa (variable dependiente, $M$) del agua y el aceite de maiz. Para ello se tomaron 6 muestras de agua y 6 muestras de aceite de distinto volumen con una probeta graduada en mililitros:

Agua:

(V $\pm$ 0.5) ml (M $\pm$ 0.05) g
2 2.1
4 4.0
6 6.2
8 7.9
10 10.1
12 11.9

Aceite de maiz:

(V $\pm$ 0.5) ml (M $\pm$ 0.05) g
2 1.8
4 3.8
6 5.4
8 7.3
10 9.3
12 11.0

3.1 Grafica

Siguiendo el procedimiento de la sección 2, grafica la masa del agua y el aceite como función del volumen con sus respectivas incertidumbres. Pon los datos de ambas sustancias en la misma gráfica. Recuerda etiquetar los ejes e indicar qué marcadores son para cada sustancia.

In [9]:
V_agua = np.array([2,4,6,8,10,12])
M_agua = np.array([2.1,4.0,6.2,7.9,10.1,11.9])
dV_agua = 0.5
dM_agua = 0.05

V_aceite = np.array([2,4,6,8,10,12])
M_aceite = np.array([1.8,3.8,5.4,7.3,9.3,11.0])
dV_aceite = 0.5
dM_aceite = 0.05
In [10]:
fig = plt.figure(figsize=(5,5))
ax1= fig.add_subplot(1,1,1)

ax1.errorbar(V_agua, M_agua, yerr=dM_agua, xerr=dV_agua, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markersize=8, 
             markerfacecolor='lightblue',markeredgecolor='k',ecolor='k', label='agua')

ax1.errorbar(V_aceite, M_aceite, yerr=dM_aceite, xerr=dV_aceite, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markersize=8, 
             markerfacecolor='gold',markeredgecolor='k',ecolor='k', label='aceite')

ax1.set_xlabel('Volumen (ml)')
ax1.set_ylabel('Masa (g)')
ax1.legend()
plt.show()

3.2 Calcula

Una vez que inspecciones la gráfica anterior y, si te parece que la relación entre las variables es lineal, procede a calcular la pendiente y la ordenada al origen de la recta que se ajusta a los datos usando el método de mínimos cuadrados. Calcula estas constantes para cada sustancia (habrá una recta para cada sustancia).

Opcional: Para hacer este cálculo más rápido y reutilizable crea una función min_cuadrados que tome como argumentos los vectores de volumen y masa y devuelva la pendiente y ordenada al origen m y b.

Posteriormente grafica las rectas y los datos con incertidumbres.

In [11]:
def min_cuadrados(X,Y):
    '''Esta función calcula la pendiente y la ordenada al origen de la recta y=mx+b por mínimos 
    cuadrados a partir de los vectores de mediciones X y Y.
    Input: 
    X - arreglo de numpy 1D
    Y - arreglo de numpy 1D del mismo tamaño que X.
    Output:
    m, b : Escalares, la pendiente y ordenada al origen.
    '''
    N = len(X) # numero de mediciones
    sum_xy = np.sum(X*Y) # suma de todos los Xi*Yi
    sum_x = np.sum(X) # suma de todas las X
    sum_y = np.sum(Y) # suma de todas las Y
    sum_x2 = np.sum(X**2) # suma de todas las Xˆ2

    m = ((N*sum_xy) - (sum_x*sum_y)) / ((N*sum_x2) - (sum_x**2))
    b = (sum_y - (m*sum_x)) / N
    return(m, b)
In [12]:
m_agua, b_agua = min_cuadrados(V_agua, M_agua)
m_aceite, b_aceite = min_cuadrados(V_aceite, M_aceite)
In [13]:
fig = plt.figure(figsize=(5,5))
ax1= fig.add_subplot(1,1,1)

ax1.plot(V_agua,m_agua*V_agua+b_agua, color='lightblue', label='min. cuadrados') # Graficar recta ajustada
ax1.errorbar(V_agua, M_agua, yerr=dM_agua, xerr=dV_agua, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markersize=8, 
             markerfacecolor='lightblue',markeredgecolor='k',ecolor='k', label='agua')

ax1.plot(V_aceite,m_aceite*V_aceite+b_aceite, color='gold', label='min. cuadrados') # Graficar recta ajustada
ax1.errorbar(V_aceite, M_aceite, yerr=dM_aceite, xerr=dV_aceite, 
             capsize=5, elinewidth=2, linewidth=0,
             marker='o', markersize=8, 
             markerfacecolor='gold',markeredgecolor='k',ecolor='k', label='aceite')

ax1.set_xlabel('Volumen (ml)')
ax1.set_ylabel('Masa (g)')
ax1.legend()
plt.show()

3.3 Reflexiona y responde

1. ¿Cuáles son las ecuaciones (ajuste de recta) que encontraste para cada sustancia?

Agua: $M=1.0$ g/ml $V+0.1$ g

Aceite: $M=0.9$ g/ml $V-0.0$ g

2. ¿Qué diferencias hay entre las ecuaciones?

a) La pendiente del agua es mayor que la del aceite. b) La ordenada al origen de agua es mayor a la del aceite. De hecho, la del aceite es cero si tomamos el número adecuado de cifras significativas.

3. ¿Cuál es el significado físico de las constantes? (Recuerda que es distinto al matemático, puedes guiarte por las unidades de las constantes)

Pendiente: Densidad de la sustancia, unidades g/ml

Ordenada al origen: Unidades de gramos. La masa de agua o aceite para volumen cero. Físicamente sabemos que debería ser cero porque para un volumen de cero no hay ninguna masa. La b del aceite es efectivamente cero, pero la del agua no (aunque es chiquita). Esto se debe a que tamién hay una incertidumbre asociada tanto a la pendiente como a la ordenada.

4. ¿Cuál es la masa de 10 l de agua?

$M = (1.0$ g/ml$)(10000 $ ml$)+(0.1 $g$) = 10000.1$ g

5. ¿Cuál es la masa de 10 l de aciete?

$M = (0.9$ g/ml$)(10000 $ ml$) = 9000.0$ g

Para las preguntas 4 y 5 tomé como correcto el valor que les haya salido usando las ecuaciones que reportaron en la pregunta 1. Si usaron otra aproximación, la tomé como correcta si justificaron adecuadamente su elección.

6. ¿Por qué no se mezclan el agua y el aceite?

Respuesta corta: Hay dos factores: El caracter polar de las moléculas de agua y la diferencia de densidades entre estas dos sustancias. En los ajustes encontramos que la densidad del agua es mayor a la del aceite.