%matplotlib inline
import numpy as np
import pylab as pl

Serie = np.random.normal(0,1,1000)
# Impresión de la media y la desviación
print Serie.mean()
print Serie.std()

"""
Explanation: Medidas de Localización
Medidas típicas, que son paramétricas:
- Media: $\mu = \frac{\sum_{i=1}^{N} x_i}{N} $
- Desviación $\sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^{N} (x_i -\mu)^2}$
- Asimetría.
- etc...
Existen otras medidas que son no paramétricas, estas pueden ser:
- Mediana.
- Quintiles.
- Deciles.
- ..
- etc.
- Cuantiles.
Dependiendo de la medida esta puede ser o no robusta, esto significa que no se vea afectada fácilmente por aspectos tales como:

Cantidad de datos.
Calidad de los datos.


Ejemplo de medidas paramétricas:
End of explanation
"""


print np.percentile(Serie,50)
print np.median(Serie)

"""
Explanation: Pero miren lo que pasa con la mediana
End of explanation
"""


def GraficaHistogramaParam(Values,bins=15):
    # Genera el histograma de valores
    h,b = np.histogram(Values,bins=bins)
    h = h.astype(float); h = h / h.sum()
    b = (b[1:]+b[:-1])/2.0
    # Obtiene la figura 
    fig=pl.figure(figsize=(10,8))
    ax=fig.add_subplot(111)
    ax.plot(b,h,'b',lw=2)
    ax.fill_between(b,h,color='b',alpha=0.2)
    ax.set_xlabel('$X$',size=15)
    ax.set_ylabel('$f(x)$',size=15)
    ax.set_xlim(-3,3)
    ax.set_ylim(0,h.max()+0.05)
    ax.grid(True)
    ax.legend(loc=0)
    # Grafica las localizaciones
    ax.vlines(Values.mean(),0,h.max()+0.05,lw=2,color='r')
    ax.vlines([Values.mean()+Values.std(),Values.mean()-Values.std()],0,h.max()+0.05,lw=1,color='r')
    pl.show()


def GraficaHistogramaNoParam(Values,bins=15):
    # Genera el histograma de valores
    h,b = np.histogram(Values,bins=bins)
    h = h.astype(float); h = h / h.sum()
    b = (b[1:]+b[:-1])/2.0
    # Obtiene la figura 
    fig=pl.figure(figsize=(10,8))
    ax=fig.add_subplot(111)
    ax.plot(b,h,'b',lw=2)
    ax.fill_between(b,h,color='b',alpha=0.2)
    ax.set_xlabel('$X$',size=15)
    ax.set_ylabel('$f(x)$',size=15)
    ax.set_xlim(-3,3)
    ax.set_ylim(0,h.max()+0.05)
    ax.grid(True)
    ax.legend(loc=0)
    # Grafica las localizaciones
    ax.vlines(np.percentile(Values,50),0,h.max()+0.05,lw=2,color='r')
    ax.vlines([np.percentile(Values,10),np.percentile(Values,90)],0,h.max()+0.05,lw=1,color='r')
    pl.show()


"""
Explanation: Que ocurre

La media esperada es 0, sin embargo esta presenta una diferencia.
Igualmente ocurre conla desviación estandar.



Pero no ocurre lo mismo con la mediana, en 1000 datos se presenta dos ordenes de magnitud más cerca de 0 

La siguiente figura ejemplifica esto:
Definición de función para graficar
End of explanation
"""


GraficaHistogramaParam(Serie)

"""
Explanation: Grafica de las medidas de localización paramétricas
End of explanation
"""


GraficaHistogramaNoParam(Serie)

"""
Explanation: Gráfica de medidas no paramétricas
End of explanation
"""


Serie = np.random.uniform(2.5,10,2e5)
print Serie.mean()
print np.median(Serie)
from scipy import stats as st
print st.skew(Serie)


"""
Explanation: Caso con menos datos
En un caso con menor cantida dde datos se espera tener una mayor diferencia entre ambas medidas, de ahí si inestabilidad:
End of explanation
"""


medianas = np.zeros(20000)
medias=np.zeros(20000)
for i in range(20000):
    Serie = np.random.normal(0,1,25)
    medias[i] = Serie.mean()
    medianas[i]=np.median(Serie)

def ComparaHistogramas(Vec1,Vec2,bins=15):
    # Genera el histograma de valores
    h1,b1 = np.histogram(Vec1,bins=bins)
    h1 = h1.astype(float); h1 = h1 / h1.sum()
    b1 = (b1[1:]+b1[:-1])/2.0
    h2,b2 = np.histogram(Vec2,bins=bins)
    h2 = h2.astype(float); h2 = h2 / h2.sum()
    b2 = (b2[1:]+b2[:-1])/2.0
    #Genera la figura 
    fig=pl.figure(figsize=(10,8))
    ax=fig.add_subplot(111)
    ax.plot(b1,h1,'b',lw=2,label='Vec 1')
    ax.plot(b2,h2,'r',lw=2,label='Vec 2')
    ax.fill_between(b1,h1,color='b',alpha=0.2)
    ax.fill_between(b2,h2,color='r',alpha=0.2)
    ax.set_xlabel('$X$',size=15)
    ax.set_ylabel('$f(x)$',size=15)
    ax.set_xlim(-1,1)
    ax.set_ylim(0,h1.max()+0.05)
    ax.grid(True)
    ax.legend(loc=0)
    # Grafica las localizaciones
    pl.show()
    return h1,h2

HistMedianas, HistMedias = ComparaHistogramas(medianas,medias)

"""
Explanation: Ejercicio para observar robustez en ambas medidas
En el siguiente ejercicio generamos 200 veces series aleatorias cada una con 25 entradas, 
luego vamos a comprar como son las diferencias entre las medias y las medianas encontradas para cada uno de los casos.
End of explanation
"""


Serie = np.random.normal(0,1,50)
fig = pl.figure(figsize=(9,7))
pl.plot(Serie)
pl.grid(True)

"""
Explanation: Suceptibilidad a Datos Atípicos
Un dato atípico se define como aquel dato que se encuentra fuera del rango de oscilación de los datos, o bien que no es coherente con la física del fenómeno que se está sensando, los siguientes son ejemplos de datos atípicos:

Valores exageradamente altos.
Valores negativos en casos de fenómenos sin valores negativos.
Valores fuera de un rango definido.
Secuencia de valores con el mismo valor (no es tanto atípico, pero si es un indicio de problemas)

Una forma de identificarlos es a partir de la media de los valores y la desviación, o los percentiles sobre los que se ubiquen.

$ValAtipico > \mu + N \sigma$, donde $N$ oscila de acuerdo a lo fuerte que se quiera hacer la pregunta 
$ValAtipico > P_{99.9}$


Dependiendo de la cantidad de registros en los datos, de la cantidad de valores atípicos y de los valores que estos tengan pueden tener o no consecuencias sobre la serie y sobre posteriores análisis que se realicen sobre la misma.
Ejemplo de robustez ante datos atípicos
End of explanation
"""


Serie2 = np.copy(Serie)
Serie2[10] = 50.0
fig = pl.figure(figsize=(9,7))
pl.plot(Serie2)
pl.plot(Serie)

"""
Explanation: Incertemos un dato loco, que se salga
End of explanation
"""


print Serie.mean()
print Serie2.mean()

"""
Explanation: Ahora veamos que ocurre con la media:
End of explanation
"""


print np.median(Serie)
print np.median(Serie2)

"""
Explanation: Y que ocurre con la mediana:
End of explanation
"""


def CreaOutliers(vect,NumOut,Mult=10):    
    # Encuentra el rango de oscilacion 
    Per = np.array([np.percentile(vect,i) for i in [0.1,99.9]])
    # Genera los aleatorios 
    vectOut = np.copy(vect)
    for i in np.random.choice(vect.shape[0],NumOut):
        p = np.random.choice(2,1)[0]
        vectOut[i] = vectOut[i] + Per[p]*Mult*np.random.uniform(2,15,1)[0]
    return vectOut

print Serie3.mean()
print Serie.mean()
print '----------'
print np.median(Serie3)
print np.median(Serie)

# Definición de variables
N = 1000
S1 = np.random.normal(0,1,N)
Medias = []; Std = []
Medianas = []; R25_75 = []
# Introduccion de outliers
for i in np.arange(5,200):
    S2 = CreaOutliers(S1, i)
    Medias.append(S2.mean())
    Medianas.append(np.median(S2))
    Std.append(S2.std())
    R25_75.append(np.percentile(S2,75)-np.percentile(S2,25))
Medias = np.array(Medias)
Medianas = np.array(Medianas)


"""
Explanation: Introducción de múltiples Outliers
Que pasa si se introduce una alta cantidad de datos atípicos?, es decir como es la tasa a 
la cual la media puede ir pasando a ser cada ves un estimador con un mayor error?.
End of explanation
"""


# Definición de variables
N = 1000
S1 = np.random.uniform(0,1,N)
Medias = []; Std = []
Medianas = []; R25_75 = []
# Introduccion de outliers
for i in np.arange(5,200):
    S2 = CreaOutliers(S1, i)
    Medias.append(S2.mean())
    Medianas.append(np.median(S2))
    Std.append(S2.std())
    R25_75.append(np.percentile(S2,75)-np.percentile(S2,25))
Medias = np.array(Medias)
Medianas = np.array(Medianas)


fig = pl.figure(figsize=(13,5))
ax = fig.add_subplot(121)
ax.scatter(Medianas,Medias,c=np.arange(5,200))
ax.set_xlabel('Mediana',size=14)
ax.set_ylabel('Media $\mu$',size=14)
ax = fig.add_subplot(122)
ax.scatter(R25_75,Std,c=np.arange(5,200))
#ax.set_xlim(0,1)
ax.set_xlabel('Rango $25%$ - $75\%$',size=14)
ax.set_ylabel('Desviacion $\sigma$',size=14)
pl.show()

"""
Explanation: Resultados:
Según lo obtenido la mediana se ve altamente afectada, y la desviación también:
Caso de una distribución Normal
End of explanation
"""


fig = pl.figure(figsize=(13,5))
ax = fig.add_subplot(121)
ax.scatter(Medianas,Medias,c=np.arange(5,200))
ax.set_xlabel('Mediana',size=14)
ax.set_ylabel('Media $\mu$',size=14)
ax = fig.add_subplot(122)
ax.scatter(R25_75,Std,c=np.arange(5,200))
#ax.set_xlim(0,1)
ax.set_xlabel('Rango $25%$ - $75\%$',size=14)
ax.set_ylabel('Desviacion $\sigma$',size=14)
pl.show()

"""
Explanation: Caso de una distribución uniforme
End of explanation
"""


S1 = np.random.normal(0,1,100)
a=pl.boxplot(S1)
a=pl.xlabel('Serie')

"""
Explanation: Cuantiles
Como una medida no paramétrica de la distribución de los datos se encuentran los cuantiles,
el más conocido es la mediana, sin embargo se pueden obtener cuantiles de cualquier medida.
Que representan : El cuantil del 25% igual a 3.56, indica que el 25% de los datos son iguales o inferiores a 3.56.
Al ser una medida no paramétrica se ve poco afectada por errores en los datos y por datos atípicos.
End of explanation
"""


S1 = np.random.normal(0,1,100)
S2 = CreaOutliers(S1,10)
Per1 = np.array([np.percentile(S1,i) for i in range(10,91,10)])
Per2 = np.array([np.percentile(S2,i) for i in range(10,91,10)])

fig = pl.figure(figsize=(9,7))
ax = fig.add_subplot(111)
ax.scatter(Per1,Per2,s=40)
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.grid(True)
ax.set_xlabel('Deciles Observados',size =14); ax.set_ylabel('Deciles Alterados',size=14) 
ax.plot([-2,2],[-2,2],lw=0.5,c='k')
pl.show()

"""
Explanation: Caso de Introducción de Outliers
QQ plot de las series donde se introducen outliers y la serie en donde no
End of explanation
"""


