La figura como Objeto Python

Todo es un objeto en Python

Puedo crear el objeto y luego modificar sus parámetros y agregar lo que quiero graficar. Suele ser más laborioso (no mucho) y las órdenes no son las mismas que las que vimos hasta ahora, pero muy parecidas. La forma de trabajo es que por un lado tengo la figura en sí, y por otro los ejes (ya que puede haber varios) y modifico cada uno con lo que necesito

In [1]:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (4,2.5)  # <- Este comando es para que la 
                                          # versión impresa de este notebook
                                          # tenga las dimensiones 
                                          # correctas, el lector debe 
                                          # ignorarlo.
In [2]:
x = np.linspace(0, 20, 100)
y1 = np.sin(x)
y2 = np.cos(x)

fig = plt.figure()             # Genera un dibujo vacío 
ax = fig.add_subplot(1, 1, 1)  # Especifico (número de filas, columnas,
                               # número de figura) 
In [3]:
fig, ax = plt.subplots()  # Creo la figura como dos objetos: figura 
                          # en si y ejes
ax.plot(x, y1)
ax.plot(x, y2)
ax.set_xlim(0., 2*np.pi)
ax.legend(['seno', 'coseno'], loc='best') 
ax.set_xlabel("$x$")
ax.set_ylabel("$\sin(x)$")
ax.set_title("Yo amo el número $\pi$");

Si se desea conocer más de este comando se puede poner "help(ax)" y generará una respuesta bastante extensa de los diversos comandos y métodos del objeto.

Plots Logarítmicos

Los gráficos logarítmicos se pueden hacer con un eje logaritmico y otro lineal o con ambos ejes logarítmicos

  • Con un eje logarímtico se lo llama semilog"n"() donde n es x o y según sean las abscisas o las ordenadas
  • Con los dos ejes logarítmico es loglog()

Si el eje X es el logarítmico puede hacer el gráfico así, donde queda muy claro su escala

In [4]:
xl = np.logspace(1, 4, 100)
fig, ax = plt.subplots()
ax.semilogx(xl, xl**2);

O dejarlo que ponga del valor del logaritmo en el eje directamente, auqnue en esta forma queda menos claro que clase de magnitud es.

In [5]:
xl = np.logspace(1, 4, 100)
fig, ax = plt.subplots()
ax.plot(np.log10(xl), xl**2);

Puedo hacer lo mismo para el eje de las ordenadas (note que cambio entonces "plt.semilogX" por "plt.semilogY")

In [6]:
fig, ax = plt.subplots()
ax.semilogy(xl, xl**3);
In [7]:
fig, ax = plt.subplots(figsize=(6,6))
ax.loglog(xl, xl**3);
ax.grid(True,which="both",ls=":", c='blue')

Scatter o gráficos con puntos

Hay mucha libertad para elegir el símbolo para el punto, su tamaño y su color veamos un dibujo donde elijo las coordenadas, tamaño y color al azar.

In [8]:
xr = np.random.rand(100)
yr = np.random.rand(100)
cr = np.random.rand(100)
sr = np.random.rand(100)

fig, ax = plt.subplots()
sc = ax.scatter(xr, yr, c=cr, s=30+sr*100, edgecolor='none', alpha=0.5) # colores y tamaños dependen 
                                                                        # del valor de las variables
                                                                        # que en este caso son números
                                                                        # al azar
fig.colorbar(sc);

Calculando $\pi$

Utilizando el método de las piedras. Probaremos arrojando 5000 piedra.

Pero primero recodemos el algoritmo que ya describimos en el apunte de Fortran:

Recordemos que: se denomina frecuencia a la cantidad de veces de que un cierto estado se repita en un experimento frente a todos los experimentos que se han realizado. La frecuencia es algo que se puede medir. Por ejemplo, tirando una moneda y contando la cantidad de veces que sale cara frente a las veces que esa moneda fue arrojada.

La probabilidad de que ese estado en particular suceda es un resultado teórico que se obtendría de repetir infinitamente el experimento. La frecuencia entonces se convierte en probabilidad cuando el número de experimentos tiende a infinito. En este caso vamos a asumir que cuando repito mucho veces un experimento y mido la frecuencia se parecerá bastante a la probabilidad. Es decir, puedo tirar una moneda muchas veces al aire, y medir la frecuencia de que salga cara y no seca. Pero sólo cuando se la tire infinitas veces obtendré la probabilidad del suceso.

Pero como la probabilidad es una definición: prob= casos favorables/casos totales, para una moneda perfecta tengo: una cara/dos posibilidades (cara mas seca). O sea que la probabilidad de obtener cara es $Prob= 0.50$.

Con esta idea de convertir la medida de la fracuencia en probabilidad vamos a calcular el valor de $\pi$ utilizando las siguientes reglas:

  • Tengo un cuadrado perfecto dibujado en el piso y le arrojo piedras. Si una piedra cae afuera la vuelvo a tirar.
  • Tengo un contador que lleva la cuenta de todas las piedras que se arrojan.
  • Dentro de ese cuadro dibujo un círculo centrado en el cuadrado y con el radio tal de que es tangente a todos los lados de cuadrado.
  • Llevo la cuenta de todas piedras que caen en el círculo.
  • Realizo este experimento utilizando la computadora. La posición de la piedra la determino generando dos números al azar con distribución uniforme. Es decir la coordenadas (x,y) del impacto de la piedra son dos números al azar.
  • Repito el experimento muchas veces, con la idea de que cuando más veces mejor.
  • Calculo la frecuencia de que la piedra caiga dentro del círculo. Es decir:

$frec= \frac{piedras\ en\ el\ c\acute{i}rculo}{total\ de\ piedras}$

Viendo este experimento ¿Cuál es la probabilidad de que una piedra quede dentro del círculo? \

$$Prob = \frac{Casos\ favorables}{Total\ de\ casos} = \frac{\acute{A}rea\ del\ c\acute{i}rculo}{\acute{A}rea\ del\ cuadrado} $$

Reemplazando las áreas por su expresiones y haciendo las cuentas:

$$Prob = \frac{\pi R^2}{(2R)^2} = \frac{\pi}{4} $$

Si suponemos (aunque sabemos que se le parecen pero no son lo mismo) que la frecuencia medida es la probabilidad de que la piedra caiga en el círculo, obtenemos que:

$frec \sim \frac{\pi}{4}$ y si despejo $\pi$ obtengo $\pi \sim 4 frec$ donde la frecuencia la obtengo a partir de la simulación. Pero para que esto funcione tenemos que hacer que la frecuencia realmente se parezca a la probabilidad y para que esto suceda tenemos que repetir el experimento muchas veces, en este caso lo haremos 5000 veces.

Veamos cómo sería un programa que realice toda esta tarea. Genere los dos números al azar, vea si estás coordenadas están dentro del círculo, actualice los contadores (piedras en el círculo y cantidad total de piedras), nos de un estimado del valor de $\pi$ que obtuvimos hasta el momento y vuelva a repetir la operación una y otra vez.

In [9]:
npts = 50000

xs = 2*np.random.rand(npts)-1
ys = 2*np.random.rand(npts)-1

r = xs**2+ys**2

ninside = (r<1).sum()

plt.figure(figsize=(6,6)) # Para que la figura sea cuadrada

plt.title("Aproximación a  $\pi$ = %f" % (4*ninside/float(npts)))
plt.plot(xs[r<1],ys[r<1],'b.')
plt.plot(xs[r>1],ys[r>1],'r.')


print("Pi da:", ninside/npts*4)
Pi da: 3.14936

Note que ahora usando la librería NumPy se "tiraron" las priedras no de a una, si no todas en un sólo comando. Por eso no hay ninguna orden tipo "bucle", a diferencia de como se realizó este mismo cálculo en el apunte de Fortran.

Parámetros del gráfico

Para dejar fijos algunos valores "domésticos" del dibujo tenemos dos formas de establecerlos:

Con la orden plc.rc('Lo que queremos cambiar', nuevo valor), donde nuevo valor puede ser una variable en la que está el valor del nuevo parámetro. O la orden:

import matplotlib # cargo TODA la matplotlib

matplotlib.rc('Lo que queremos cambiar', nuevo valor) # modifico un parámetro

Veamos algunos ejemplos:

In [10]:
SMALL_SIZE = 8
MEDIUM_SIZE = 10
BIGGER_SIZE = 12

plt.rc('font', size=SMALL_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)    # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title

# y  lo mismo se puede hacar para los demás parámetros del
# gráfico

## Ejemplo:
plt.plot(x,np.sin(x))
plt.title('Original')
plt.show()

print("")
print("Hago un cambio en los números de los ticks del eje X")
plt.rc('xtick', labelsize=BIGGER_SIZE) 
plt.title('Modifico el eje X')
plt.plot(x,np.sin(x))
plt.show()

print("")
print("y si ahora cambio el eje Y")
plt.rc('ytick', labelsize=BIGGER_SIZE) 
plt.title('Modifico el eje Y')
plt.plot(x,np.sin(x))
plt.show()

# O también lo podría hacer de la forma

import matplotlib
SMALL_SIZE = 8
matplotlib.rc('font', size=SMALL_SIZE)
matplotlib.rc('axes', titlesize=SMALL_SIZE)
Hago un cambio en los números de los ticks del eje X
y si ahora cambio el eje Y

Cálculos y dibujos en Matplolib

Matplotlib tiene también capacidad de hacer cálculos para dibujos específicos. Por ejemplo en el caso de calcular un histograma. Los histogramas son graficos de barras, donde cada barra es la cuenta de cuentos eventos se producen en un cierto intervalo.

Veamos como es esto:

In [11]:
N_points = 100000
n_bins = 20

# Generemos dos distribuciones de números la azar 
# gaussianas (Son los que tiene forma de campana)

dist1 = np.random.normal(0,0.1,N_points)
dist2 = 0.4 * np.random.normal(0,0.6,N_points) + 2

# Con esos datos hago el dibujo de un histograma

fig, axs = plt.subplots(1, 1, sharey=True, tight_layout=True)

axs.hist(dist1, bins=n_bins);
In [12]:
bins = np.histogram(np.hstack((dist1, dist2)), bins=10)[1]
plt.hist(dist1, bins, edgecolor='black')
plt.hist(dist2, bins, edgecolor='black');

Gráficos con distribución de puntos en coordenadas Polares

Este es otro de los gráficos que podemos hacer Para este dibujo enero dos coordenadas r y tita con 150 números al azar cada una

In [13]:
N=150
r = 2* np.random.rand(N)
tita = 2 * np.pi * np.random.rand(N)

# Genero un área para cada punto en función de la coordenada r
area = 200* r**2

# Genero colores que los hago depender del la coordenada anfgular
color = tita

# Creo la figura
fig= plt.figure()

# Y en la figura creo un plot polar
ax = fig.add_subplot(projection='polar') #<-- Notar que uso la proyección polar 
                                         # para activar esta propiedad

# la dibujo

ax.scatter(tita, r, c=color, s=area, cmap='hsv', alpha=0.75);

Incluso podría tomar una región determinado por el ángulo

In [14]:
fig= plt.figure()
ax = fig.add_subplot(projection='polar')
ax.set_thetamin(45)
ax.set_thetamax(135)
ax.scatter(tita, r, c=color, s=area, cmap='hsv', alpha=0.75);

Gráficos múltiples usando un sólo Objeto

In [15]:
import numpy as np
import matplotlib.pyplot as plt

# example data
x = np.arange(0.1, 4, 0.1)
y1 = np.exp(-1.0 * x)
y2 = np.exp(-0.5 * x)

# example variable error bar values
y1err = 0.1 + 0.1 * np.sqrt(x)
y2err = 0.1 + 0.1 * np.sqrt(x/2)


fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, sharex=True,
                                    figsize=(12, 6))

ax0.set_title('all errorbars')
ax0.errorbar(x, y1, yerr=y1err)
ax0.errorbar(x, y2, yerr=y2err)

ax1.set_title('only every 6th errorbar')
ax1.errorbar(x, y1, yerr=y1err, errorevery=6)
ax1.errorbar(x, y2, yerr=y2err, errorevery=6)

ax2.set_title('second series shifted by 3')
ax2.errorbar(x, y1, yerr=y1err, errorevery=(0, 6))
ax2.errorbar(x, y2, yerr=y2err, errorevery=(3, 6))

fig.suptitle('Errorbar subsampling')
plt.show()
In [ ]: