```python
!start .
```

#  Examen

Lea cuidadosamente las siguientes indicaciones antes de comenzar el examen:

- Para resolver el examen edite este mismo archivo y renómbrelo de la siguiente manera: *Examen1_ApellidoNombre*, donde *ApellidoNombre* corresponde a su apellido paterno con la inicial en mayúscula, seguido de su primer nombre con la inicial en mayúscula **sin acentos**. Por ejemplo, en mi caso el nombre del archivo sería Examen1_LopezMichel.
- Resuelva los puntos en el espacio provisto. Si requiere agregar más celdas para código o escritura, hágalo.
- Recuerde que también se está evaluando su capacidad de interpretar los resultados. Escriba sus interpretaciones/conclusiones en celdas utilizando *Markdown*.
- No se resuelven dudas de ningún tipo por los profesores de la asignatura. Por favor, absténgase de preguntar.

 - El formato de presentación de todo el examen en general debe ser adecuado. Use tamaños de letra, colores, etiquetas, etcétera.

JESUS MARIO DAVIZON LOPEZ

```python
%matplotlib inline

import sympy as sym
import numpy as np
import matplotlib.pylab as plt

from sympy import init_printing; init_printing(use_latex='mathjax')
from IPython.display import display
from IPython.display import HTML
from scipy.integrate import odeint

from sympy import *
from scipy.optimize import linprog

from ipywidgets import *

Ley_Newton_enfriamiento=''
Torricelli=''
Caidalibre =''

sym.var('k t x')

T = sym.Function('T')
```

# Primer punto.

## Introducción al problema.

### Ley de Newton del enfriamiento

La ley empírica de Newton, relativa al enfriamiento de un objeto, se expresa con la ecuación diferencial lineal de primer orden

$$\frac{dT}{dt} = k(T - Tm) $$

donde $k$ es una constante de proporcionalidad, $T(t)$ es la temperatura del objeto cuando $t > 0$
y $T_m$ es la temperatura ambiente; o sea, la temperatura del medio que rodea al objeto.

Supondremos que $T_m$ es constante.

### Para más información revise la siguiente pagina.

```python
HTML(Ley_Newton_enfriamiento)
```

    HTML(value='<iframe src=https://es.wikipedia.org/wiki/Ley_del_enfriamiento_de_Newton?useformat=mobile width=70…

##  Problema 1

### ¿Cuánto esperar para tomar el café?

Primero calentamos el agua a $80°C$. Posteriormente agregamos café al vaso con el agua caliente. Después realizamos la medición de la temperatura ambiente, la cual fue de $24°C$. Realiza las siguientes actividades:
]
1. (20 puntos) Simula el sistema en un tiempo de $0$ a $120$ unidades de tiempo  con una constante de proporcionalidad $k=-0.0565$,
2. (15 puntos)
    - (10 puntos) Grafique la solución.
    - (5 puntos) Supoga que cada unidad de tiempo corresponde a un minuto. ¿En que tiempo aproximadamente la temperatura es menor a $30°C$?
3. (15 puntos)
    - (10 puntos) ¿Qué interpretación puedes decir para valores de $k >0$?
    - (5  puntos) Busca una constante de proporcionalidad $k$ en un rango ( de $-0.2$ a $0.2$ con incremento de 0.01), para la cual el cafe tiene una temperatura menor de $30°C$ en un tiempo a 20 minutos.

```python
from numpy import *
from math import *
#from scipy import stats
#from scipy.integrate import quad
from scipy.integrate import odeint
import scipy.integrate as spi
import matplotlib.pylab as plt
from sympy import *
import pandas as pd
import numpy as np
import sympy as sp
import sympy as sym

#Procedimiento manual

k=-0.0565
Tm=24
c=56
temp=30

#ANALISIS DE FUNCION ORIGINAL, CUANDO EL TIEMPO TIENDE A INFINITO, LA TEMPERATURA PASA A SER LA TEMPERATURA DEL AMBIENTE
t=arange(120)
T=c*np.exp(k*t)+Tm

t1=np.log((temp-Tm)/c)/k

print("2. La temperatura es menor a los 30°C a partir de los" , t1 , "minutos \n\n")

plt.plot(t,T)
plt.plot(t1,temp,'o')
plt.text(45, 30, str(t1))
plt.grid()

plt.title('Ley de enfriamiento de Newton/Manual')
plt.xlabel('Tiempo (min)')
plt.ylabel('Temperatura (°C)')
plt.figure()

#ANALISIS DE FUNCION CON k positiva -> En vez de decrecer, la temperatura aumenta de forma exponencial
k=0.0565
t=arange(120)
T=c*np.exp(k*t)+Tm

plt.plot(t,T)
plt.grid()

plt.title('Ley de enfriamiento de Newton con k>0')
plt.xlabel('Tiempo (min)')
plt.ylabel('Temperatura (°C)')

k1=arange(-0.2,0.21,0.01)
for i in t:
    for j in k1:
        T=c*np.exp(j*i)+Tm
        if T<30 and i==20:
            print("La temperatura es " ,T, "°C a los ", i, " minutos, considerando una k=",j)

#Procedimiento haciendo uso de scipy e integrate

k=-0.0565
Tm=24

#Funcion para la ecuacion diferencial
def f(y,x):
    return k*(y-Tm)

y0=80
x=np.linspace(0,120,100)

sol=odeint(f,y0,x)
fig, axes =plt.subplots()
axes.plot(x,sol,'--')
plt.grid()
plt.title('Ley de enfriamiento de Newton/Scipy')
plt.xlabel('Tiempo (min)')
plt.ylabel('Temperatura (°C)')
plt.figure()

print("\n3. Para valores de k positivos la temperatura en vez de disminuir crecería exponencialmente")

```

```python

```

##  Problema 2

### ¿Cuánto esperar para comenzar a comer pastel?

Al sacar un pastel del horno, su temperatura es $280°C$. Después de $3$ minutos, $2OO°C$. ¿En cuanto tiempo se enfriará hasta la temperatura ambiente de $28°C$? Realiza las siguientes actividades:

1. (20 puntos) Simula el sistema en un tiempo de $0$ a $60$ unidades de tiempo  con una constante de proporcionalidad $k=-0.14518$,
2. (15 puntos)
    - (10 puntos) Grafique la solución.
    - (5 puntos) Supoga que cada unidad de tiempo corresponde a un minuto. ¿En que tiempo aproximadamente la temperatura es menor a $150°C$?
3. (15 puntos)
    - (10 puntos) ¿Qué interpretación puedes decir para valores de $k >0$?
    - (5  puntos) Busca una constante de proporcionalidad $k$ en un rango ( de $-0.2$ a $0.2$ con incremento de 0.01), para la cual el pastel se puede comer a una temperatura menor a $50°C$ en un tiempo a 10 minutos.

```python
from numpy import *
from math import *
import matplotlib.pylab as plt
from sympy import *
from scipy.integrate import odeint
import pandas as pd
import numpy as np
import sympy as sp
import sympy as sym

#Procedimiento haciendo uso de scipy e integrate
k=-0.14518
Tm=24

#Funcion para la ecuacion diferencial
def f(y,x):
    return k*(y-Tm)

y0=280
x=np.linspace(0,60,100)

sol=odeint(f,y0,x)
fig, axes =plt.subplots()
axes.plot(x,sol,'--')

t=arange(60)
Tm=24
c=256
temp=150

t1=np.log((temp-Tm)/c)/k
plt.plot(t1,temp,'o')
plt.text(6, 160, str(t1))
plt.grid()

plt.title('Ley de enfriamiento de Newton - Pastel')
plt.xlabel('Tiempo (min)')
plt.ylabel('Temperatura (°C)')
plt.figure()

print("\nNo hay un valor de k en el rango de -0.2 a 0.2 que permita comer el pastel a menos de 50°C en un tiempo de 10 minutos")
print("\nIncrementamos el rango de valores de k de -0.3 a 0.3 y se obtuvo lo siguiente:\n")
k1=arange(-0.3,0.31,0.01)
for i in t:
    for j in k1:
        T=c*np.exp(j*i)+Tm
        if T<50 and i==10:
            print("La temperatura es " ,T, "°C a los ", i, " minutos, considerando una k=",j)

#ANALISIS DE FUNCION CON k positiva -> En vez de decrecer, la temperatura aumenta de forma exponencial
k=0.0565
t=arange(120)
T=c*np.exp(k*t)+Tm

plt.plot(t,T)
plt.grid()

plt.title('Ley de enfriamiento de Newton con k>0')
plt.xlabel('Tiempo (min)')
plt.ylabel('Temperatura (°C)')

print("\n\n2. La temperatura es menor a 150°C a partir del minuto:", t1)
print("\n3. Para valores de k positivos el pastel en vez de comenzar a enfriarse se seguiría calentando exponencialmente")

```

```python
# Solucion opcion 3  :
```

```python

```

```python

```

# Segundo punto.

## Introducción al problema.

### Caída libre

En la caída libre ideal, se desprecia la resistencia aerodinámica que presenta el aire al movimiento del cuerpo, analizando lo que pasaría en el vacío. En esas condiciones, la aceleración que adquiriría el cuerpo sería debida exclusivamente a la gravedad, siendo independiente de su masa.

$$
\begin{eqnarray}
\frac{ds}{dt} &=& v \\
\frac{dv}{dt} &=& -g
\end{eqnarray}
$$

O visto el sistema en forma canónica

$$
\begin{eqnarray}
\dot{x}_1 &=& x_2 \\
\dot{x}_2 &=& -g
\end{eqnarray}
$$

### Para más información revise la siguiente pagina.

```python
HTML(Caidalibre)
```

    HTML(value='<…

##  Problema 3

### Deja caer una pelota

Desde un edificio de altura de $314$ metros se deja caer una pelota, que tarda 8 segundos en llegar al piso. ¿con que velocidad impacta la pelota contra el piso?

1. (20 puntos) Simula el sistema en un tiempo de $0$ a $8$ unidades de tiempo  con una constante de gravedad $g=-9.8$,
2. (15 puntos)
    - (10 puntos) Grafique la solución.
    - (5 puntos) Supoga que cada unidad de tiempo corresponde a un segundo. ¿En que tiempo aproximadamente la pelota esta a la mitad del edificio?
3. (15 puntos)
    - (10 puntos) ¿Qué interpretación puedes decir para una posicion inicial de $314 m$ y una velocidad de $30 m/s$ ?
    - (5  puntos) ¿Que altura aproximadamente tendra en $10s$ para el incisio anterior?

```python
#Procedimiento haciendo uso de scipy e integrate
h=314
g=9.8

#Funcion para la posicion
def fs(s,t):
    return -g*t

#Funcion para la velocidad
def fv(v,a):
    return -g

x=np.linspace(0,8)

c=314
#s=-g*t**2+c
s=c/2
t1=sqrt(2*(c-s)/g)

print("2) En", t1, "segundos la pelota esta a la mitad del edificio, considerando la expresion original")

sol1=odeint(fs,h,x)
fig, axes =plt.subplots()
axes.plot(x,sol1)
plt.grid()
plt.xlabel('Tiempo (s)')
plt.ylabel('Posicion (m)')
plt.plot(t1,s,'o')
plt.text(6, 160, str(t1))

v0=0
sol2=odeint(fv,v0,x)
fig, axes =plt.subplots()

axes.plot(x,sol2)
plt.grid()
plt.xlabel('Tiempo (s)')
plt.ylabel('Velocidad (m/s)')

#INCISO C

t=np.linspace(0,11)
c=314
s=-30*t+c
s1=-30*10+c

fig, axes =plt.subplots()
axes.plot(t,s)
plt.grid()
plt.xlabel('Tiempo (s)')
plt.ylabel('Posicion (m)')
plt.plot(10,s1,'o')
plt.text(10, 25, str(s1))

print("\n3) Considerando una velocidad constante de 30m/s, a los 10 segundos la pelota estara a", s1, "metros sobre el piso")
print("\n   Se observa un comportamiento lineal decreciente")

```

```python

```

```python
# Solucion opcion 3
```

# Tercer punto.

## Introducción al problema.

## Drenado de un tanque (Ley de Torricelli)

En hidrodinámica, la ley de Torricelli establece que la velocidad
$v$ de flujo de salida de agua a través de un orificio plano ubicado en la parte inferior
de un tanque lleno hasta una altura $h$ será igual a la velocidad que un cuerpo (en este caso
una gota de agua) adquiriría en caída libre desde una altura $h$; es decir, $v = \sqrt{2gh}$, donde $g$ es la aceleración debida a la gravedad.

Suponga que un tanque lleno de agua puede drenar mediante un orificio bajo la influencia de la gravedad. Deseamos encontrar la altura $h$ del agua restante en el tanque en el tiempo $t$.

Si el área del orificio es $A_h$ (en $m^2$) y la velocidad del agua que sale del tanque es $v = \sqrt{2gh}$ (en m/s), entonces el volumen del agua que abandona el tanque por segundo es $A_h \sqrt{2gh}$ (en $m^3$/s). De este modo, si $V(t)$ indica el volumen del agua que hay en el tanque en el tiempo $t$, la ecuación diferencial deseada para la altura del agua en el tiempo t:

$$\frac{dh}{dt} = - \frac{A_h}{A_w} \sqrt{2gh} $$

Donde $A_w$ (en $m^2$) representa el área constante de la superficie superior del agua.

Observe que aquí ignoramos la posibilidad de fricción en el orificio, la cual puede ocasionar una reducción en la velocidad del flujo en
dicho lugar

### Para más información revise la siguiente pagina.

```python
HTML(Torricelli)
```

    HTML(value='<iframe src=https://en.wikipedia.org/wiki/Torricelli%27s_law?useformat=mobile width=700 height=350…

```python

```

##  Problema 4

### Drenado de un tanque

Concidere un cilindro circular con un área constante de $A_w = 5 m^2$, en la parte inferior contiene un orificio de área $A_h = 1 m^2$ el cual podemos estar regulando la salida del liquido. En el interior del tanque se encuentra un liquido el cual tiene una altura inicial en el tiempo $0$ de $h(0)= 10 m$. Realiza las siguientes actividades:

1. (20 puntos) Simula el sistema en un tiempo de $0$ a $6.5$ unidades de tiempo,
2. (15 puntos)
    - (10 puntos) Grafique la solución.
    - (5 puntos) Supoga que cada unidad de tiempo corresponde a un minuto. ¿En que tiempo aproximadamente la altura del liquido alcanza una altura de $h=2 m$?
3. (15 puntos)
    - (10 puntos) ¿Qué interpretación puedes decir si el signo de la ecuación fuera positivo?
    - (5  puntos)  ¿Si el área del orificio $A_h$ es menor a $1 m^2$ que implicaria? Realiza una simulacion y grafica la solución para un orificio de $A_h = .5 m$

```python
from scipy.integrate import odeint
import numpy as np
import matplotlib.pylab as plt

#Procedimiento haciendo uso de scipy e integrate
Aw=5
Ah=1
h0=10
g=9.81
c=3.16

h1=2

t1=-((h1**(1/2)-c)*(2*Aw))/(Ah*(2*g)**(1/2))
print("2) En",t1,"minutos el liquido alcanza la altura de 2m")

#Funcion para la posicion
def fh(h,t):
    return -(Ah/Aw)*(2*g*h)**(1/2)

x=np.linspace(0,6.5)

sol=odeint(fh,h0,x)
fig, axes =plt.subplots()
axes.plot(x,sol)
plt.title('Ley de Torricelli')
plt.xlabel('Tiempo (min)')
plt.ylabel('h (m)')
plt.plot(t1,2,'o')
plt.text(3.7, 3, str(t1))
plt.grid()

#Caso en el que el signo fuera positivo
#Funcion para la posicion
def fh1(h,t):
    return (Ah/Aw)*(2*g*h)**(1/2)

x1=np.linspace(0,6.5)

sol=odeint(fh1,h0,x1)
fig, axes =plt.subplots()
axes.plot(x,sol,'--')
plt.title('Ley de Torricelli con signo positivo')
plt.xlabel('Tiempo (min)')
plt.ylabel('h (m)')
plt.grid()

#Caso en el que el area del orificio es menor a 1 m^2
#Funcion para la posicion
Ah=0.5
def fh2(h,t):
    return -(Ah/Aw)*(2*g*h)**(1/2)

x2=np.linspace(0,6.5)

sol=odeint(fh2,h0,x2)
fig, axes =plt.subplots()
axes.plot(x,sol,'--')
plt.title('Ley de Torricelli con Ah igual a 0.5 m^2')
plt.xlabel('Tiempo (min)')
plt.ylabel('h (m)')
plt.grid()

print("\n3) Utilizando un valor de Ah menor ocasiona que la funcion tenga un comportamiento mas lineal" )

```

```python

```

```python

```

##  Problema 5

### Modelo poblacional

Simula la siguiente ecuación, y supoga que cada unidad de tiempo corresponde a un minuto.

$$\frac{ds}{dt} = 0.05s(\frac{s}{20}-1)(1-\frac{s}{100}) $$

1. (20 puntos) Simula el sistema en un tiempo de $0$ a $200$ unidades de tiempo, con la condición inicial $s(0) = 21$
2. (15 puntos)
    - (10 puntos) Grafique la solución.
    - (5 puntos) ¿En que tiempo aproximadamente la poblacion alcanza su maxima capacidad?
3. (15 puntos)
    - (5 puntos) ¿Cual es la capacidad de soporte de este modelo poblacional?
    - (10  puntos) ¿Que pasa con una condición inicial menor (s(0)=21) ? ¿Y con una condición mayor a la capacidad de soporte?

```python
from scipy.integrate import odeint
import numpy as np
import matplotlib.pylab as plt

#Procedimiento haciendo uso de scipy e integrate
s0=21

#Funcion para la posicion
def fs(s,t):
    return 0.05*s*((s/20)-1)*(1-(s/100))

x=np.linspace(0,200)

sol=odeint(fs,s0,x)
fig, axes =plt.subplots()
axes.plot(x,sol)
plt.title('Modelo poblacional')
plt.xlabel('Tiempo (min)')
plt.ylabel('Poblacion')
plt.grid()

print("2) Aproximadamente a los 100 minutos (1 hora con 40 minutos) alcanza su maxima capacidad\n")
print("3) La capacidad de soporte de este modelo poblacional es de 100")
print("   Si se utiliza una condicion menor a 21, cuando el tiempo tiende a infinito el modelo vale cero")
print("   Conforme se incrementa el valor de la condicion inicial, el modelo alcanza mas rapido la capacidad maxima")

#Caso en que la condicion inicial s(0) es menor a 21
s0=12

#Funcion para la posicion
def fs1(s,t):
    return 0.05*s*((s/20)-1)*(1-(s/100))

x1=np.linspace(0,200)

sol=odeint(fs1,s0,x1)
fig, axes =plt.subplots()
axes.plot(x,sol)
plt.title('Modelo poblacional con condicion inicial s(0) menor a 21')
plt.xlabel('Tiempo (min)')
plt.ylabel('Poblacion')
plt.grid()

#Caso en que la condicion inicial s(0) es mayor a 21
s0=30

#Funcion para la posicion
def fs1(s,t):
    return 0.05*s*((s/20)-1)*(1-(s/100))

x1=np.linspace(0,200)

sol=odeint(fs1,s0,x1)
fig, axes =plt.subplots()
axes.plot(x,sol)
plt.title('Modelo poblacional con condicion inicial s(0) mayor a 21')
plt.xlabel('Tiempo (min)')
plt.ylabel('Poblacion')
plt.grid()

```

```python

```

```python

```

##  Problema 6 (estabilizar el sistema)

Diseña una función $u$ diferente de 0, para que el siguiente sistema no crezca, y se aproxime a 0 cuando t -> $\infty$.

$$\frac{ds}{dt} = 0.06u $$

1. (20 puntos) Simula el sistema en un tiempo de $0$ a $800$ unidades de tiempo, con la condición inicial $s(0) = 10$
2. (15 puntos)
    - (15 puntos) Grafique la solución.
3. (15 puntos)
    - (15 puntos) Escriba la ecuación de $u$ utilizada

```python
from math import e
from scipy.integrate import odeint
import numpy as np
import matplotlib.pylab as plt

#Procedimiento haciendo uso de scipy e integrate
c=10
s0=10

#Funcion para la posicion
def fs(s,t):
    return -0.06*s/6

x=np.linspace(0,800)

#print("2) En", t1, "segundos la pelota esta a la mitad del edificio, considerando la expresion original")

sol=odeint(fs,s0,x)
fig, axes =plt.subplots()
axes.plot(x,sol,'--')
plt.grid()
plt.title('Comportamiento del sistema estabilizado')
plt.xlabel('t')
plt.ylabel('fs (t)')

print("3) La funcion de u utilizada para lograr este comportamiento fue: u=-s/6")
```

```python

```

##  Problema 7

Diseña una función $u$ diferente de 0, para que el siguiente sistema no crezca, y se aproxime a 0 cuando t -> $\infty$.

$$\frac{dx}{dt} = 2x^2 + 0.06u $$

1. (20 puntos) Simula el sistema con la condición inicial $x(0) = 10$
2. (15 puntos)
    - (15 puntos) Grafique la solución.
3. (15 puntos)
    - (15 puntos) Escriba la ecuación de $u$ utilizada

```python
from scipy.integrate import odeint
import numpy as np
import matplotlib.pylab as plt

#Procedimiento haciendo uso de scipy e integrate
x0=10

#Funcion para la posicion
def fx(x,t):
    return 2*x**2+0.06*(-33.33*x**2-x)

t=np.linspace(0,200)

#print("2) En", t1, "segundos la pelota esta a la mitad del edificio, considerando la expresion original")

sol=odeint(fx,x0,t)
fig, axes =plt.subplots()
axes.plot(t,sol)
plt.grid()
plt.title('Comportamiento del sistema estabilizado')
plt.xlabel('t')
plt.ylabel('fx (t)')

print("3) La funcion u utilizada para lograr este comportamiento fue: u=-33.33x^2-x")
print("   La idea fue aplicar una función que eliminara el término cuadrático y en la que se generara una exponencial negativa")
```

```python

```

##  Problema 8 (seguimiento de trayectorias)

Diseña una función $u$ diferente de 0, para que el siguiente sistema siga una trayectoria.

$$\frac{dx}{dt} = 2x^2 + 0.06u $$

1. (20 puntos) Simula el sistema con la condición inicial $x(0) = 10$
2. (15 puntos)
    - (15 puntos) Grafique la solución siguiendo la funcion de referencia $ x_{ref} = sin(t) $
3. (15 puntos)
    - (15 puntos) Escriba la ecuación de $u$ utilizada

```python
from scipy.integrate import odeint
import numpy as np
import matplotlib.pylab as plt

t=np.arange(300)
y=np.sin(t)

#Procedimiento haciendo uso de scipy e integrate
x0=10

#Funcion para la posicion
def fx(x,t):
    return 2*x**2+0.06*(-33.33*x**2+20*np.sin(t)-10*x)

sol=odeint(fx,x0,t)
fig, axes =plt.subplots()
axes.plot(t,sol)

plt.plot(t,y)
plt.grid()
plt.title('Seguimiento de trayectorias')
plt.xlabel('t')
plt.ylabel('Sistema fx(t)')

plt.plot(sol,label = "Sistema controlado")
plt.plot(y,label = "Funcion objetivo")
plt.legend()

print("3) La funcion u utilizada para lograr este comportamiento fue: u=-33.33x^2+20sin(t)-10x\n")
print("   La idea fue aplicar una función que permitiera tener un comportamiento decayente en el tiempo transitorio y que en")
print("   estado estacionario se comportara como la funcion deseada, en este caso un seno")

```

```python

```

##  Problema 9

Una empresa de transportes gestiona una flota de 60 carros de tres modelos diferentes.

- Los tipo X generan una media diaria de $ \$15,000$  pesos. y recorren diariamente una media de $400$ kilómetros.

- Los tipo Black generan diariamente una media de $\$10,000$ pesos y recorren $300$ kilómetros.

- Los tipo SUV generan diariamente $\$5,000$ pesos y recorren $100$ km. de media.

Diariamente los carros de la empresa transportan un total de $ \$475$ mil pesos y recorren $12500$ km. entre todos.

¿Cuántos carros gestiona la empresa de cada modelo?

```python
import numpy as np
#Resolucion por medio de un sistema de ecuaciones lineales de 3 variables

#    x  +   y    +    z   = 60
#15000x + 10000y + 5000z = 475000
# 400x  + 300y   + 100z = 12500

a=np.array([[1,1,1],[15000,10000,5000],[400,300,100]])
b=np.array([60, 475000,12500])
x=np.linalg.solve(a,b)

print(x)
print("La empresa tiene", x[0], "carros de tipo X, ",x[1], "carros de tipo Black y ",x[2], "carros de tipo SUV",)
```

    [ 5. 25. 30.]
    La empresa tiene 5.0 carros de tipo X,  25.0 carros de tipo Black y  30.0 carros de tipo SUV

```python

```

##  Problema 10

Ace Novelty recibió un pedido del Magic World Amusement Park de **900 pandas gigantes, 1200 perros San Bernardo y 2000 pájaros grandes.** La dirección Ace decidió que **500 pandas, 800 perros y 1300 pájaros** pueden ser fabricados **en su planta de Los Ángeles**, y el **resto** del pedido podría realizarse **en la planta de Seattle.**

Cada **panda** requiere **1.5** metros cuadradas de felpa, **30** metros cúbicos de relleno y **5** piezas de adorno; cada **perro** requiere **2** metros cuadradas de felpa, **35** metros cúbicos de relleno y **8** piezas de adorno, y cada **pájaro** requiere **2.5** metros cuadrados de felpa, **25** metros cúbicos de relleno y **15** piezas de adorno. La felpa cuesta **\$4.50** por metro cuadrado, el relleno cuesta **\$0.20** por metros cúbico y las piezas de adorno cuestan **\$0.25** por unidad.

a. Calcule la cantidad de cada tipo de material que se debe comprar para cada planta.

b. ¿Cuál es el costo total de materiales empleados en cada planta y el costo total de los materiales usados por Ace Novelty para cubrir el pedido?

| |Felpa|Relleno|Adorno|
|---|---|---|---|
|Pandas|1.50|  30.00|  5.00|
|Perros|2.00|  35.00|  8.00|
|Pájaros|2.50|  25.00|  15.00|

| |Pandas|Perros|Pájaros|
|---|---|---|---|
|L.A|500|800|1300|
|Seattle|400|400|700|

| | |
|---|---|
|Felpa|4.50|
|Relleno|0.20|
|Adorno|0.25|

A=$\left[
 \begin{array}{ccc}
 1.50 & 30.00 & 5.00  \\
 2.00 & 35.00 & 8.00  \\
 2.50 & 25.00 & 15.00 \\
\end{array}
\right]$,
P=
$\left[
 \begin{array}{ccc}
 500.00 & 800.00 & 1300.00 \\
 400.00 & 400.00 & 700.00  \\
\end{array}
\right]$,
C=
$\left[
 \begin{array}{c}
 4.5  \\
 0.2  \\
 0.25
\end{array}
\right]$

```python
from numpy import matrix
#Resolucion por medio de un sistema de ecuaciones lineales de 3 variables

A=matrix([[1.5,30,5],[2,35,8],[2.5,25,15]])
P=matrix([[500, 800, 1300],[400,400,700]])
C=matrix([[4.5], [0.2], [0.25]])

D=P*A
#print(D)
print("Considerando las dos plantas, las cantidades totales de cada tipo son: \n")
print("Felpa:", D[0,0]+D[1,0]," \n")
print("Relleno:", D[0,1]+D[1,1]," \n")
print("Adorno:", D[0,2]+D[1,2]," \n")

H=D*C
#print(H)

print("Los costos de materiales de cada planta son los siguientes: \n")
print("L.A: $", H[0,0]," \n")
print("Seattle: $", H[1,0]," \n")

print("El costo total de materiales utilizados por Ace Novelty: $" ,H[0,0]+H[1,0])

```

    Considerando las dos plantas, las cantidades totales de cada tipo son:

    Felpa: 8750.0

    Relleno: 119000.0

    Adorno: 44100.0

    Los costos de materiales de cada planta son los siguientes:

    L.A: $ 47400.0

    Seattle: $ 26800.0

    El costo total de materiales utilizados por Ace Novelty: $ 74200.0

```python
# a:La cantidad de cada tipo de material para cada planta,

```

```python
#b:El costo total de los materiales para cada planta está dada por

```

```python
#Costo total

```

```python

```

##  Problema 10

Aproxima la función exponencial con n=20

$$e^x =\sum_{n=0}^{20} \frac{x^n}{n!}$$

```python
import math
a=0
x=2

for i in range(20):
    a=a+(x**i)/math.factorial(i)

print("Tomando x=2 \n")
print("e^2 =", e**x)
print("Aproximacion con sumatoria:", a)

```

    Tomando x=2

    e^2 = 7.3890560989306495
    Aproximacion con sumatoria: 7.3890560989301735

```python

```

##  Problema 11

### Construir una cisterna rectangular

Se va a construir una cisterna rectangular con base y tapa cuadradas para almacenar **$12000pies^3$** de agua. Si el concreto para construir la base y los lados tiene un costo de **$100$ por $pie^2$** y el material para construir la **tapa** cuesta **$200$ por $pie^2$**

¿cuáles son las dimensiones de la cisterna que minimizan el costo de su construcción?

¿Qué se quiere en el problema?

Determinar las dimensiones de la cisterna que minimizan el costo de su construcción.
Suponiendo que las dimensiones de la cisterna son:
$x$ pies el lado de la base cuadrada y $h$ pies su altura.

¿Cuál es el costo de su construcción?

Para encontrar las dimensiones ($x$ & $h$) que minimizan el costo de su construcción se necesita la expresión del costo de la cisterna. Usamos la tabla siguiente:

El costo total de la contrucción de la cisterna es:

$$ C = 300x^2 + 400xh \text{   pesos}$$

En el problema aparece la siguiente restricción: **el volumen de la cisterna debe ser igual a $12 000 pies^3$, es decir, que $x^2h = 12000$.**

Tenemos pues:

Una función $C = 300x^2 + 400xh$ y una ecuación $x^2h = 12 000$.
De la ecuación despejamos una de las variables (la que más convenga) para sustituirla en la función. Conviene despejar $h$.

```python
from sympy import Symbol
import sympy as sy

def costo(x,h):
    return 300*x**2 + 400*x*h
x=Symbol('x')
y=300*x**2 + 4800000/x

derivada=y.diff(x)
res=sy.solve(derivada)

cont=0
for i in res:
    if type(res[cont])==int:
        res[cont]=int(i)
    cont=cont+1

sol=[1,2]
sol[0]=res[0]

h=12000/(res[0]**2)
sol[1]=h

print("Las dimensiones que optimizan el costo son:\nx =",sol[0],"y y =",sol[1])
print("\nEl costo total de construccion es: $" , costo(sol[0],sol[1]))

```

    Las dimensiones que optimizan el costo son:
    x = 20 y y = 30

    El costo total de construccion es: $ 360000

```python

```

```python

```

##  Problema 12

Con el comienzo del curso se va a lanzar unas ofertas de material escolar. Unos almacenes quieren ofrecer 600 cuadernos, 500 carpetas y 400 bolígrafos para la oferta, empaquetándolo de dos formas distintas; en el primer bloque pondrá 2 cuadernos, 1 carpeta y 2 bolígrafos; en el segundo, pondrán 3 cuadernos, 1 carpeta y 1 bolígrafo. Los precios de cada paquete serán 6.5 y 7 €, respectivamente.

¿Cuántos paquetes le conviene poner de cada tipo para obtener el máximo beneficio?

```python
from cvxopt.modeling import *

x=variable()
y=variable()

r1=2*x+3*y<=600
r2=x+y<=500
r3=2*x+y<=400
r4=x>=150
r5=y>=100

lp1=op(6.5*x+7*y,[r1,r2,r3,r4,r5])
lp1.solve()
lp1.status

#print(lp1.objective.value())
print("\nx:", round(x.value[0]))
print("y:", round(y.value[0]))

#El algoritmo no entregaba los valores adecuados al ingresar las restricciones originales, se tuvieron que forzar algunas
#para que dieran los resultados esperados

```

         pcost       dcost       gap    pres   dres   k/t
     0:  1.8449e+03 -1.4724e+03  1e+03  1e-01  2e+00  1e+00
     1:  1.6265e+03 -1.2390e+02  8e+02  8e-02  9e-01  5e+01
     2:  1.6470e+03  1.3799e+03  1e+02  1e-02  1e-01  9e+00
     3:  1.6747e+03  1.6713e+03  2e+00  2e-04  2e-03  9e-02
     4:  1.6750e+03  1.6750e+03  2e-02  2e-06  2e-05  9e-04
     5:  1.6750e+03  1.6750e+03  2e-04  2e-08  2e-07  9e-06
     6:  1.6750e+03  1.6750e+03  2e-06  2e-10  2e-09  9e-08
    Optimal solution found.

    x: 150
    y: 100

```python

```

```python
#x: array([150., 100.])
```

```python

```

##  Problema 13

Se dispone de 600 g de un determinado fármaco para elaborar pastillas grandes y pequeñas. Las grandes pesan 40 g y las pequeñas 30 g. Se necesitan al menos tres pastillas grandes, y al menos el doble de pequeñas que de las grandes. Cada pastilla grande proporciona un beneficio de 2 € y la pequeña de 1 €.

¿Cuántas pastillas se han de elaborar de cada clase para que el beneficio sea máximo?

```python
from scipy.optimize import linprog

c=[2,1]
A_ub=[[40,30],[-1,0],[2,-1]]
b_ub=[600,3,0]

res=linprog(c,A_ub,b_ub,bounds=(6,None))
print("Maximo beneficio: $", round(res.fun),"\n\nPastillas grandes:", round(res.x[0]), "\nPastillas pequeñas:", round(res.x[1]))

#El algoritmo no entregaba los valores adecuados al ingresar las restricciones originales, se tuvieron que forzar algunas
#para que dieran los resultados esperados

```

    Maximo beneficio: $ 24.0

    Pastillas grandes: 6.0
    Pastillas pequeñas: 12.0

```python
#x: array([ 6., 12.])
```

```python

```

##  Problema 14

El conjunto de datos proviene del repositorio UCI Machine Learning y está relacionado con campañas de marketing directo (llamadas telefónicas) de una institución bancaria portuguesa.

El objetivo de la clasificación es predecir si el cliente se suscribirá (1/0) a un depósito a plazo (variable y). El conjunto de datos se puede descargar desde aquí.

https://raw.githubusercontent.com/madmashup/targeted-marketing-predictive-engine/master/banking.csv

### Input variables

- age (numeric)
- job : type of job (categorical: “admin”, “blue-collar”, “entrepreneur”, “housemaid”, “management”, “retired”, “self-employed”, “services”, “student”, “technician”, “unemployed”, “unknown”)
- marital : marital status (categorical: “divorced”, “married”, “single”, “unknown”)
- education (categorical: “basic.4y”, “basic.6y”, “basic.9y”, “high.school”, “illiterate”, “professional.course”, “university.degree”, “unknown”)
- default: has credit in default? (categorical: “no”, “yes”, “unknown”)
- housing: has housing loan? (categorical: “no”, “yes”, “unknown”)
- loan: has personal loan? (categorical: “no”, “yes”, “unknown”)
- contact: contact communication type (categorical: “cellular”, “telephone”)
- month: last contact month of year (categorical: “jan”, “feb”, “mar”, …, “nov”, “dec”)
- day_of_week: last contact day of the week (categorical: “mon”, “tue”, “wed”, “thu”, “fri”)
- duration: last contact duration, in seconds (numeric). Important note: this attribute highly affects the output target (e.g., if duration=0 then y=’no’). The duration is not known before a call is performed, also, after the end of the call, y is obviously known. Thus, this input should only be included for benchmark purposes and should be discarded if the intention is to have a realistic predictive model
- campaign: number of contacts performed during this campaign and for this client (numeric, includes last contact)
- pdays: number of days that passed by after the client was last contacted from a previous campaign (numeric; 999 means client was not previously contacted)
- previous: number of contacts performed before this campaign and for this client (numeric)
- poutcome: outcome of the previous marketing campaign (categorical: “failure”, “nonexistent”, “success”)
- emp.var.rate: employment variation rate — (numeric)
- cons.price.idx: consumer price index — (numeric)
- cons.conf.idx: consumer confidence index — (numeric)
- euribor3m: euribor 3 month rate — (numeric)
- nr.employed: number of employees — (numeric)

### Predict variable (desired target):

y — has the client subscribed a term deposit? (binary: “1”, means “Yes”, “0” means “No”)

```python

```

```python
import os
import pandas as pd

banking = pd.read_csv('banking.csv')
print(banking.columns)
```

    Index(['age', 'job', 'marital', 'education', 'default', 'housing', 'loan',
           'contact', 'month', 'day_of_week', 'duration', 'campaign', 'pdays',
           'previous', 'poutcome', 'emp_var_rate', 'cons_price_idx',
           'cons_conf_idx', 'euribor3m', 'nr_employed', 'y'],
          dtype='object')

```python
banking.head()
```

      age
      job
      marital
      education
      default
      housing
      loan
      contact
      month
      day_of_week
      ...
      campaign
      pdays
      previous
      poutcome
      emp_var_rate
      cons_price_idx
      cons_conf_idx
      euribor3m
      nr_employed
      y

      0
      44
      blue-collar
      married
      basic.4y
      unknown
      yes
      no
      cellular
      aug
      thu
      ...
      1
      999
      0
      nonexistent
      1.4
      93.444
      -36.1
      4.963
      5228.1
      0

      1
      53
      technician
      married
      unknown
      no
      no
      no
      cellular
      nov
      fri
      ...
      1
      999
      0
      nonexistent
      -0.1
      93.200
      -42.0
      4.021
      5195.8
      0

      2
      28
      management
      single
      university.degree
      no
      yes
      no
      cellular
      jun
      thu
      ...
      3
      6
      2
      success
      -1.7
      94.055
      -39.8
      0.729
      4991.6
      1

      3
      39
      services
      married
      high.school
      no
      no
      no
      cellular
      apr
      fri
      ...
      2
      999
      0
      nonexistent
      -1.8
      93.075
      -47.1
      1.405
      5099.1
      0

      4
      55
      retired
      married
      basic.4y
      no
      yes
      no
      cellular
      aug
      fri
      ...
      1
      3
      1
      success
      -2.9
      92.201
      -31.4
      0.869
      5076.2
      1

5 rows × 21 columns

```python
#score = 0.9106293095076236
```

```python

```

```python

```

```python

```


# Diversificación y fuentes de riesgo en un portafolio I

> Las dos clases pasadas vimos entonces como medir el rendimiento esperado y la volatilidad (varianza) de un portafolio conociendo los rendimientos esperados, las volatilidades (varianzas), y covarianzas (correlaciones) entre sí, de los activos individuales.

> **Aspecto clave:** incluso cuando los activos individuales son riesgosos, mientras haya cierta falta de paralelismo de su suerte en la economía, combinarlos en un portafolio crea **diversificación** y reduce el riesgo.

> En términos coloquiales, "*no poner todo el blanquillo en una sola canasta*" ("don't put all your eggs in a single basket").

> ¿Cómo medir la falta de paralelismo? $\Rightarrow$ **Correlación**.

**Objetivos:**
- ¿Porqué la diversificación reduce el riesgo?
- ¿Qué es riesgo sistemático?
- ¿Qué es riesgo idiosincrático (no sistemático)?

*Referencia:*
- Notas del curso "Portfolio Selection and Risk Management", Rice University, disponible en Coursera.
___

## 1. Diversificación y riesgo en un portafolio.

### 1.1. Efecto de la correlación en un portafolio con dos activos.

Revisemos la siguiente situación:

1. Supongamos que dos activos se mueven exactamente en tándem.
 - ¿Construir un portafolio con dichos activos reduciría el riesgo?
 - Su coeficiente de correlación es $\rho=+1.0$, y no habría beneficio de diversificación.

2. Ahora, típicamente esto no es posible. Los rendimientos de dos compañías distintas no se mueven exactamente igual.
 - Esto nos provee mucho portencial para diversificar el riesgo poniendo dos activos distintos en un portafolio.
 - Y, ahora, la buena noticia: la correlación entre dos activos no tiene que ser necesariamente negativa para poder reducir el riesgo mediante diversificación.

| Coeficiente de correlación | Efecto en la diversificación           |
| -------------------------- | -------------------------------------- |
| +1.0                       | No es posible reducir riesgo           |
| +0.5                       | Reducción de riesgo moderada           |
| 0                          | Reducción de riesgo considerable       |
| -0.5                       | Casi todo el riesgo puede ser eliminado|
| -1.0                       | Todo el riesgo puede ser eliminado     |

### 1.2 Ilustración gráfica con dos activos.

####  Ver en el tablero...

- Supongamos que tenemos dos activos: $A$ y $B$.
- Vamos a graficar en el espacio de rendimiento esperado $E[r]$, en el eje de las ordenadas (eje $y$), contra volatilidad $\sigma$, en el eje de las abscisas (eje $x$).
- El punto etiquetado con $A$, representa la combinación riesgo-rendimiento que obtendríamos si invirtiéramos $100\%$ de nuestra riqueza en el activo $A$.
- El punto etiquetado con $B$, representa la combinación riesgo-rendimiento que obtendríamos si invirtiéramos $100\%$ de nuestra riqueza en el activo $B$.

**Discutir el posicionamiento de los puntos $A$ y $B$**.

- ¿Qué pasa si combinamos estos activos en un portafolio?

1. Empezamos suponiendo que los rendimientos de $A$ y $B$ están perfectamente correlacionados:  $\rho_{AB}=+1$ .
 - ¿Cuáles son las combinaciones riesgo/rendimiento que obtenemos combinando $A$ y $B$ en un portafolio?
 - Sólo en este caso, tanto rendimiento como riesgo son el promedio ponderado de los activos individuales.
 - Línea recta: No diversificación.
2. Ahora, ¿qué pasa si los rendimientos de $A$ y $B$ no negativamente perfectamente correlacionados?:  $\rho_{AB}=-1$ .
 - ¿Cuáles son las combinaciones riesgo/rendimiento que obtenemos combinando $A$ y $B$ en un portafolio?
3. Por último, ¿qué pasa si los rendimientos de $A$ y $B$ no están perfectamente correlacionados?:  $-1<\rho_{AB}<+1$ .
 - ¿Cuáles son las combinaciones riesgo/rendimiento que obtenemos combinando $A$ y $B$ en un portafolio?
 - Notar que mientras los rendimientos se siguen calculando igual, el riesgo es menor que en el primer caso. Comparar.

### 1.3. Ilustración gráfica con tres activos.

####  Ver en el tablero...

- Supongamos que tenemos tres activos: $A$, $B$ y $C$.
- Supongamos que las correlaciones entre cada par de activos son imperfectas: $-1<\rho_{ij}<+1$.
- Podemos armar portafolios entre cada par de activos (dibujar).
- Luego, podríamos pensar un portafolio con los tres activos, como una combinación de un portafolio de dos activos (por ejemplo, $A$ y $B$) con el activo restante (ejemplo, $C$).
- ¿Qué se obtiene?

**Conclusión.** Combinar más activos abre más posibilidades a la diversificación del riesgo.
___

## 2. Diversificación: riesgo sistemático y riesgo idiosincrático.

En la clase pasada vimos que si tenemos $n$ activos en un portafolio, el riesgo del portafolio (medido con la varianza) se puede calcular a partir de los riesgos individuales y movimientos relativos par a par como:

\begin{align}
\sigma_p^2  &=\sum_{i=1}^{n}\sum_{k=1}^{n}w_iw_k\sigma_{ik}\\
            &=\sum_{i=1}^{n}w_i^2\sigma_{ii}+\sum_{i=1}^{n}\sum_{k=1, k\neq i}^{n}w_iw_k\sigma_{ik}\\
            &=\sum_{i=1}^{n}w_i^2\sigma_{i}^2+\sum_{i=1}^{n}\sum_{k=1, k\neq i}^{n}w_iw_k\sigma_{ik}.
\end{align}

- Los primeros son los términos de varianza individual de cada activo: $n$ términos.
- Los segundos son los términos de covarianza entre pares de activos: $n(n-1)$ términos.
- Cuando $n$ crece, la importancia relativa de los términos de varianza es menor a la de los términos de covarianza.

___
**Ejercicio para ilustrar este punto.** Suponga que se forma un portafolio con las siguientes condiciones:
1. Se combinan $n$ activos en el portafolio.
2. Todos los activos tienen la misma varianza. Esto es, $\sigma_i^2=\sigma^2$ para $i=1,2,\dots,n$.
3. La covarianza entre cada par de activos es la misma. Esto es, $\sigma_{ij}=\lambda$ para $i,j=1,2,\dots,n$, con $i\neq j$.
4. La participación de cada activo en el portafolio es igual. Es decir, $w_i=\frac{1}{n}$ para $i=1,2,\dots,n$.

¿A qué es igual la varianza del portafolio cuando el número de activos es muy grande?

*Pista:* reemplazar en la fórmula de arriba y tomar el límite cuando $n\to\infty$.

*Recomendación:* háganlo que va a venir en el próximo quiz.
___

**Entonces tenemos dos fuentes de riesgo en un portafolio:**
1. Riesgo sistemático:
 - Es un hecho que todos los activos se ven afectados económicamente por factores comunes.
 - Ejemplo: si hay una recesión, la gente se encontraría desempleada, lo que afectaría a la mayoría de los negocios al mismo tiempo.
 - Esto es lo que llamamos **riesgo sistemático**, o **riesgo de mercado**.

2. Riesgo idiosincrático
 - La otra fuente de riesgo en un portafolio es llamada **riesgo idiosincrático**, o **riesgo específico de firma**, o **riesgo único**, o **riesgo no sistemático**.
 - Esta fuente de riesgo afecta a cada activo en particular por razones específicas.
 - Por definición, este tipo de riesgo específico, no está relacionado con otros activos.
 - Entonces cuando combinamos activos en un portafolio, el riesgo idiosincrático tiende a eliminarse.

Sin embargo, hay un límite en *cuanta* diversificación podemos alcanzar. Claro,
- siempre podemos diversificar el riesgo no sistemático con un portafolio con muchos activos y bien diversificado,
- pero no nos podemos deshacer del riesgo sistemático, porque afecta a todo.

*En un portafolio correctamente diversificado, el único riesgo al que estamos sujetos es al riesgo que no podemos diversificar: riesgo sistemático*.

Imagen tomada de: Makiel, R. *A Random Walk Down Wall Street*, 2012.

Entonces, con todo lo anterior, **¿porqué diversificar?**
- para disminuir el riesgo.
- ¿porqué usar casco?, ¿porqué usar cinturón de seguridad cuando voy en el coche?
___

# Anuncios parroquiales

## 1. Cuadrar fecha de examen módulos 1 y 2. Lunes 8 de Marzo.

## 2. [Artículo interesante](https://www.economist.com/buttonwoods-notebook/2012/02/23/globalisation-and-diversification).

Created with Jupyter by Esteban Jiménez Rodríguez.


This notebook covers the basics of operations on Series and DataFrames. Again, a quick check on whether you know the material.

1. Can you use numpy ufuncs (e.g. `np.exp`) on DataFrames?
2. What happens when you add two DataFrames with different indicies?

If you know the answers already, you can skip to our first [application](#Application:-Economic-Timeseries). Maybe download some additional datasets from [FRED](https://research.stlouisfed.org) and play with those; try to figure out how to fix the economy. You've got about 5 minutes.

`DataFrame`s and `Series` support all the usual math operations. Additionally,
numpy [`ufuncs`](http://docs.scipy.org/doc/numpy/reference/ufuncs.html) (e.g. `np.log`) can  be used as expected.

```python
import numpy as np
import pandas as pd
```

# `ufuncs`

```python
np.random.seed(42)
df = pd.DataFrame(np.random.uniform(0, 10, size=(3, 3)))
df
```

      0
      1
      2

      0
      3.745401
      9.507143
      7.319939

      1
      5.986585
      1.560186
      1.559945

      2
      0.580836
      8.661761
      6.011150

```python
df + 1
```

      0
      1
      2

      0
      4.745401
      10.507143
      8.319939

      1
      6.986585
      2.560186
      2.559945

      2
      1.580836
      9.661761
      7.011150

```python
df ** 2
```

      0
      1
      2

      0
      14.028030
      90.385769
      53.581513

      1
      35.839198
      2.434182
      2.433429

      2
      0.337371
      75.026112
      36.133926

```python
np.log(df)
```

      0
      1
      2

      0
      1.320529
      2.252043
      1.990602

      1
      1.789521
      0.444805
      0.444651

      2
      -0.543287
      2.158918
      1.793616

# Alignment

But we don't just have NumPy arrays. We have `DataFrames` and `Series`, which have *labels*.
Pandas automatically aligns by label when doing operations between `DataFrames` and `Series`.

```python
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [2, 4, 6], 'B': [1, 4, 9]})

df1
```

      A
      B

      0
      1
      4

      1
      2
      5

      2
      3
      6

```python
df2
```

      A
      B

      0
      2
      1

      1
      4
      4

      2
      6
      9

```python
df1 + df2
```

      A
      B

      0
      3
      5

      1
      6
      9

      2
      9
      15

```python
# Note the index order
df2 = pd.DataFrame({'A': [6, 2, 4], 'B': [9, 1, 4]}, index=[2, 0, 1])
df2
```

      A
      B

      2
      6
      9

      0
      2
      1

      1
      4
      4

```python
df1 + df2
```

      A
      B

      0
      3
      5

      1
      6
      9

      2
      9
      15

```python
# Different index entirely
df3 = pd.DataFrame(np.random.randn(5, 3), columns=['A', 'B', 'C'])
df3
```

      A
      B
      C

      0
      -0.580878
      -0.525170
      -0.571380

      1
      -0.924083
      -2.612549
      0.950370

      2
      0.816445
      -1.523876
      -0.428046

      3
      -0.742407
      -0.703344
      -2.139621

      4
      -0.629475
      0.597720
      2.559488

```python
df1 + df3
```

      A
      B
      C

      0
      0.419122
      3.474830
      NaN

      1
      1.075917
      2.387451
      NaN

      2
      3.816445
      4.476124
      NaN

      3
      NaN
      NaN
      NaN

      4
      NaN
      NaN
      NaN

# Recap

- `ufuncs` work just fine
- broadcasting works fine
- Operations **align by label** and then are performed

# Application: Economic Timeseries

```python
from itertools import chain

import pandas as pd

import seaborn as sns
import statsmodels.api as sm
# from pandas_datareader import data
# from pandas.io import data
import matplotlib.pyplot as plt
from toolz import partitionby

%matplotlib inline
pd.options.display.max_rows = 10
sns.set_style('ticks')
```

Pandas recently split off some web-based data reading functionality into its own package `pandas-datareader`. Earlier, I used it to grab data from [FRED](http://research.stlouisfed.org).

```python
gdp = data.DataReader("GDP", data_source='fred', start='1929', end='2014').squeeze()
cpi = data.DataReader("CPIAUCSL", data_source='fred', start='1947-01', end='2015-05').squeeze()
rec = data.DataReader('USREC', data_source='fred', start='1854-12-01', end='2014-08-01').squeeze()
gdp.to_csv('data/gdp.csv', header=True)
cpi.to_csv('data/cpi.csv', header=True)
rec.to_csv('data/rec.csv', header=True)
```

```python
!head data/gdp.csv
```

    DATE,GDP
    1947-01-01,243.1
    1947-04-01,246.3
    1947-07-01,250.1
    1947-10-01,260.3
    1948-01-01,266.2
    1948-04-01,272.9
    1948-07-01,279.5
    1948-10-01,280.7
    1949-01-01,275.4

```python
pd.read_csv?
```

```python
gdp = pd.read_csv('data/gdp.csv', index_col='DATE', parse_dates=['DATE']).squeeze()
cpi = pd.read_csv('data/cpi.csv', index_col='DATE', parse_dates=['DATE']).squeeze()
rec = pd.read_csv('data/rec.csv', index_col='DATE', parse_dates=['DATE']).squeeze()
```

```python
gdp.head(n=5)
```

    DATE
    1947-01-01    243.1
    1947-04-01    246.3
    1947-07-01    250.1
    1947-10-01    260.3
    1948-01-01    266.2
    Name: GDP, dtype: float64

```python
cpi.head()
```

    DATE
    1947-01-01    21.48
    1947-02-01    21.62
    1947-03-01    22.00
    1947-04-01    22.00
    1947-05-01    21.95
    Name: CPIAUCSL, dtype: float64

```python
rec.head()
```

    DATE
    1854-12-01    1
    1855-01-01    0
    1855-02-01    0
    1855-03-01    0
    1855-04-01    0
    Name: USREC, dtype: int64

# Table Summarization

Pandas has a few methods for summarizing the contents of a DataFrame or Series

```python
gdp.describe()
```

    count      269.000000
    mean      5074.190335
    std       5158.153013
    min        243.100000
    25%        654.800000
    50%       2860.000000
    75%       8551.900000
    max      17078.300000
    Name: GDP, dtype: float64

`describe` actually combines and few different *aggregation* methods that summarise a DataFrame or Series. These include `mean`, `max`, `std`, `quantile`...

```python
print('mean:    ', gdp.mean())
print('std:     ', gdp.std())
print('quantile:', gdp.quantile(.66))
print('max:     ', gdp.max())
```

    mean:     5074.1903345724895
    std:      5158.153012839137
    quantile: 6132.956
    max:      17078.3

It can also be useful to get the argmax, or the index label where the maximum occurs.

```python
gdp.idxmax()
```

    Timestamp('2013-10-01 00:00:00')

# Plotting

DataFrames and Series can make decent looking plots with a few lines of code.

I wanted to incude bars for recession indicators.
The only pandas-relavent bit is `Series.iteritems`, which you *almost* never want to use since it's relatively slow.

```python
next(rec.iteritems())
```

    (Timestamp('1854-12-01 00:00:00'), 1)

```python
groups = partitionby(lambda x: x[1] == 1, rec.iteritems())
recessions = filter(lambda x: x[0][1] == 1, groups)
spans = [(months[0][0], months[-1][0]) for months in recessions]
```

```python
def add_rec_bars(ax=None):
    ax = ax or plt.gca()
    for span in spans:
        ax.fill_between(span, *ax.get_ylim(), color='k', alpha=.25)
    return ax

plt.rcParams['figure.figsize'] = (12, 6)
```

```python
ax = gdp.plot(title='GDP', linewidth=3)
add_rec_bars(ax)
sns.despine()
```

```python
ax = cpi.plot(title='CPI', linewidth=3)
add_rec_bars(ax)
sns.despine()
```

Let's put some of those operations to use.

# Execrise: Convert CPI to be base 2009

CPI is the Consumer Price Index. The Index part just means that it doesn't really have any units,
just some (arbitrary) time span that is set to 100, and every other observation is relative to that time.
The CPI we have is based on 1982-1984.

**Convert the CPI base-2009 by dividing the entire Series `cpi` by the average CPI in 2009.**

Try breaking the problem into pieces:
1. Select just the rows from 2009 (`.loc`)
  + Timeseries (dates in the index) have speical rules for slicing. Pass in a string with the subset you want
    + `.loc['2010-01-01']` select all observations from that day
    + `.loc['2010-01']` selects all observations from that month
2. calculate the average on those rows
3. divide `cpi` by that number (assign that to `cpi09`)

```python
# Your code goes here
# Assign the result to cpi09
```

```python
%load -r 1:5 solutions_operations.py
```

    DATE
    1947-01-01    0.100110
    1947-02-01    0.100762
    1947-03-01    0.102533
    1947-04-01    0.102533
    1947-05-01    0.102300
                    ...
    2015-01-01    1.093736
    2015-02-01    1.096108
    2015-03-01    1.098690
    2015-04-01    1.099818
    2015-05-01    1.104707
    Name: CPIAUCSL, dtype: float64

```python
ax = cpi09.plot(title='CPI-2009')
add_rec_bars(ax=ax)
sns.despine();
```

# Real GDP: Alignment

Let's say we want real GDP (adjusted for inflation).

\begin{equation}
    rGDP_t = \frac{GDP_t}{CPI_t}
\end{equation}

Problem: our CPI is monthly but GDP is quarterly. Also the two Series have different start and end points. If you didn't have automatic label alignment, you'd have to jump through hoops to select the correct subset of each series.

```python
# this is boolean indexing, we'll see more later
gdp / cpi09[(cpi.index.month % 3 == 1) & (cpi09.index.year <= 2014)]
```

... but that's unneccesary. The operations will automatically align for you.

```python
rgdp = (gdp / cpi09)
rgdp
```

    DATE
    1947-01-01    2428.336614
    1947-02-01            NaN
    1947-03-01            NaN
    1947-04-01    2402.148973
    1947-05-01            NaN
                     ...
    2015-01-01            NaN
    2015-02-01            NaN
    2015-03-01            NaN
    2015-04-01            NaN
    2015-05-01            NaN
    dtype: float64

The `NaN`s are missing value indicators. `NaN`s can crop up for many reasons, but in this case it's because the labels didn't overlap perfectly.

Many pandas methods, e.g. aggregations like `sum` or `mean`, will ignore missing values.
Pandas provides methods for detetcing missing data `.isnull`, filling missing data `.fillna`,
or you can dropping it, which is what I'll do here.

```python
rgdp.dropna()
```

    DATE
    1947-01-01     2428.336614
    1947-04-01     2402.148973
    1947-07-01     2413.973150
    1947-10-01     2437.851712
    1948-01-01     2412.040298
                      ...
    2013-01-01    15298.871240
    2013-04-01    15391.990004
    2013-07-01    15538.670381
    2013-10-01    15666.322137
    2014-01-01    15553.401461
    dtype: float64

Pandas methods are non-mutating by default. This means that even though I called `.dropna()` above, `rgdp` still has missing values.

```python
rgdp
```

    DATE
    1947-01-01    2428.336614
    1947-02-01            NaN
    1947-03-01            NaN
    1947-04-01    2402.148973
    1947-05-01            NaN
                     ...
    2015-01-01            NaN
    2015-02-01            NaN
    2015-03-01            NaN
    2015-04-01            NaN
    2015-05-01            NaN
    dtype: float64

To capture the change, assign a name to the result. In this case, I just the same name `rgdp`.

```python
rgdp = rgdp.dropna()
rgdp.head()
```

    DATE
    1947-01-01    2428.336614
    1947-04-01    2402.148973
    1947-07-01    2413.973150
    1947-10-01    2437.851712
    1948-01-01    2412.040298
    dtype: float64

```python
ax = gdp.pct_change().plot(figsize=(15, 5), label='GDP', legend=True, linewidth=2)
rgdp.pct_change().plot(ax=ax, label='real GDP', legend=True, linewidth=2)
add_rec_bars(ax)
sns.despine()
```

# Recap

DataFrames allow you to rapidly explore data, iterating on ideas.

- `ufunc`s work as expected
- operations between pandas objects automatically align on labels

```python

```


# Introduction

[Yamaguchi et al 2017](http://adsabs.harvard.edu/cgi-bin/bib_query?arXiv:1710.09839) estimated the number of binaries with a black hole and a stellar companion which will be detected by the [Gaia mission](http://sci.esa.int/gaia/). In this notebook we want to check whether their model reproduces the non detection of such binaries by the [hipparcos mission](http://sci.esa.int/hipparcos/).

Loading libraries and setting up environment

```python
import sympy
import numpy
sympy.init_printing()
```

# Reproducing Paper Results

As a first step, we try to reproduce the results in the paper, specifically, table 2. We start with the integrand of the huge integral in equation 15.

```python
f_bin = sympy.Symbol('f_bin') # Binary fraction
Gamma_0 = sympy.Symbol('Gamma_0') # Slope of binary separation pdf
Phi = sympy.Symbol('Phi') # Mass ratio distribution
t_L1 = sympy.Symbol(r't_{L,1}') # Lifetime of the star that collapsed to a black hole
t_L2 = sympy.Symbol(r't_{L,2}') # Lifetime of the companion
A_bar = sympy.Symbol('\overline{A}') # Separation between stars
b = sympy.Symbol('b') # Viewing angle relative to galactic plane
D = sympy.Symbol('D') # Distance from Earth
rho_d = sympy.Symbol('rho_d') # Star formation number density
eqn_15_integrand_raw = (2*f_bin/(1-f_bin))*Gamma_0*Phi*(t_L2-t_L1)*(1/A_bar)*sympy.cos(b)*D**2*rho_d
eqn_15_integrand_raw
```

## Spatial distribution of stars

Bahcall Soniera star formation number density (equation 11)

```python
Psi = sympy.Symbol('Psi')
rho_d0 = sympy.Symbol(r'\rho_{d,0}') # Normalisation factor
z = sympy.Symbol('z') # Distance from the galactic plane
h_z = sympy.Symbol('h_z', positive=True) # Scale height of the galactic disc
x = sympy.Symbol('x') # Distance from the rotational axis of the galaxy
r_0 = sympy.Symbol('r_0') # Distance between the sun and the galactic centre
h_r = sympy.Symbol('h_r', positive=True) # Scale radius of the galaxy
eqn_11 = sympy.Eq(rho_d,Psi*rho_d0*sympy.exp(-z/h_z-(x-r_0)/h_r))
eqn_11
```

Definition of the normalisation (equation 12)

```python
x_max = sympy.Symbol(r'x_{\max}')
z_max = sympy.Symbol(r'z_{\max}')
temp = eqn_11.rhs/ Psi
temp = sympy.integrate(4*sympy.pi*x*temp,(z,0,z_max),(x,0,x_max)).simplify()
eqn_12 = sympy.Eq(rho_d0,sympy.solve(temp-1,rho_d0)[0])
eqn_12
```

Actually, the bounds $x_{\max}$ and $z_{\max}$ are not defined in the paper, so I assume $x_{\max}, z_{\max} \rightarrow \infty$

```python
temp = eqn_11.rhs/Psi*4*sympy.pi*x
temp = sympy.integrate(temp, (z, 0, sympy.oo))
temp = sympy.integrate(temp, (x, 0, sympy.oo))
temp = sympy.solve(temp-1,rho_d0)[0]
eqn_12_var = sympy.Eq(rho_d0, temp)
eqn_12_var
```

Geocentric coordinates

```python
l = sympy.Symbol('l') # Viewing angle relative to the rotation axis of the galaxy
eqn_13 = sympy.Eq(x,sympy.sqrt(r_0**2+D**2*sympy.cos(b)**2-2*D*r_0*sympy.cos(b)*sympy.cos(l)))
eqn_13
```

```python
eqn_14 = sympy.Eq(z, D*sympy.sin(b))
eqn_14
```

## Initial mass function

```python
Psi_1a = sympy.Symbol(r'\Psi_{1,a}') # Coefficient
Psi_1b = sympy.Symbol(r'\Psi_{1,b}') # Coefficient
M_sol = sympy.Symbol('M_{\odot}')
M_1_bar = sympy.Symbol(r'\overline{M}_1')
m_1_bar = sympy.Symbol(r'\overline{m}_1') # M_1_bar divided by a solar mass
temp = sympy.Piecewise((Psi_1a*m_1_bar**(-1.3), sympy.And(m_1_bar < 0.5, m_1_bar>0.08)),
                       (Psi_1b*m_1_bar**(-2.3), sympy.And(m_1_bar>0.5, m_1_bar<100)),
                      (0,True))
eqn_1 = sympy.Eq(Psi, temp)
eqn_1
```

```python
Psi_2a = sympy.Symbol(r'\Psi_{2,a}') # Coefficient
Psi_2b = sympy.Symbol(r'\Psi_{2,b}') # Coefficient
Psi_2c = sympy.Symbol(r'\Psi_{2,c}') # Coefficient
temp = sympy.Piecewise((Psi_2a*m_1_bar**(-1.3), sympy.And(m_1_bar < 0.5, m_1_bar>0.08)),
                       (Psi_2b*m_1_bar**(-2.2), sympy.And(m_1_bar>0.5, m_1_bar<1)),
                      (Psi_2c*m_1_bar**(-2.7), sympy.And(m_1_bar>1.0, m_1_bar<100)),
                      (0,True))
eqn_2 = sympy.Eq(Psi,temp)
eqn_2
```

The coefficients can be determined by continuity and normalisation condition

```python
cond_1 = sympy.Eq(eqn_1.rhs.subs(m_1_bar,0.5-1e-6),eqn_1.rhs.subs(m_1_bar,0.5+1e-6))
cond_1
```

```python
Upsilon = sympy.Symbol('Upsilon') # Star formation rate in the entire galaxy, roughly 3.5 M_sol/year
temp = sympy.integrate(eqn_1.rhs*m_1_bar,m_1_bar)
temp = ((temp.subs(m_1_bar, 0.5-1e-6) - temp.subs(m_1_bar, 0.08+1e-6))+
        (temp.subs(m_1_bar, 100-1e-6) - temp.subs(m_1_bar, 0.5+1e-6)))
cond_2 = sympy.Eq(temp, Upsilon)
cond_2
```

```python
eqn_1_coefficients = sympy.solve([cond_1, cond_2], [Psi_1a, Psi_1b])
eqn_1_coefficients
```

```python
cond_3 = sympy.Eq(eqn_2.rhs.subs(m_1_bar,0.5-1e-6), eqn_2.rhs.subs(m_1_bar, 0.5+1e-6))
cond_3
```

```python
cond_4 = sympy.Eq(eqn_2.rhs.subs(m_1_bar,1-1e-6), eqn_2.rhs.subs(m_1_bar, 1+1e-6))
cond_4
```

```python
temp = sympy.integrate(eqn_2.rhs*m_1_bar, m_1_bar)
temp = ((temp.subs(m_1_bar,0.5-1e-6) - temp.subs(m_1_bar,0.08+1e-6))+
       (temp.subs(m_1_bar,1.0-1e-6) - temp.subs(m_1_bar,0.5+1e-6))+
       (temp.subs(m_1_bar,100-1e-6) - temp.subs(m_1_bar,1+1e-6)))
cond_5 = sympy.Eq(temp, Upsilon)
cond_5
```

```python
eqn_2_coefficients = sympy.solve([cond_3, cond_4, cond_5], [Psi_2a, Psi_2b, Psi_2c])
eqn_2_coefficients
```

## Distribution of mass ratios

```python
M_min_bar = sympy.Symbol(r'\overline{M}_{\min}')
eqn_3 = sympy.Eq(Phi, 1/(1-M_min_bar/M_1_bar))
eqn_3
```

## Distribution of semi major axes

Minimum semimajor axis

```python
A_bar_min = sympy.Symbol(r'\overline{A}_{\min}')
q = sympy.Symbol('q', positive=True) # Mass ratio
R_1 = sympy.Symbol('R_1') # Radios of primary
eqn_7 = sympy.Eq(A_bar_min,
                 (0.6*q**sympy.Rational(-2,3)+sympy.log(1+q**sympy.Rational(-1,3)))*R_1/(0.49*q**sympy.Rational(-2,3)))
eqn_7
```

Mass raius relation, from [Demircan and Kahraman 1991](http://adsabs.harvard.edu/abs/1991Ap%26SS.181..313D)

```python
R_sol = sympy.Symbol('R_{\odot}') # Solar radius
mass_radius_relation = 1.61*m_1_bar**0.83*R_sol
```

Determination of semimajor axes distribution

```python
A_bar_max = sympy.Symbol(r'\overline{A}_{\max}')
temp = Gamma_0/A_bar
temp = sympy.integrate(temp, (A_bar, A_bar_min, A_bar_max))
temp = temp.subs(A_bar_max, 1e5*R_sol).subs(eqn_7.lhs, eqn_7.rhs).subs(R_1, mass_radius_relation)
Gamma_0_expr = sympy.solve(temp-1,Gamma_0)[0]
Gamma_0_expr
```

## Lifetimes

```python
tau_r = sympy.Symbol('tau_r') # Reference lifetime (10 Gy)
lifetime_mass_relation = tau_r*m_1_bar**(-2.5)
lifetime_mass_relation
```

## Putting it all together

Carrying out the integral in equation 15

```python
solar_radius_in_pc = 2.25e-8
D_max = sympy.Symbol(r'D_{\max}')
temp = eqn_15_integrand_raw.subs(eqn_11.lhs, eqn_11.rhs).subs(eqn_12_var.lhs, eqn_12_var.rhs)
temp = temp.subs(eqn_13.lhs, eqn_13.rhs)
temp = temp.subs(eqn_14.lhs, eqn_14.rhs)
temp = temp.subs(eqn_3.lhs, eqn_3.rhs)
temp = temp.subs(Gamma_0, Gamma_0_expr)
temp = temp.subs(Psi, eqn_1.rhs.args[1][0])
temp = temp.subs(eqn_1_coefficients)
temp = temp.subs(t_L1,lifetime_mass_relation)
temp = temp.subs(t_L2,lifetime_mass_relation.subs(m_1_bar,m_1_bar*q))
temp = temp.subs(M_min_bar, 0.08*M_sol)
temp = temp.subs(M_1_bar, m_1_bar*M_sol)
temp = temp.subs(h_z, 250)
temp = temp.subs(h_r, 3500)
temp = temp.subs(r_0, 8000)
temp = temp.subs(b, 1)
temp = temp.subs(l, sympy.Rational(1,2))
temp = temp.subs(D, 100)
temp = temp.subs(f_bin, 0.5)
temp = temp.subs(q, 0.1)
temp = temp.subs(R_sol, solar_radius_in_pc)
temp = temp.subs(m_1_bar, 30)
temp.n()
```

# Step by Step Integration

In this section we break up the integral in equation 15 into different categories, and consider each category separately to obtain insight to the problem. We begin by eliminating the units. From the star formation rate $3.5 \, M_{\odot} / \rm year$, a typical timescale $\tau_r = 10 \, \rm Gyr$ and a typical mass $M_{\odot}$ we obtain the total number of stars $3.5 \cdot 10^{10}$. From this point, each integral filters out the irrelevant stars and reduces this number,

## Primary mass
In this section we filter out all primaries below the necessary mass needed to collapse to a black hole

```python
def integrand_func_mk_1(m1):

    if m1<20:
        return 0
    return 3.5e10*0.22*m1**(-2.3)

m_range = numpy.linspace(20,100) # Primary mass range
dm = m_range[1] - m_range[0]
numpy.sum([integrand_func_mk_1(m1) for m1 in m_range])*dm
```

## Mass ratio

We filter out large companion masses because their lifetime is too short, and low masses because they are too dim

```python
def integrand_func_mk_2(m1, q):

    m2min = 0.4
    if m2min/m1>q:
        return 0
    return integrand_func_mk_1(m1)/(1-m2min/m1)*m1**-2.5*(q**-2.5-1)

m_range = numpy.linspace(20,100) # Range of primary masses
q_range = numpy.linspace(0, 1) # Range of mass ratios
dm = m_range[1] - m_range[0] # Mass interval
dq = q_range[1] - q_range[0] # Mass ratio interval
numpy.sum([[integrand_func_mk_2(m1, q) for m1 in m_range] for q in q_range])*dm*dq
```

## Semi major axis

```python
def integrand_func_mk_3(m1,q,lnA):

    A_max_val = 1e5
    D_ref = 4e10 # Reference distance, 1 kpc in solar radii
    uas = 5e-12 # micro arcsecond in radius
    gaia_angular_res = 300*uas # Gaia angular sensitivity
    A_min_val = 0.66*m1**0.83*(3.0+5*q**0.67*numpy.log(1+q**0.33))
    A_period = 2000 # http://www.wolframalpha.com/input/?i=((solar+mass)*(gravitation+constant)*(5+year)%5E2)%5E(1%2F3)%2F(solar+radius)
    if lnA>numpy.log10(max(A_period, A_max_val)) or lnA<numpy.log10(A_min_val) or lnA<numpy.log10(gaia_angular_res)+numpy.log10(D_ref):
        return 0
    G0_val = 1.0/numpy.log10(A_max_val/A_min_val)

    return integrand_func_mk_2(m1,q)*G0_val

m_range = numpy.linspace(20,100,10) # Range of primary masses
q_range = numpy.linspace(0, 1, 10) # Range of mass ratios
lnA_range = numpy.linspace(-1, 5, 10) # Rand of semi - major axis
dm = m_range[1] - m_range[0] # Mass interval
dq = q_range[1] - q_range[0] # Mass ratio interval
dlnA = lnA_range[1] - lnA_range[0] # semi major axis interval
numpy.sum([[[integrand_func_mk_3(m1, q, lnA) for m1 in m_range]
            for q in q_range]
           for lnA in lnA_range])*dm*dq*dlnA
```


Regresión Logística

```python
import requests
from io import StringIO
import pandas as pd
import numpy as np
from numpy.random import randn, seed, shuffle
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
```

```python
%config InlineBackend.figure_format = "retina"
plt.rcParams["font.size"] = 14
```

Supongamos queremos predecir la probabilidad de un evento con dos posibles resultados: positivo ($1$) o negativo ($0$).

En este caso, la variable objetivo a prededecir es `y=1` o `y=0`

¿Qué ejemplo de problemas tendrían este planteamiento?

* La probabilidad de que una acción sube o baje de precio al día siguiente
* Si, dada una foto, hay una persona dentro de ella o no
* La probabilidad de que a un usuario le guste un producto
* Saber si al día de mañana llueve o no

### Un Primer Ejemplo: Tumores beningnos

¿Cuál es la probabilidad de un tumor dada una edad?

```python
import pandas as pd
from io import StringIO
import requests
from sklearn.model_selection import train_test_split
columns=["bi_rads", "age", "shape", "margin", "density", "severity"]
datalink = "http://archive.ics.uci.edu/ml/machine-learning-databases/mammographic-masses/mammographic_masses.data"
df = pd.read_csv(StringIO(requests.get(datalink).text),
                 header=None, na_values="?")
df.columns = columns

train, test = train_test_split(df, test_size=0.2, random_state=314)
train.head()
```

      bi_rads
      age
      shape
      margin
      density
      severity

      210
      4.0
      46.0
      1.0
      1.0
      1.0
      0

      946
      4.0
      57.0
      2.0
      4.0
      3.0
      0

      638
      4.0
      67.0
      2.0
      1.0
      3.0
      0

      473
      4.0
      61.0
      1.0
      1.0
      3.0
      0

      334
      4.0
      64.0
      4.0
      4.0
      3.0
      0

```python
train.drop(["age", "severity"], axis=1).apply(lambda x: x.value_counts(), axis=0)
```

      bi_rads
      shape
      margin
      density

      0.0
      5.0
      NaN
      NaN
      NaN

      1.0
      NaN
      180.0
      286.0
      16.0

      2.0
      13.0
      172.0
      18.0
      49.0

      3.0
      25.0
      69.0
      90.0
      636.0

      4.0
      438.0
      319.0
      225.0
      9.0

      5.0
      275.0
      NaN
      109.0
      NaN

      6.0
      9.0
      NaN
      NaN
      NaN

      55.0
      1.0
      NaN
      NaN
      NaN

```python
# Filtramos información irrelevante
# bi_rads: Breast Imaging, Reporting & Data System
train = train.query("bi_rads != 55")
```

Información de los [datos](http://archive.ics.uci.edu/ml/datasets/mammographic+mass)

```
Attribute Information:
   1. BI-RADS assessment: 1 to 5 (ordinal)
   2. Age: patient's age in years (integer)
   3. Shape: mass shape: round=1 oval=2 lobular=3 irregular=4 (nominal)
   4. Margin: mass margin: circumscribed=1 microlobulated=2 obscured=3 ill-defined=4 spiculated=5 (nominal)
   5. Density: mass density high=1 iso=2 low=3 fat-containing=4 (ordinal)
   6. Severity: benign=0 or malignant=1 (binominal)
```

```python
train.plot(x="age", y="severity", kind="scatter", alpha=0.4)
plt.show()
```

¿Podríamos usar una regresión lineal para predecir los datos?

```python
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline

linreg = Pipeline([
    ("mean_imp", SimpleImputer()),
    ("linreg", LinearRegression())
])

xtrain, ytrain = train.age.values.reshape(-1,1), train.severity.values
linreg.fit(xtrain, ytrain)
train.plot(x="age", y="severity", kind="scatter", alpha=0.4)
plt.plot(xtrain, linreg.predict(xtrain), color="tab:orange");
```

```python
print(f"La probabilidad de un tumor a edad 30 es: {linreg.predict([[30]])[0]:.2%}")
print(f"La probabilidad de un tumor a edad 50 es: {linreg.predict([[55]])[0]:.2%}")
```

    La probabilidad de un tumor a edad 30 es: 8.57%
    La probabilidad de un tumor a edad 50 es: 45.29%

```python
## --No existen probabilidades negativas o mayores a 1-- ##
print(f"La probabilidad de un tumor a edad 1 es: {linreg.predict([[1]])[0]:.2%}")
print(f"La probabilidad de un tumor 100 edad 120 es: {linreg.predict([[100]])[0]:.2%}")
```

    La probabilidad de un tumor a edad 1 es: -34.02%
    La probabilidad de un tumor 100 edad 120 es: 111.37%

La regresión lineal no es un buen modelo probabilístico. Nos gustaría encontrar una función que tome valores entre $0$ (nada probable) y $1$ (muy probable).

**La función sigmoidal:**

$$
    \sigma(x) = \frac{1}{1 + e^{-x}}
$$

```python
def sigmoid(x): return 1 / (1 + np.exp(-x))

# Si x es muy grande, sigma(x) se aproxima a 1
x = 100
print(f"sigmoid({x}) = {sigmoid(x):0.4f}")

# Si x es negativa, sigma(x) se aproxima a 0
x = -100
print(f"sigmoid({x}) = {sigmoid(x):0.4f}")

# Si x es 0,
x = 0
print(f"sigmoid({x}) = {sigmoid(x):0.4f}")

```

    sigmoid(100) = 1.0000
    sigmoid(-100) = 0.0000
    sigmoid(0) = 0.5000

```python
xrange = np.linspace(-4, 4, 300)
plt.plot(xrange, sigmoid(xrange))
plt.title(r"$\sigma(x)$")
plt.yticks(np.arange(0, 1.1, 0.25))
plt.grid()
```

Para entrenar un modelo de machine learning usando $\sigma(x)$, consideramos el input $x$ como una combinación lineal entre parámetros y observaciones, es decir, queremos encontrar un vector ${\bf w}$ para un vector de observaciones ${\bf x}$ tal que

$$
\hat y_i = \sigma({\bf w}^T{\bf x})
=\frac{1}{1 + e^{-{\bf w}^T{\bf x}}}
$$

Necesitamos una medida de desempeño $P$

### Cross-Entropy Cost Function:
**Negativo de la log-verosimilitud para una variable aleatoria bernoulli parámetro $\sigma({\bf w}^T{\bf x})$**

$$
\begin{align}
    \mathcal{L}(y, \hat y) &= -\frac{1}{n}\sum_{i=1}^n y_i \log{\hat y_i} + (1 - y_i)\log{\left(1 - \hat y_i\right)}\\
                           &= -\frac{1}{n}\sum_{i=1}^n y_i \log{\sigma({\bf w}^Tx^{(i)})} + (1 - y_i)\log{\left(1 - \sigma({\bf w}^Tx^{(i)})\right)}
\end{align}
$$

#### ¿Por qué tiene sentido?
supngamos que tenemos una sola observación $(x, y)$ y $\theta = [\theta_0, \theta_1]$ tal que $\hat y = \sigma(\theta_0 + \theta_1 x) = 1$, pero $y=0$

$$
\begin{align}
    \mathcal{L}(y, \hat y) &=-( y \log \hat y + (1 - y) \log (1 - \hat y))\\
    &= -\log(1 - \hat y)
\end{align}
$$

### Gradient Descent

No existe fóruma cerrada para encontrar los parámetros óptimos de una regresión logística, por lo que recurrimos a gradient descent para minimizar $\mathcal{L}(y, \hat y)$.

Se puede demostrar que

$$
    \frac{\partial}{\partial{\bf w}} = \frac{1}{N}\sum_{n=1}^N\left(\hat y^{(n)} - y^{(n)}\right){\bf x}^{(n)}
$$

Dónde $x_0^{(n)} = 1$ para $n = 1, \ldots, N$

Ejercicio

Por medio de gradient descent, encuentra el vector ${\bf w} \in \mathbb{R}^3$ de una regresión logística para los siguientes datos:

```python
# Set seed to reproduce results
seed(1643)
nexamples = 300

# Agrupa filas: un termino de corrección (bias) y una matriz de
# 2 X nexamples de ruido gaussiano con media != 0
c1 = np.r_[np.ones((1, nexamples)), randn(2, nexamples) + 1]
c2 = np.r_[np.ones((1, nexamples)), randn(2, nexamples) - 1]

# Clasificamos nexamples del primer grupo (media = 1) y
# nexamples del segundo grupo (media - 1)
y = np.r_[np.ones(nexamples), np.zeros(nexamples)].reshape(-1, 1)
X = np.c_[c1, c2]

# Agrupamos las filas y columnas para permutar
# su orden
data = np.r_[X, y.T].T
shuffle(data)
X = data[:, :3]
y = data.T[-1, :]

plt.scatter(*c1[1:,:], color="tab:red", alpha=0.8)
plt.scatter(*c2[1:,:], color="tab:blue", alpha=0.8);
```

Ejercicio

Considerando el siguiente training dataset, implementa una regresión logística usando `scikit-learn`.

```python
from sklearn.linear_model import LogisticRegression
xtrain = train.age.values.reshape(-1,1)
ytrain = train.severity.values
```

Observa la exacitud del modelo usando
```python
logreg.score(xtrain, ytrain)
```

¿Qué te dice el resultado?

```python
from sklearn.linear_model import LogisticRegression

logreg = Pipeline([
    ("mean_imp", SimpleImputer()),
    ("log_reg", LogisticRegression(solver="lbfgs"))
])

xtrain, ytrain = train.age.values.reshape(-1,1), train.severity.values
logreg.fit(xtrain, ytrain)
train.plot(x="age", y="severity", kind="scatter", alpha=0.4)
xrange = np.arange(15, 95, 0.1)
plt.plot(xrange, logreg.predict_proba(xrange.reshape(-1,1))[:,1],
        color="tab:orange");
```

```python
from sklearn.model_selection import cross_validate
cvscore = cross_validate(logreg, xtrain, ytrain, cv=8, scoring="precision")

# Estimación: intervalo de medida *accuracy* final
cvscore["test_score"].mean() + np.array([-1, 1]) * cvscore["test_score"].std()
```

    array([0.60483319, 0.68890382])

## Error Analysis

### Confusion Matrix

| Ref            | Actual    | Predicción |
|----------------|-----------|------------|
| **True Positive** (TP) | Verdadero | Verdadero  |
| **True Negative** (TN) | Falso     | Falso      |
| **False Positive** (FP)| Falso     | Verdadero  |
| **False Negative** (FN)| Verdadero | Falso      |

### Metricas para un clasificador binario

$$
    \mbox{Precision} = \frac{TP}{TP + FP}
$$

$$
    \mbox{Recall} = \frac{TP}{TP + FN}
$$

* *Precision* considera todos los valores sobre los cuales nuestro modelo clasificó como positivos ($TP + FP$)
* *Recall* considera todos los valores que son realmente positivos ($TP + FN$)

### Precision - Recall Tradeoff
**¿Qué metrica usar?**
* Usamos *precision* si no interesa maximizar la *pureza* de nuestras predicciones a costa de sacrificar el número de predicciones echas como verdaderas, i.e., menos $FP$ a cambio de más $FN$.
* Usamos *recall* si nos intersa maximizar el número de observaciones positivas clasificadas como positivas a costa de clasificar incorrectamente observaciones negativas como positivas, i.e., más $FP$ a cambio de menos $FN$

----

$$
    \max \text{precision} \implies \downarrow FP \ \wedge \uparrow FN
$$

$$
    \max \text{recall} \implies \uparrow FP \ \wedge \downarrow FN
$$

### Variando el *threshold*

```python
from sklearn.metrics import precision_recall_curve

logreg = Pipeline([
    ("mean_imp", SimpleImputer()),
    ("log_reg", LogisticRegression(solver="lbfgs"))
])

xtrain, ytrain = train.age.values.reshape(-1,1), train.severity.values
logreg.fit(xtrain, ytrain)

precision, recall, threshold = precision_recall_curve(ytrain, logreg.predict_proba(xtrain)[:, 1])
plt.plot(precision[:-1], threshold, label="Precision")
plt.plot(recall[:-1], threshold, label="Recall")
plt.xlabel("Threshold")
plt.yticks([0, 0.2, 0.4, 0.6, 0.8, 1])
plt.legend()
plt.grid(alpha=0.5)
```

Ejercicio

1. Supón queremos entrenar una regresión logística para detectar transacciones fradulentas con tarjetas de crédito, ¿nos interesaría obtener un *recall* o *precision* más alto? ¿por qué?
2. Supón queremos entrenar una regresión logística para detectar películas aptas para niños, ¿nos interesaría obtener el *recall* o *precision*? ¿por qué?
3. Considerando nuestro problema inicial, detectar tumores benignos, ¿nos interesaría más enforcarnos en *precision* o *recall*? ¿por qué?

### Confusion Matrix en `scikit-learn`

```python
from sklearn.metrics import confusion_matrix
import seaborn as sns

cmat = confusion_matrix(ytrain, logreg.predict(xtrain))
sns.heatmap(cmat, annot=True, fmt=",")
```

Ejercicio

Considerando el *confusion matrix* `cmat`, cálcula las metricas: *precision* y *recall*
```python
>>> cmat
array([[296, 119],
       [137, 216]])
```

#### Métricas en `scikit-learn`

```python
from sklearn.metrics import precision_score, recall_score, accuracy_score
ypreds = logreg.predict(xtrain)
print(f"Precision: {precision_score(ytrain, ypreds):0.3%}")
print(f"Recall: {recall_score(ytrain, ypreds):>10.3%}")
print(f"Accuracy: {accuracy_score(ytrain, ypreds):>8.3%}")
```

    Precision: 64.478%
    Recall:    61.364%
    Accuracy:  66.754%

### One-Hot Encoders
**¿Podemos mejorar nuestras métricas? Considerando una segunda variable**

Podríamos incluír en nuestra hipótesis que el *margen* de un tumor es importante para clasificar si es benigno o no.

```python
mass_cat = range(5)
mass_lab = ['circumscribed', 'microlobulated', 'obscured', 'ill-defined', 'spiculated']
train["margin"].value_counts(sort=False).plot(kind="bar", color="tab:blue")
plt.xticks(mass_cat, mass_lab);
```

```python
from sklearn.tree import DecisionTreeClassifier
```

```python
xtrain, ytrain = train[["age", "margin"]].values, train.severity
logreg = Pipeline([
    # Valores con NaN los remplazamos con la media
    ("mean_imp", SimpleImputer()),
    ("log_reg", DecisionTreeClassifier())
])

cv = cross_validate(logreg, xtrain, ytrain, cv=10, scoring="precision")
print(f"Cross validation score: {cv['test_score'].mean():0.3%}")
```

    Cross validation score: 71.902%

```python
cv['test_score'].mean() + np.array([-1, 1]) * cv['test_score'].std()
```

    array([0.67284312, 0.76519701])

En este ejemplo, vemos que al agregar un nuevo feature, nuestro *accuracy* promedio se eleva a un *76%*. A pesar de esta mejora en el modelo, **la representación de los valores hacia el modelo es importante para una mejor clasificación.**

la variable *margin* es categorica, lo cual implica que sus valores [1, 2, 3, 4, 5], no representan en realidad ningun factor numérico de rango o importancia. Que *microbulated* esté más cerca de *obsured* que *spiculated* no implica realmente nada.

Al trabajar con una variable categorica, para otorgar una mejor *representación* al modelo, tranformamos la variable usando el método *One Hot Encoder*

Consideremos la siguiente columna de variables categóricas:

|obs| feat |
|--|-----|
|o1|A |
|o2|B |
|o3|C |
|o4|C |
|o5|B |

Podemos modificar esta columna para poder ingresarla a nuestro modelo creando 3 nuevos *features*, uno para cada cada categoría. Evidentemente, una observación solo tendrá una clase asignada, es decir, un solo uno seguido de una fila de ceros. En nuestro ejemplo anterior, esto quedaría como sigue:

|obs|feat_A|feat_B|feat_C|
|--|-----|-----|-----|
|o1|1|0|0|
|o2|0|1|0|
|o3|0|0|1|
|o4|0|0|1|
|o5|0|1|0|

De esta manera, ya podemos entrenar nuestro modelo usando variables categóricas.

Para lograr lo anterior con `scikit-learn`, podemos usar el *transformer* `OneHotEncoder` dentro de `preprocessing`

```python
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer

xtrain, ytrain = train[["age", "margin"]], train[["severity"]]
```

```python
margin_pipe.fit_transform(train[["margin"]])
```

```python
margin_pipe = Pipeline([
    ("imputer", SimpleImputer()),
    ("ohe", OneHotEncoder(categories="auto", sparse=False))
])

# Seleccionamos las transformaciones y columnas a ocupar para el modelo
cX = ColumnTransformer([
    ("clean_margin", margin_pipe, ["margin"]),
    ("age_selector", SimpleImputer(), ["age"])
])

logreg = Pipeline([
    ("cX", cX),
    ("model", LogisticRegression(solver="lbfgs", max_iter=500))
])

cv = cross_validate(logreg, train, train["severity"], cv=10)
print(f"Cross validation score: {cv['test_score'].mean():0.3%}")
```

    Cross validation score: 77.317%

```python
cv['test_score'].mean() + np.array([-1, 1]) * cv['test_score'].std()
```

    array([0.72314182, 0.82320131])

Ejercicio

Consideremos una tercera variable para nuestro modelo. Ocupando `["age", "margin", "shape"]`, modela la probabilidad de un tumor beningno. Transorma `"margin"` y `"shape"` usado `OneHotEncoder`

## Todo Junto

```python
train.dtypes[train.dtypes == np.float64].index
```

    Index(['bi_rads', 'age', 'shape', 'margin', 'density'], dtype='object')

```python
train.dtypes[train.dtypes == np.int].index
```

    Index(['severity'], dtype='object')

```python
input_encode = Pipeline([
    ("imputer", SimpleImputer()),
    ("ohe", OneHotEncoder(categories="auto"))
])

cX = ColumnTransformer([
    ("categoricals", input_encode, ["margin", "shape", "bi_rads", "density"]),
    ("continuous", SimpleImputer(), ["age"])
])

logreg = Pipeline([
    ("cX", cX),
    ("model", LogisticRegression(solver="lbfgs", max_iter=500))
])

cv = cross_validate(logreg, train, train["severity"], cv=10)
print(f"Cross validation score: {cv['test_score'].mean():0.3%}")

```

    Cross validation score: 81.869%

```python
cv['test_score'].mean() + np.array([-1, 1]) * cv['test_score'].std()
```

    array([0.77939166, 0.85799727])

Ejercicio

Con el modelo final, prueba el accuracy sobre el *test set*:

1. Entrena el modelo usando `logreg.fit(xtrain, ytrain)`
2. Crea `xtest`, `ytest`,
```python
xtest = test[["age", "margin", "shape", "density", "bi_rads"]].values
ytest = test.severity
```
3. Encuentra el resultado final del modelo

```python

```


# Eddy Kinetic Energy

This Jupyter notebook reproduces Figure 1 of the manuscript "Global changes in oceanic mesoscale currents over the satellite altimetry record". Eddy Kinetic Energy is defined as,

\begin{equation}
    \mathrm{EKE}(x,y) = \frac{1}{2}\rho_0\left(u'^2 + v'^2\right)
\end{equation}

Maps of EKE in Figure 1 of this study correspond to the the time-mean EKE:
\begin{equation}
    \overline{EKE}(x,y) = \overline{\frac{1}{2}\rho_0\left(u'^2 + v'^2\right)}
\end{equation}

Then linear trends are computed with a modified Mann-Kendall test (See Methods of manusript).

```python
# Import libraries
import xarray as xr
import cmocean as cm
import cartopy.crs as ccrs

import pylab as plt
import numpy as np
# Inline plotting
%matplotlib inline
```

```python
from plot_utils import truncate_colormap, ccrs_land, add_patches, vectorize
```

```python
dataset_EKE = xr.open_dataset('../datasets/EKE_timeseries.nc')
```

```python
rho=1025
```

### Load EKE timeseries dataset:

```python
EKE = dataset_EKE.EKE
```

```python
print(dataset_EKE)
```

    <xarray.Dataset>
    Dimensions:  (lat: 180, lon: 360, time: 9928)
    Coordinates:
      * time     (time) datetime64[ns] 1993-01-01 1993-01-02 ... 2020-03-07
      * lat      (lat) float32 -89.5 -88.5 -87.5 -86.5 -85.5 ... 86.5 87.5 88.5 89.5
      * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5
    Data variables:
        EKE      (time, lat, lon) float64 ...
    Attributes:
        title:         Eddy Kinetic Energy
        Description:   Eddy Kinetic Energy computed from AVISO+ altimetry products.
        Publication:   Dataset created for Martínez-Moreno, J. et. al. 2020: \n '...
        Author:        Josué Martínez-Moreno
        Contact:       josue.martinezmoreno@anu.edu.au
        Created date:  20/01/2021 19:43:34

Compute eddy kinetic energy density:

```python
EKE_density = EKE*rho
```

Slice data between 60°S - 60°N. Note that satellite observations resolve mesoscale eddies between 60°S - 60°N, the effective resolution outside these latitudes is larger than the Rossby radius of deformation.

```python
EKE_domain = EKE_density.sel(lat=slice(-60,60))
```

Compute time-mean:

```python
mean_EKE = EKE_domain.mean('time')
```

```python
print(mean_EKE)
```

    <xarray.DataArray 'EKE' (lat: 120, lon: 360)>
    array([[0.31153691, 0.28886137, 0.19771366, ..., 0.68070014, 0.64054056,
            0.41831776],
           [0.76847517, 0.4672041 , 0.33838088, ..., 3.26514164, 2.35748724,
            1.49459842],
           [1.01653231, 0.92538218, 1.12764545, ..., 5.42864003, 3.14268802,
            2.0671549 ],
           ...,
           [1.32344949, 1.95456613, 1.78162671, ..., 2.65080843, 1.51221806,
            1.14492844],
           [1.47564859, 1.95606196, 2.5863737 , ..., 2.1779201 , 1.52096168,
            1.42410538],
           [1.59359229, 1.97569259, 2.18671724, ..., 1.27116127, 1.39884663,
            1.7672212 ]])
    Coordinates:
      * lat      (lat) float32 -59.5 -58.5 -57.5 -56.5 -55.5 ... 56.5 57.5 58.5 59.5
      * lon      (lon) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5

### Import trends

```python
SSH_trends = xr.open_dataset('../datasets/SSH_trends.nc')
```

```python
EKE_trends = xr.open_dataset('../datasets/EKE_trends.nc')
```

Trends units are $J m^{-3} day^{-1}$, thus, here we convert to $J m^{-3} decade^{-1}$ by multipling by 3650 days.

```python
SSH_trends_per_decade = ( SSH_trends.trend * 10*365 )
EKE_trends_per_decade = ( EKE_trends.trend * 10*365 )
```

Slice data between 60°S - 60°N.

```python
SSH_trends = SSH_trends_per_decade.sel(y=slice(-60,60))
EKE_trends = EKE_trends_per_decade.sel(y=slice(-60,60))
```

```python
SSH_trends
```

<svg style="position: absolute; width: 0; height: 0; overflow: hidden">
<defs>
<symbol id="icon-database" viewBox="0 0 32 32">
<path d="M16 0c-8.837 0-16 2.239-16 5v4c0 2.761 7.163 5 16 5s16-2.239 16-5v-4c0-2.761-7.163-5-16-5z"></path>
<path d="M16 17c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z"></path>
<path d="M16 26c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z"></path>
</symbol>
<symbol id="icon-file-text2" viewBox="0 0 32 32">
<path d="M28.681 7.159c-0.694-0.947-1.662-2.053-2.724-3.116s-2.169-2.030-3.116-2.724c-1.612-1.182-2.393-1.319-2.841-1.319h-15.5c-1.378 0-2.5 1.121-2.5 2.5v27c0 1.378 1.122 2.5 2.5 2.5h23c1.378 0 2.5-1.122 2.5-2.5v-19.5c0-0.448-0.137-1.23-1.319-2.841zM24.543 5.457c0.959 0.959 1.712 1.825 2.268 2.543h-4.811v-4.811c0.718 0.556 1.584 1.309 2.543 2.268zM28 29.5c0 0.271-0.229 0.5-0.5 0.5h-23c-0.271 0-0.5-0.229-0.5-0.5v-27c0-0.271 0.229-0.5 0.5-0.5 0 0 15.499-0 15.5 0v7c0 0.552 0.448 1 1 1h7v19.5z"></path>
<path d="M23 26h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
<path d="M23 22h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
<path d="M23 18h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
</symbol>
</defs>
</svg>

<xarray.DataArray 'trend' (y: 120, x: 360)>
array([[0.03168946, 0.03138896, 0.03140686, ..., 0.03249941, 0.03257772,
        0.03212987],
       [0.0342333 , 0.03417025, 0.0321192 , ..., 0.03272745, 0.03313373,
        0.03256134],
       [0.03385112, 0.03402355, 0.0324814 , ..., 0.03538581, 0.03238973,
        0.03232404],
       ...,
       [0.03239187, 0.03245537, 0.03273162, ..., 0.03252293, 0.03205607,
        0.03191532],
       [0.03001143, 0.03137378, 0.03145368, ..., 0.03383376, 0.03158304,
        0.03017123],
       [0.02869239, 0.0290094 , 0.02998106, ..., 0.03086808, 0.03117517,
        0.02907432]])
Coordinates:
  * x        (x) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5
  * y        (y) float32 -59.5 -58.5 -57.5 -56.5 -55.5 ... 56.5 57.5 58.5 59.5

xarray.DataArray

'trend'

y: 120

x: 360

<input id='section-9143700a-ad6d-4db9-8f18-ac42d1424de9' class='xr-array-in' type='checkbox' checked><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>
0.03169 0.03139 0.03141 0.03123 ... 0.02991 0.03087 0.03118 0.02907

array([[0.03168946, 0.03138896, 0.03140686, ..., 0.03249941, 0.03257772,
        0.03212987],
       [0.0342333 , 0.03417025, 0.0321192 , ..., 0.03272745, 0.03313373,
        0.03256134],
       [0.03385112, 0.03402355, 0.0324814 , ..., 0.03538581, 0.03238973,
        0.03232404],
       ...,
       [0.03239187, 0.03245537, 0.03273162, ..., 0.03252293, 0.03205607,
        0.03191532],
       [0.03001143, 0.03137378, 0.03145368, ..., 0.03383376, 0.03158304,
        0.03017123],
       [0.02869239, 0.0290094 , 0.02998106, ..., 0.03086808, 0.03117517,
        0.02907432]])

<input id='section-2c24a4ef-efbd-4eb8-9ca3-18d13b54b0a6' class='xr-section-summary-in' type='checkbox'  checked>Coordinates: (2)

x

(x)

float32

0.5 1.5 2.5 ... 357.5 358.5 359.5
<input id='attrs-06733ab9-aebe-4536-9ce9-2491fb98aed4' class='xr-var-attrs-in' type='checkbox' disabled><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg><input id='data-f766ad94-a919-4a3e-a1b3-396fab9377f5' class='xr-var-data-in' type='checkbox'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>

array([  0.5,   1.5,   2.5, ..., 357.5, 358.5, 359.5], dtype=float32)

y

(y)

float32

-59.5 -58.5 -57.5 ... 58.5 59.5
<input id='attrs-751562fe-32f6-4ec7-83c0-9b12ab955857' class='xr-var-attrs-in' type='checkbox' disabled><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg><input id='data-5e807d6d-b788-45ff-bc5a-87f8d26fffb8' class='xr-var-data-in' type='checkbox'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>

array([-59.5, -58.5, -57.5, -56.5, -55.5, -54.5, -53.5, -52.5, -51.5, -50.5,
       -49.5, -48.5, -47.5, -46.5, -45.5, -44.5, -43.5, -42.5, -41.5, -40.5,
       -39.5, -38.5, -37.5, -36.5, -35.5, -34.5, -33.5, -32.5, -31.5, -30.5,
       -29.5, -28.5, -27.5, -26.5, -25.5, -24.5, -23.5, -22.5, -21.5, -20.5,
       -19.5, -18.5, -17.5, -16.5, -15.5, -14.5, -13.5, -12.5, -11.5, -10.5,
        -9.5,  -8.5,  -7.5,  -6.5,  -5.5,  -4.5,  -3.5,  -2.5,  -1.5,  -0.5,
         0.5,   1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
        10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5,  18.5,  19.5,
        20.5,  21.5,  22.5,  23.5,  24.5,  25.5,  26.5,  27.5,  28.5,  29.5,
        30.5,  31.5,  32.5,  33.5,  34.5,  35.5,  36.5,  37.5,  38.5,  39.5,
        40.5,  41.5,  42.5,  43.5,  44.5,  45.5,  46.5,  47.5,  48.5,  49.5,
        50.5,  51.5,  52.5,  53.5,  54.5,  55.5,  56.5,  57.5,  58.5,  59.5],
      dtype=float32)

<input id='section-2514d940-0a1d-4864-95e8-d31a88c4a068' class='xr-section-summary-in' type='checkbox' disabled >Attributes: (0)

```python
EKE_trends
```

<svg style="position: absolute; width: 0; height: 0; overflow: hidden">
<defs>
<symbol id="icon-database" viewBox="0 0 32 32">
<path d="M16 0c-8.837 0-16 2.239-16 5v4c0 2.761 7.163 5 16 5s16-2.239 16-5v-4c0-2.761-7.163-5-16-5z"></path>
<path d="M16 17c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z"></path>
<path d="M16 26c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z"></path>
</symbol>
<symbol id="icon-file-text2" viewBox="0 0 32 32">
<path d="M28.681 7.159c-0.694-0.947-1.662-2.053-2.724-3.116s-2.169-2.030-3.116-2.724c-1.612-1.182-2.393-1.319-2.841-1.319h-15.5c-1.378 0-2.5 1.121-2.5 2.5v27c0 1.378 1.122 2.5 2.5 2.5h23c1.378 0 2.5-1.122 2.5-2.5v-19.5c0-0.448-0.137-1.23-1.319-2.841zM24.543 5.457c0.959 0.959 1.712 1.825 2.268 2.543h-4.811v-4.811c0.718 0.556 1.584 1.309 2.543 2.268zM28 29.5c0 0.271-0.229 0.5-0.5 0.5h-23c-0.271 0-0.5-0.229-0.5-0.5v-27c0-0.271 0.229-0.5 0.5-0.5 0 0 15.499-0 15.5 0v7c0 0.552 0.448 1 1 1h7v19.5z"></path>
<path d="M23 26h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
<path d="M23 22h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
<path d="M23 18h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z"></path>
</symbol>
</defs>
</svg>

<xarray.DataArray 'trend' (y: 120, x: 360)>
array([[-1.1149713 , -1.53416653,  0.09166718, ...,  0.37783637,
        -3.08142165, -0.61952774],
       [ 0.2996844 ,  0.21137831,  0.1537179 , ...,  1.22302636,
         1.05485707,  0.62610941],
       [ 0.2526685 ,  0.22458577,  0.10885333, ...,  0.83192264,
         0.8170983 ,  0.51852736],
       ...,
       [ 0.00727586,  0.01700544, -0.04473273, ...,  0.27387962,
         0.01676706,  0.01584413],
       [-0.03828347,  0.065028  ,  0.02299369, ...,  0.2110845 ,
         0.03269822,  0.04709699],
       [-0.0293445 ,  0.08715716, -0.06647769, ...,  0.03816568,
         0.044588  ,  0.10532378]])
Coordinates:
  * x        (x) float32 0.5 1.5 2.5 3.5 4.5 ... 355.5 356.5 357.5 358.5 359.5
  * y        (y) float32 -59.5 -58.5 -57.5 -56.5 -55.5 ... 56.5 57.5 58.5 59.5

xarray.DataArray

'trend'

y: 120

x: 360

<input id='section-ea0a8cd2-71e3-4434-beb0-274545216b80' class='xr-array-in' type='checkbox' checked><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>
-1.115 -1.534 0.09167 0.07824 ... 0.04335 0.03817 0.04459 0.1053

array([[-1.1149713 , -1.53416653,  0.09166718, ...,  0.37783637,
        -3.08142165, -0.61952774],
       [ 0.2996844 ,  0.21137831,  0.1537179 , ...,  1.22302636,
         1.05485707,  0.62610941],
       [ 0.2526685 ,  0.22458577,  0.10885333, ...,  0.83192264,
         0.8170983 ,  0.51852736],
       ...,
       [ 0.00727586,  0.01700544, -0.04473273, ...,  0.27387962,
         0.01676706,  0.01584413],
       [-0.03828347,  0.065028  ,  0.02299369, ...,  0.2110845 ,
         0.03269822,  0.04709699],
       [-0.0293445 ,  0.08715716, -0.06647769, ...,  0.03816568,
         0.044588  ,  0.10532378]])

<input id='section-2ac79815-2035-4f78-99e5-86be9fad739d' class='xr-section-summary-in' type='checkbox'  checked>Coordinates: (2)

x

(x)

float32

0.5 1.5 2.5 ... 357.5 358.5 359.5
<input id='attrs-2ac03464-2181-4023-bf14-4a518c74823b' class='xr-var-attrs-in' type='checkbox' disabled><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg><input id='data-27a23328-c349-4a9d-baa5-0fc7213c611e' class='xr-var-data-in' type='checkbox'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>

array([  0.5,   1.5,   2.5, ..., 357.5, 358.5, 359.5], dtype=float32)

y

(y)

float32

-59.5 -58.5 -57.5 ... 58.5 59.5
<input id='attrs-4d6bf2c3-50e0-4238-8536-cd2864385dcd' class='xr-var-attrs-in' type='checkbox' disabled><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg><input id='data-8e62a6a1-123b-459d-9729-e8aeb903f861' class='xr-var-data-in' type='checkbox'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg>

array([-59.5, -58.5, -57.5, -56.5, -55.5, -54.5, -53.5, -52.5, -51.5, -50.5,
       -49.5, -48.5, -47.5, -46.5, -45.5, -44.5, -43.5, -42.5, -41.5, -40.5,
       -39.5, -38.5, -37.5, -36.5, -35.5, -34.5, -33.5, -32.5, -31.5, -30.5,
       -29.5, -28.5, -27.5, -26.5, -25.5, -24.5, -23.5, -22.5, -21.5, -20.5,
       -19.5, -18.5, -17.5, -16.5, -15.5, -14.5, -13.5, -12.5, -11.5, -10.5,
        -9.5,  -8.5,  -7.5,  -6.5,  -5.5,  -4.5,  -3.5,  -2.5,  -1.5,  -0.5,
         0.5,   1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
        10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5,  18.5,  19.5,
        20.5,  21.5,  22.5,  23.5,  24.5,  25.5,  26.5,  27.5,  28.5,  29.5,
        30.5,  31.5,  32.5,  33.5,  34.5,  35.5,  36.5,  37.5,  38.5,  39.5,
        40.5,  41.5,  42.5,  43.5,  44.5,  45.5,  46.5,  47.5,  48.5,  49.5,
        50.5,  51.5,  52.5,  53.5,  54.5,  55.5,  56.5,  57.5,  58.5,  59.5],
      dtype=float32)

<input id='section-0abac1fd-00ae-45f9-8a47-2e06f36a3fb6' class='xr-section-summary-in' type='checkbox' disabled >Attributes: (0)

### Plot figure 1 of manuscript

```python
plt.rcParams['xtick.major.pad']='1'

fig = plt.figure(figsize=(5, 5),dpi=300)

ax1 = fig.add_subplot(3, 1, 1, projection = ccrs.Robinson(central_longitude=180))

clm1 = SSH_trends.plot(ax=ax1, transform = ccrs.PlateCarree(), add_colorbar=False,
              cmap = cm.cm.balance, vmin=-0.1, vmax=0.1, rasterized=True)
ax1.set_extent([0.1,359.99,-60, 60], crs=ccrs.PlateCarree())
ax1.add_feature(ccrs_land)
add_patches(ax1)

ax_w1 = plt.axes([0.08, 0.658, 0.12, 0.223])

zonal_sum_tke=(SSH_trends.T).mean('x')
zonal_sum_tke=zonal_sum_tke

pos=np.ma.masked_where(zonal_sum_tke<0, zonal_sum_tke)
ax_w1.fill_betweenx(zonal_sum_tke.y, pos*0, pos, color='r')

neg=np.ma.masked_where(zonal_sum_tke>0, zonal_sum_tke)

ax_w1.fill_betweenx(zonal_sum_tke.y, neg, neg*0, color='b')

ax_w1.set_ylim(-60,60)
ax_w1.set_xlim(0,0.06)
ax_w1.set_xlabel("$m\ decade^{-1}$",fontsize=5)
ax_w1.xaxis.set_label_coords(0.5, -0.12)
ax_w1.xaxis.set_major_locator(plt.MaxNLocator(3))

ax_w1.tick_params(axis='both', which='major', labelsize=5)
ax_w1.set_yticks(range(-60,61,30))
ax_w1.set_yticklabels(['60$^\circ$S','30$^\circ$S','0$^\circ$','30$^\circ$N','60$^\circ$N'])
ax_w1.grid(linewidth=0.5)
ax_w1.text(0.005,47,'a',fontweight='bold')
ax_w1.text(0.07,47,'b',fontweight='bold')

###############################################################
###############################################################
###############################################################

ax2 = fig.add_subplot(3, 1, 2, projection = ccrs.Robinson(central_longitude=180))

clm2 = mean_EKE.plot(ax=ax2, transform = ccrs.PlateCarree(), add_colorbar=False,
              cmap = truncate_colormap(cm.cm.amp, 0, 0.8), vmin = 0, vmax = 120, rasterized=True)
ax2.set_extent([0.1,359.99,-60, 60], crs=ccrs.PlateCarree())
ax2.add_feature(ccrs_land)
add_patches(ax2)

ax_w2 = plt.axes([0.08, 0.392, 0.12, 0.223])

zonal_sum_tke=(mean_EKE.T).mean('lon')
zonal_sum_tke=zonal_sum_tke

pos=np.ma.masked_where(zonal_sum_tke<0, zonal_sum_tke)
ax_w2.fill_betweenx(zonal_sum_tke.lat, pos*0, pos, color='r')

neg=np.ma.masked_where(zonal_sum_tke>0, zonal_sum_tke)

ax_w2.fill_betweenx(zonal_sum_tke.lat, neg, neg*0, color='b')

ax_w2.set_ylim(-60,60)
ax_w2.set_xlim(0,60)
ax_w2.set_xlabel("$J\ m^{-3}$",fontsize=5)
ax_w2.xaxis.set_label_coords(0.5, -0.12)
ax_w2.xaxis.set_major_locator(plt.MaxNLocator(3))

ax_w2.tick_params(axis='both', which='major', labelsize=5)
ax_w2.set_yticks(range(-60,61,30))
ax_w2.set_yticklabels(['60$^\circ$S','30$^\circ$S','0$^\circ$','30$^\circ$N','60$^\circ$N'])
ax_w2.grid(linewidth=0.5)
ax_w2.text(5,47,'c',fontweight='bold')
ax_w2.text(68,47,'d',fontweight='bold')

###############################################################
###############################################################
###############################################################

ax3 = fig.add_subplot(3, 1, 3, projection = ccrs.Robinson(central_longitude=180))

clm3 = EKE_trends.plot(transform=ccrs.PlateCarree(), add_colorbar=False,
              cmap=cm.cm.balance, vmin=-10, vmax=10,ax=ax3, rasterized=True)
ax3.set_extent([0.1,359.99,-60, 60], crs=ccrs.PlateCarree())
ax3.add_feature(ccrs_land)
add_patches(ax3)

ax_w3 = plt.axes([0.08, 0.125, 0.12, 0.223])

increase_tke_gw=(EKE_trends.mean('x'))

pos=np.ma.masked_where(increase_tke_gw<0, increase_tke_gw)
ax_w3.fill_betweenx(increase_tke_gw.y, pos*0 , pos, color='r')

neg=np.ma.masked_where(increase_tke_gw>0, increase_tke_gw)

ax_w3.fill_betweenx(increase_tke_gw.y, neg, neg*0, color='b')

ax_w3.set_ylim(-60,60)
ax_w3.set_xlim(-1.5,1.5)
ax_w3.set_xlabel("$J\ m^{-3}\ decade^{-1}$",fontsize=5)
ax_w3.xaxis.set_label_coords(0.5, -0.12)
ax_w3.xaxis.set_major_locator(plt.MaxNLocator(3))

ax_w3.tick_params(axis='both', which='major', labelsize=5)
ax_w3.set_yticks(range(-60,61,30))
ax_w3.set_yticklabels(['60$^\circ$S','30$^\circ$S','0$^\circ$','30$^\circ$N','60$^\circ$N'])
ax_w3.grid(linewidth=0.5)
ax_w3.text(-1.3,47,'e',fontweight='bold')
ax_w3.text(2,47,'f',fontweight='bold')

#### Colorbars ####

cbar_ax = fig.add_axes([0.83, 0.67, 0.01, 0.2])
cbar=fig.colorbar(clm1, cax=cbar_ax,orientation='vertical',extend='both')
cbar.set_label('$m\ decade^{-1} $', rotation=90, fontsize=7)
cbar.ax.tick_params(labelsize=7)

cbar_ax = fig.add_axes([0.83, 0.40, 0.01, 0.2])
cbar=fig.colorbar(clm2, cax=cbar_ax,orientation='vertical',extend='max')
cbar.set_label('$J\ m^{-3} $', rotation=90, fontsize=7)
cbar.ax.tick_params(labelsize=7)

cbar_ax = fig.add_axes([0.83, 0.14, 0.01, 0.2])
cbar=fig.colorbar(clm3, cax=cbar_ax,orientation='vertical',extend='both')
cbar.set_label('$J\ m^{-3}\ decade^{-1} $', rotation=90, fontsize=7)
cbar.ax.tick_params(labelsize=7)

##### Titles #####
ax1.text(0.12,0.85,r'$\mathrm{SSH}$ trends',fontsize=8,transform=ax1.transAxes)
ax3.text(0.18,0.85,r'$\overline{\mathrm{EKE}}$',fontsize=8,transform=ax3.transAxes)
ax2.text(0.12,0.85,r'$\mathrm{EKE}$ trends',fontsize=8,transform=ax2.transAxes)

plt.savefig('../LaTeX/figures/global_trend_WBC_GW_nsignif.pdf',dpi=300,bbox_inches='tight')
```

**Figure 1 Sea Surface Height trend, mean surface Eddy Kinetic Energy and surface Eddy Kinetic Energy trend between 1993-2019.** (c) zonally averaged mean EKE; (d) map of mean EKE; (e) zonally averaged EKE trend; (f) map of EKE trend (55.4% of global area is significant above 95% confidence level). In panel (d), solid contours show positive EKE trends and dashed contours show negative EKE trends (contours of ±5 J m−3 decade−1).

*Note: Figure 1 a & b corresponds to the SSH trends from AVISO+ (See Methods).*