Estadistica Con Python I

download Estadistica Con Python I

of 14

Transcript of Estadistica Con Python I

  • 8/20/2019 Estadistica Con Python I

    1/30

    Tutorial sobre estadística en Python: I

    Distribuciones, gráficos, clustering

    In [1]:   import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport scipy.stats as s

    In [2]:   print np.__version__import scipyprint scipy.__version__print matplotlib.__version__

    Referencias

    Este es un tutorial sobre análisis estadístico en Python: tutorial R. Olson

    (http://www.randalolson.com/2012/08/06/statistical-analysis-made-easy-in-python/)

    Esta es la documentación de la librería estadística de SciPy: scipy.stats

    (http://docs.scipy.org/doc/scipy/reference/tutorial/stats.html)

    Otro tutorial también sobre análisis estadístico y Python, en forma de notebook: Statistical Data Analysis in

    Python (https://github.com/fonnesbeck/statistical-analysis-python-tutorial), con una parte interesante

    dedicada a modelos

    Y un curso con el material disponible en forma de notebooks: Coursera data analysis course, done in

    Python (https://github.com/herrfz/dataanalysis). Aunque he descargado todos los notebooks en la carpeta

    Coursera_dataanalysis, lo mejor es utilizar el índice del fichero readme

    (https://github.com/herrfz/dataanalysis/blob/master/README.md)

    El material completo del curso de Cursera está aquí: Coursera Data Analysis

    (https://github.com/jtleek/dataanalysis) , aunque los pdf de las lecciones los he descargado en

    Dropbox/personal/R proyectos

    El blog Micropore (http://micropore.wordpress.com/) tiene bastantes cosas de Python y astronomía

    Generación de valores aleatorios con Numpy

    1.8.0

    0.9.01.3.1

    https://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/jtleek/dataanalysishttps://github.com/herrfz/dataanalysis/blob/master/README.mdhttps://github.com/herrfz/dataanalysis/blob/master/README.mdhttps://github.com/herrfz/dataanalysis/blob/master/README.mdhttps://github.com/herrfz/dataanalysishttps://github.com/herrfz/dataanalysishttps://github.com/herrfz/dataanalysishttps://github.com/herrfz/dataanalysishttps://github.com/herrfz/dataanalysishttps://github.com/fonnesbeck/statistical-analysis-python-tutorialhttps://github.com/fonnesbeck/statistical-analysis-python-tutorialhttp://docs.scipy.org/doc/scipy/reference/tutorial/stats.htmlhttp://micropore.wordpress.com/https://github.com/jtleek/dataanalysishttps://github.com/herrfz/dataanalysis/blob/master/README.mdhttps://github.com/herrfz/dataanalysishttps://github.com/fonnesbeck/statistical-analysis-python-tutorialhttp://docs.scipy.org/doc/scipy/reference/tutorial/stats.htmlhttp://www.randalolson.com/2012/08/06/statistical-analysis-made-easy-in-python/

  • 8/20/2019 Estadistica Con Python I

    2/30

    In [3]:  # generación de valores aleatorios según distribución normal # normal(loc=0.0, scale=1.0, size=None) # loc es la media, scale la desviación estándar # size es un un valor entero

     # o una tupla con el shape deseado

    alturas = np.random.normal(188, 3, 20)print alturas

    In [4]:  # Cuando deseamos valores según la distribución normal estándar # (mean=0, stdev =1), podemos utilizar # standard_normal(size), donde size puede ser un valor entero o un shape

    np.random.standard_normal((4,2))

    In [22]:  # Generación de valores aleatorios según distribución binomial # binomial(n, p, size)

    coinFlips = np.random.binomial(1, 0.5, 10)print coinFlips

    In [9]:  # Generación de valores aleatorios según la distribución uniforme # En un intervalo semiabierto [low, high) # uniform(low=0.0, high=1.0, size=1) # size puede ser un entero o una tupla indicando un shape

    np.random.uniform(size=10)

    [ 190.09944441 193.31053145 192.10140399 189.08948296 194.01804315  187.08032995 190.53477337 188.38027873 190.74397206 191.10964306  196.58267576 187.70675913 191.99139412 188.14624532 190.57930883  191.81629275 185.77887953 189.25968599 183.90044742 187.989887 ]

    Out[4]: array([[-0.12656278, 1.04534887],  [ 0.96472613, -0.95483344],  [ 0.40339144, -2.29152082],  [-1.15241441, 0.73026361]])

    [0 1 1 1 1 1 0 0 1 1]

    Out[9]: array([ 0.5526518 , 0.59462721, 0.13537618, 0.91364061, 0.59570865,  0.24854146, 0.7054177 , 0.17873533, 0.70354042, 0.30468929])

  • 8/20/2019 Estadistica Con Python I

    3/30

    In [15]:  # Generación de valores aleatorios enteros según la distribución unifor me # en el intervalo semiabierto [low, high) # o bien, si high se omite, en [0, low) # size es un entero o una tupla indicando un shape #randint(low, high=None, size=None)

    print np.random.randint(0,12,(3,4))

     # También se puede utilizar random_integers, similar a la anterior # pero que genera valores enteros en el intervalo cerrado #[low, high]print

    print np.random.random_integers(1,6, size=10)

    Obtención de muestras con Numpy

    In [16]:  # random.choice(a, size=None, replace=True, p=None) # a es un array 1-D obligatoriamente, o bien un num. entero # (ver ejemplo siguiente) # size es un valor entero o una tupla indicando un shape

     # p es un array de probabilidades de igual longitud que a # si se omite se supone distribución uniforme para a # replace=False si se desea una muestra sin remplazamiento

    np.random.choice(alturas, size=5, replace=False)

    In [18]:  # Otra posibilidad es hacer a igual a un valor entero n # Viene bien para por ejemplo indexar con él

     # En este caso las muestras se toman del array arange(n)

    np.random.choice(10, size = (2,3), replace=False)

    Fijar una semilla

    [[ 5 2 0 8] [ 8 11 11 7] [11 9 0 9]]

    [6 6 3 4 1 4 1 6 6 1]

    Out[16]: array([ 196.58267576, 190.74397206, 189.25968599, 191.99139412,  189.08948296])

    Out[18]: array([[1, 2, 8],  [3, 9, 4]])

  • 8/20/2019 Estadistica Con Python I

    4/30

    In [71]:  # Vamos obteniendo valores diferentes en cada llamada a la funciónnp.random.seed(12345)print np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)

    In [76]:  # Y si volvemos a activar la semilla, recomenzamos la misma secuencianp.random.seed(12345)print np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)

    In [77]:  # Sin embargo una nueva celda del notebook # comienza simepre con una semilla aleatoria # a menos que volvamos a fijar una semillaprint np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)print np.random.random_integers(1,6, size=10)

    Estadística descriptiva

    Media

    [3 6 6 2 5 2 6 3 6 2]

    [2 4 2 4 1 3 2 4 6 3]

    [2 3 4 6 1 6 2 4 5 1]

    [3 6 6 2 5 2 6 3 6 2][2 4 2 4 1 3 2 4 6 3]

    [2 3 4 6 1 6 2 4 5 1]

    [4 5 5 6 4 4 2 4 6 3][6 4 1 6 3 5 6 4 4 5][4 1 4 1 1 4 6 3 5 5]

  • 8/20/2019 Estadistica Con Python I

    5/30

    In [295]: np.random.seed(12345)x =np.random.randint(0,12,(3,4))print x

     # Obtener la media de todos los valores del arrayprint "media total: ", x.mean()

     # Obtener la media de cada columnaprint "media calculada variando la fila: ", x.mean(axis=0)

     # Obtener la media de cada filaprint "media calculada variando la columna: ", x.mean(axis=1)

    Varianza y desviación estándar 

    In [291]:  # varianza muestral (dividiendo por n-1)print x.var(ddof=1)print x.var(ddof=1, axis=0)print x.var(ddof=1, axis=1)

    In [292]:  # Desviación típica muestralprint x.std(ddof=1)print x.std(ddof=1, axis=0)print x.std(ddof=1, axis=1)

    Relación lineal entre dos variables

    En este apartado utilizaremos el paquete estadístico de scipy, que se importa con:

    "import scipy.stats as s"

    Coeficiente de correlación r de Pearson

    [[ 2 5 1 4] [ 9 5 2 1] [ 6 1 11 9]]media total: 4.66666666667

    media calculada variando la fila: [ 5.66666667 3.66666667 4.666666674.66666667]

    media calculada variando la columna: [ 3. 4.25 6.75]

    12.2424242424[ 12.33333333 5.33333333 30.33333333 16.33333333][ 3.33333333 12.91666667 18.91666667]

    3.49891758154

    [ 3.51188458 2.30940108 5.50757055 4.04145188][ 1.82574186 3.59397644 4.34932945]

  • 8/20/2019 Estadistica Con Python I

    6/30

    In [54]:  # Construyamos dos arrays x e y con los que experimentar

    np.random.seed(12345)

    x = np.random.uniform(0,10,size=20)noise = np.random.uniform(0, 3, size=20)y = 5 + x + noiseplt.scatter(x,y);

    In [45]: r, p = s.pearsonr(x,y)print r, p

    El coeficiente de determinación es el cuadrado del coeficiente de correlación , y se interpreta como elporcentaje de variabilidad de la variable que es explicado por el modelo lineal.

    In [39]:  # El valor de r**2 es el coeficiente de determinación, # que indica la fortaleza de la relación entre ambas variables

    print u"El coeficiente de determinación es %.2f" %r**2

     # El 93% de la variación de y se puede explicar por la variable x, # el resto será debido a causas desconocidas, variables ocultas # o variabilidad inherente

    Regresión lineal

    Cálculo de los parámetros de la recta de regresión lineal se hacen con la función linregress() de Scipy.

    http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mstats.linregress.html

    In [48]: slope, intercept, r_value, p_value, std_err = s.linregress(x, y)

    0.966159857324 4.88820573698e-12

       

       

    El coeficiente de determinación es 0.93

  • 8/20/2019 Estadistica Con Python I

    7/30

    In [49]:   print r_value

    In [53]: plt.scatter(x,y)plt.plot([0, 10], [intercept, slope * 10 + intercept], '-r', lw=2);

    Factores / variables categóricas

    In [228]:  # Los factores de R equivalen a las variables categóricas de Pandasciudades = np.array(['Madrid', 'Cuenca', 'Toledo', 'Madrid', 'Soria', 'Toledo', 'Madrid'])

    cF = pd.Categorical.from_array(ciudades)cF

    In [231]:  # Los niveles de la variable categórica son los valores diferentes quetomacF.levels

    In [232]:  # Los labels son el contenido de la variable, codificada numéricamente # haciendo referencia a los levelscF.labels

    Las distribuciones de probabilidad con Scipy

    Cuestiones de notación

    0.966159857324

    Out[228]: Categorical:[Madrid, Cuenca, Toledo, Madrid, Soria, Toledo, Madrid]Levels (4): Index(['Cuenca', 'Madrid', 'Soria', 'Toledo'], dtype=object)

    Out[231]: Index([u'Cuenca', u'Madrid', u'Soria', u'Toledo'], dtype=object)

    Out[232]: array([1, 0, 3, 1, 2, 3, 1])

  • 8/20/2019 Estadistica Con Python I

    8/30

    Dada una variable aleatoria , el rango de , es el conjunto de valores que puede tomar. Cada

    valor es llamado un cuantil, y la probabilidad de que tome el valor se designa por

    Un "random variate" es un resultado particular de una variable aleatoria.

    Vamos a utilizar el paquete stats de Scipy, que normalmente se importa con: "import scipy.stats as s".

    Todas las distribuciones de probabilidad tienen en este paquete un tratamiento similar, con los mismos

    métodos en todas ellas. Por ejemplo, para ver los métodos de la distribución normal, hacer:

    print s.norm.doc

    La distribución normal

    In [12]:  # pdf: Probability Density Function

     # s.norm.pdf(array x de cuantiles, loc, scale) # loc es la media, scale es la desviación estándarx = np.linspace(-5, 5, num=100)normalDensity = s.norm.pdf(x, loc=0, scale=1)plt.plot(x, normalDensity)plt.xlabel('cuantiles');

    In [13]:  # cdf: Cumulative Distribution Function # s.norm.pdf(array de cuantiles, loc, scale) # loc es la media, scale la desviación estándar

     # Probabilidad de |X| < sigmasigma = 4print "%.3f %%" %(s.norm.cdf(sigma, loc=0, scale=sigma)-s.norm.cdf(-sigma, loc=0, scale=sigma))

    X X R     

    X     

        ∈    R     

    X     

    X    

    P      (   X      =         )  

    0.683 %

  • 8/20/2019 Estadistica Con Python I

    9/30

    In [15]:  # ppf: Percent Point Function (Inversa de CDF)

    s.norm.ppf(0.683/2 + 0.5, loc=0, scale=1)

    In [16]:  # Devuelve la media y varianza de la distribucións.norm.stats(loc=3, scale=4)

    In [17]:  # rvs: generación de valores aleatorios (random variates) # bajo la variable aleatoria considerada ( la normal en el ejemplo)

    s.norm.rvs(loc=5, scale=2, size=10)

    "Freezing"

    Con el fin de no tener que escribir en cada caso los parámetros loc y scale de una distribución, se puede

    definir una variable aleatoria con "rv" del tipo y con los parámetros deseados, y despues referirnos a ella

    con el nombre de la variable que le hayamos dado. Es decir, el objeto que hemos definido tiene los

    mismos métodos que la distribución original

    In [21]: mi_rv = s.norm(loc=5, scale=2)mi_rv.stats()

    In [24]: mi_rv.rvs(size=10)

    Distribución binomial

    In [34]:  # pmf: Probability Mass Function

     # Sustituye a pdf en las variables discretas # pmf(x, n, pr)

    x=[0,1,2]s.binom.pmf(x,2, 0.5)

    Gráficos exploratorios

    Representar muchos puntos

    Out[15]: 1.0006418287624492

    Out[16]: (array(3.0), array(16.0))

    Out[17]: array([ 2.07049884, 5.71139329, 5.58134749, 6.19376692, 5.92728227,  4.37584035, 4.60381517, 7.66738297, 8.44622194, 1.63065077])

    Out[21]: (array(5.0), array(4.0))

    Out[24]: array([ 6.16432526, 5.48459398, 3.25193664, 3.58073744, 7.89030591,  6.35598259, 4.12032957, 4.99845849, 6.55752111, 3.26517431])

    Out[34]: array([ 0.25, 0.5 , 0.25])

  • 8/20/2019 Estadistica Con Python I

    10/30

    In [297]: x = np.random.normal(size=1e4)

    y = np.random.normal(size=1e4)plt.scatter(x, y);

     # con plt.plot(x,y, 'o') hubieramos obtenido el mismo resultado

     Aquí no se ve nada, una posibilidad es representar una muestra (consideramos los valores de x e y

    emparejados)

    In [298]: r = np.random.choice(10**4, size=1000, replace=False)plt.scatter(x[r], y[r]);

    Out[297]:

  • 8/20/2019 Estadistica Con Python I

    11/30

    In [309]:  # También puede intentarse variar la transperencia # y tamaño de los puntosplt.scatter(x, y, edgecolors='none', alpha=0.025, s=60);

    In [310]:  # Y otra opción es utilizar un gráfico de R%load_ext rmagic%Rpush x y%R smoothScatter(x, y, nrpoints=0)

    The rmagic extension is already loaded. To reload it, use:  %reload_ext rmagic

  • 8/20/2019 Estadistica Con Python I

    12/30

    In [8]:  # Se puede añadir como opción un color map, por ejemplo: # cmap=plt.cm.Greys

     # cmap=plt.cm.Reds # # cmap=plt.cm.hotplt.hexbin(x, y) # El color en cada hexágono corresponde a la frecuencia   # También se puede utilizar la opción bins=n para normalizarcb=plt.colorbar()cb.set_label('frecuencias')fig = plt.gcf() # Get current figurefig.set_size_inches(10,8)

     Ahora, el mismo ejemplo utilizando figure y axis explícitamente:

  • 8/20/2019 Estadistica Con Python I

    13/30

    In [9]: fig, ax = plt.subplots()fig.set_size_inches(10,8)im = ax.hexbin(x,y)fig.colorbar(im, ax=ax);

     A continuación haremos un histograma 2d, de baja resolución para poder analizarlo

  • 8/20/2019 Estadistica Con Python I

    14/30

    In [40]: fig, ax = plt.subplots()fig.set_size_inches(10,8)hist, xedges, yedges, im = ax.hist2d(x,y, bins=(10,10),  range=[[-4.,4.], [-4., 4.]])fig.colorbar(im, ax=ax);

    Vamos a ver lo que significa cada una de las variables devueltas por hist2d:

    In [26]:  # hist es la frecuencia en cada bin, en este caso un grid de 10x10print hist.shapeprint hist

    (10, 10)[[ 0. 0. 0. 1. 2. 5. 0. 0. 0. 0.] [ 0. 0. 7. 18. 17. 19. 10. 6. 0. 0.] [ 0. 4. 26. 69. 136. 151. 61. 16. 6. 0.] [ 0. 10. 62. 234. 449. 455. 220. 67. 9. 2.] [ 2. 17. 134. 445. 845. 823. 477. 135. 27. 2.] [ 4. 19. 130. 456. 824. 824. 447. 147. 23. 2.] [ 0. 9. 93. 239. 451. 435. 253. 72. 14. 1.] [ 1. 4. 24. 90. 137. 151. 78. 20. 3. 0.] [ 1. 0. 6. 12. 13. 20. 15. 6. 0. 0.] [ 0. 0. 0. 0. 0. 3. 2. 0. 0. 0.]]

  • 8/20/2019 Estadistica Con Python I

    15/30

    In [31]:  # xbins e ybins son los límites de los intervalos semiabiertos # que definen cada binprint xbinsprint ybins

     Ahora vamos a ver como podemos representar un diagrama de contornos sobre la propia figura. Lo que

    queremos representar como tercera magnitud son las frecuencias en cada celda...

    Hay un ejemplo en: http://micropore.wordpress.com/2011/10/01/2d-density-plot-or-2d-histogram/

    In [263]: fig, ax = plt.subplots()fig.set_size_inches(10, 8)

     # Rango de valores en cada ejerango = [[-4., 4.],[-4., 4.]]

     # Generamos el histograma y el gráfico a la vez # Valores de bins elevados dan mucha fragmentación en las curvashist, xedges, yedges, im = ax.hist2d(x,y, bins=(30,30),  range=rango, cmap=plt.cm.Greys)

    fig.colorbar(im, ax=ax);

    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

     # Niveles para las curvas de nivel

    niveles = [30, 50, 70, 90]colores=['green', 'blue', 'yellow', 'red']

    cset = ax.contour(hist, niveles, linewidths=2, extent=extent, colors=colores)ax.clabel(cset, inline=1, fontsize=15, fmt='%d')

    [-4. -3.2 -2.4 -1.6 -0.8 0. 0.8 1.6 2.4 3.2 4. ][-4. -3.2 -2.4 -1.6 -0.8 0. 0.8 1.6 2.4 3.2 4. ]

  • 8/20/2019 Estadistica Con Python I

    16/30

    Pero, supongamos que solo queremos los contornos y no queremos representar la figura del histograma.

    En ese caso el histograma lo generamos con numpy

    Out[263]:

  • 8/20/2019 Estadistica Con Python I

    17/30

    In [262]: fig, ax = plt.subplots()fig.set_size_inches(8, 8)

     # Rango de valores en cada ejerango = [[-4., 4.],[-4., 4.]]

     # Generamos un histograma wD de frecuencias con numpyhist, xedges, yedges= np.histogram2d(x,y, bins=(30,30),  range=rango)

    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

     # Niveles para las curvas de nivel de las frecuenciasniveles = [30, 50, 70, 90]colores=['green', 'blue', 'yellow', 'red']cset = ax.contour(hist, niveles, linewidths=2, extent=extent, colors=colores)ax.clabel(cset, inline=1, fontsize=15, fmt='%d');

    Veamos ahora que significa normalizar los histogramas. Empecemos con el de contornos:

  • 8/20/2019 Estadistica Con Python I

    18/30

    In [200]: rango = [[-4., 4.],[-4., 4.]]hist, xedges, yedges= np.histogram2d(x,y, bins=(30,30),  range=rango)total_count = sum(hist)print total_countprint hist[14:16,14:16]

     Ahora normalizamos. El array 2D ahora contiene la densidad en cada bin, es decir, el número de

    observaciones en el bin dividido por el área del bin.

    In [229]: rango = [[-4., 4.],[-4., 4.]]hist, xedges, yedges= np.histogram2d(x,y, bins=(30,30),  range=rango)

    histn, xedges, yedges= np.histogram2d(x,y, bins=(30,30),  range=rango, normed=True)print "el resultado es:"print histn[14:16,14:16]

     # obtenido del siguiente modo:hist_suma_1 = hist/sum(hist)print '\n'area = 8. * 8. / (30*30)print hist_suma_1[14:16,14:16]/area

    De hecho, si consideramos que la matriz histn es una matriz de densidades, cumplirá:

    suma(densidad_i x area bin_i) = 64/900 x suma(densidad_i) = 64/900 * sum(histn) = 1

    En efecto:

    In [232]: 64./900 * sum(histn)

    9998.0

    [[ 111. 115.] [ 100. 111.]]

    el resultado es:[[ 0.15612497 0.1617511 ] [ 0.14065313 0.15612497]]

    [[ 0.15612497 0.1617511 ] [ 0.14065313 0.15612497]]

    Out[232]: 0.99999999999999833

  • 8/20/2019 Estadistica Con Python I

    19/30

    In [236]:  # Este es un ejemplo con valores normalizados. Las curvas dicen # Que por ejemplo la densidad es superior a 0.01 dentro de la curva

    fig, ax = plt.subplots()fig.set_size_inches(8, 8)

     # Rango de valores en cada ejerango = [[-4., 4.],[-4., 4.]]

    histn, xedges, yedges= np.histogram2d(x,y, bins=(30,30),  range=rango, normed=True)extent = [yedges[0], yedges[-1], xedges[0], xedges[-1]]

     # Niveles para las curvas de nivel de las densidadesniveles = [0.01,0.05, 0.1, 0.15]colores=['green', 'blue', 'yellow', 'red']cset = ax.contour(histn, niveles, linewidths=2, extent=extent, colors=colores)ax.clabel(cset, inline=1, fontsize=15, fmt='%.2f');

    Heatmaps

  • 8/20/2019 Estadistica Con Python I

    20/30

    En lugar de emplear la función plt.hist2d, también se puede generar un histograma 2D con

    numpy.histogram2dy, y representarlo con imshow():

    imshow() se emplea para representar imágenes. En nuestro caso la imagen será una matriz bidimensional,

    generada con histogram2d(), donde cada celda corresponde a un bin, y en ella hay un valor entero (una

    cuenta de observaciones que caen dentro del bin). De modo que imshow() lo interpreta como una imagen

    en escala de grises, aunque lo pinte en color, dependiendo del mapa de color que utilicemos. Esto tienecomo consecuencia que imshow() suaviza la imagen (los bins/pixels) no se muestran con claridad.

    In [270]:  # generamos datos de test con la distribución normal estándarx = np.random.randn(8873)y = np.random.randn(8873)heatmap, xedges, yedges = np.histogram2d(x, y, bins=50) # genera un heatmap 50x50extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]plt.imshow(heatmap, extent=extent);

    En cambio, matshow() representa la matriz sin hacer ningun suavizado. Por ello matshow es la mejor 

    opción cuando se quiere utilizar un mapa de colores para ver como se distribuyen las observaciones.

    In [273]: plt.matshow(heatmap, extent=extent);

    Clustering

  • 8/20/2019 Estadistica Con Python I

    21/30

    Agrupamiento jerárquico (hierarchical clustering)

    La idea del clustering o agrupamiento jerárquico es construir un arbol de smilaridades basado en

    distancias entre cada dos observaciones.

    Referencia a la librería scipy.cluster.hierarchy

    (http://docs.scipy.org/doc/scipy/reference/cluster.hierarchy.html#module-scipy.cluster.hierarchy)

    Referencia a la librería scipy.spatial.distance

    (http://docs.scipy.org/doc/scipy/reference/spatial.distance.html)

    In [89]:   from scipy.spatial.distance import pdist, squareformfrom scipy.cluster.hierarchy import linkage, dendrogram

    In [90]: np.random.seed(12345)

    clase1 = np.random.normal(loc=1,scale=0.2,size=(4,2))clase2 = np.random.normal(loc=2,scale=0.2,size=(4,2))clase3 = np.random.normal(loc=3,scale=0.2,size=(4,2))clases = vstack((clase1, clase2, clase3))x = clases[:,0]y = clases[:,1]plt.scatter(x,y, s=60)for i in range(12):  plt.text(x[i]-0.025, y[i]+0.1,i)

    Vamos a crear un dataframe con las 12 observaciones:

    http://docs.scipy.org/doc/scipy/reference/spatial.distance.htmlhttp://docs.scipy.org/doc/scipy/reference/cluster.hierarchy.html#module-scipy.cluster.hierarchy

  • 8/20/2019 Estadistica Con Python I

    22/30

    In [109]: df = pd.DataFrame(clases, columns=['x', 'y'])df

    La función pdist() calcula la distancia de cada uno de los 12 puntos con respecto a los demás. Se crea un

    array de valores

    In [93]: dm = pdist(df,metric='euclidean')dm.shape

     A continuación, hacemos el clustering

    In [101]: z = linkage(dm, method='complete')z.shape

    Y construimos un dendrograma

    Out[109]:x y

    0   0.959058 1.095789

    1   0.896112 0.888854

    2   1.393156 1.278681

    3   1.018582 1.056349

    4   2.153805 2.249287

    5   2.201438 1.740756

    6   2.054998 2.045783

    7   2.270583 2.177286

    8   2.599673 2.925631

    9   3.333805 2.912286

    10   2.892052 3.095397

    11   3.649789 2.795754

         (        − 1 ) / 2      

    Out[93]: (66,)

    Out[101]: (11, 4)

  • 8/20/2019 Estadistica Con Python I

    23/30

    In [102]: dendrogram(z);

    In [106]:  # Se pueden buscar otras orientacionesdendrogram(z, orientation='right');

    Por curiosidad, las distancias también se pueden poner en forma de matriz cuadrada, aunque como se ha

    visto, no es preciso para calcular el clustering:

    In [94]: distxy = squareform(dm)

    distxy.shape

    In [95]:  # Como vemos, es una matriz simétrica con 0 en la diagonaldistxy[0:3,0:3]

    k-clustering

    Out[94]: (12, 12)

    Out[95]: array([[ 0. , 0.21629657, 0.47105247],  [ 0.21629657, 0. , 0.63167861],  [ 0.47105247, 0.63167861, 0. ]])

  • 8/20/2019 Estadistica Con Python I

    24/30

    El algoritmo "k-means" toma como entrada el número de clusters a generar, k (esto es su principal

    limitación) y un conjunto de vectores resultado de observaciones (en nuestro caso los 12 pares de

    coordenadas x,y). Devuelve un conjunto de k centroides, uno por cada cluster. Las observaciones son

    clasificadas mediante el número del cluster (el index del centroide más próximo).

    Este proceso se conoce a veces como "cuantificación" de los vectores de las observaciones. Al cluster 

    index de un vector se le llama el "código" y la tabla que asocia códigos y centroides se conoce como el

    "code book"

    In [107]:   from scipy.cluster.vq import kmeans, vq

    In [127]: codebook, varianza = kmeans(df,3)

    In [128]:  # parece que a la función kmeans no le gustan los dtaframes de pandascodebook, varianza = kmeans(np.vstack(zip(x,y)),3)

    In [129]:  # sin embargo... pasandolo a array de numpy, funcionacodebook, varianza = kmeans(df.values,3)

    In [132]:  # En realidad, esto no lo necesitamos para nadaprint distortion

    ---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()

    ----> 1 codebook, varianza = kmeans(df,3)

    /usr/lib/python2.7/dist-packages/scipy/cluster/vq.pyc in kmeans(obs, k_or_guess, iter, thresh)  505 for i in range(iter):  506 #the intial code book is randomly selected from observations--> 507 guess = take(obs, randint(0, No, k), 0)  508 book, dist = _kmeans(obs, guess, thresh = thresh)  509 if dist < best_dist:

    /usr/local/lib/python2.7/dist-packages/numpy/core/fromnumeric.pyc in take(a, indices, axis, out, mode)  116 except AttributeError:  117 return _wrapit(a, 'take', indices, axis, out, mode)--> 118 return take(indices, axis, out, mode)  119

    120

    TypeError: take() takes at most 4 arguments (5 given)

    0.262435264634

  • 8/20/2019 Estadistica Con Python I

    25/30

    In [131]:  # estos son los centroides de los tres grupos:print codebook

    In [133]:  # A continuación, la función vq() asigna números de clusters (códigos d el codebook) a las observaciones:

    In [134]: code,distance = vq(df.values,codebook)

    In [136]:  # Así obtenemos el código de cada observacióncode

    In [138]: colores = ['red', 'blue', 'green']c = [colores[i] for i in code]print c

    In [139]: plt.scatter(x,y, s=60, c=c)

    Ejemplo completo de clustering

    Para este ejemplo tenemos que leer un fichero de datos en el formato binario propietario de R: RData. Por 

    eso lo mejor es leerlo con R y despues pasarlo a un dataframe de Pandas

    [[ 1.0667271 1.07991825] [ 2.17020602 2.05327779]

     [ 3.11882952 2.93226726]]

    Out[136]: array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2])

    ['red', 'red', 'red', 'red', 'blue', 'blue', 'blue', 'blue', 'green', 'green', 'green', 'green']

    Out[139]:

  • 8/20/2019 Estadistica Con Python I

    26/30

    In [148]: !ls ./datos

    In [144]: %load_ext rmagic

    In [149]: %%Rload("./datos/samsungData.rda")write.csv(samsungData,file="./datos/samsungData.csv")

    In [150]: !ls ./datos

    In [151]: samsungData = pd.read_csv('./datos/samsungData.csv')

    In [152]: samsungData.tail()

    In [153]: samsungData.shape

    In [165]:  # Nombres de las 10 primeras columnassamsungData.columns[0:10]

    In [168]:  # Nombres de las 10 últimas columnassamsungData.columns[-10:]

    In [161]: samsungData = samsungData.drop('Unnamed: 0', axis=1)

    foods-2011-10-03.json prueba prueba4.txt samsungData.rdahola prueba2.csv prueba5.txt warningsphoenix.html prueba3.csv prueba.csv

    phoenix-tidied.html prueba4.csv prueba.html

    foods-2011-10-03.json prueba prueba4.txt samsungData.csvhola prueba2.csv prueba5.txt samsungData.rdaphoenix.html prueba3.csv prueba.csv warnings

    phoenix-tidied.html prueba4.csv prueba.html

    Out[152]:

    Int64Index: 5 entries, 7347 to 7351

    Columns: 564 entries, Unnamed: 0 to activity

    dtypes: float64(561), int64(2), object(1)

    Out[153]: (7352, 564)

    Out[165]: Index([u'tBodyAcc-mean()-X', u'tBodyAcc-mean()-Y', u'tBodyAcc-mean()-Z', u'tBodyAcc-std()-X', u'tBodyAcc-std()-Y', u'tBodyAcc-std()-Z', u'tBodyA

    cc-mad()-X', u'tBodyAcc-mad()-Y', u'tBodyAcc-mad()-Z', u'tBodyAcc-max()-X'], dtype=object)

    Out[168]: Index([u'fBodyBodyGyroJerkMag-kurtosis()', u'angle(tBodyAccMean,gravity)', u'angle(tBodyAccJerkMean),gravityMean)', u'angle(tBodyGyroMean,gravityMean)', u'angle(tBodyGyroJerkMean,gravityMean)', u'angle(X,gravityMean)', u'angle(Y,gravityMean)', u'angle(Z,gravityMean)', u'subject', u'activity'], dtype=object)

  • 8/20/2019 Estadistica Con Python I

    27/30

    In [162]: samsungData.columns[0:10]

    In [163]: samsungData['activity'].value_counts()

    In [178]:  # para hacernos una idea de como viene codificado el dataframe # vamos a listar un subconjunto:samsungData.ix[985:995,[0,1,2,3,4,5,-2,-1]]

    Hay una línea por sujeto y lectura de los accelerómetros, y a cada vector de observaciones se asigna una

    actividad. A continuación vamos a ir probando variables del sujeto 1, y viendo si estas variables discriminan

    bien entre actividades:

    In [205]:  # Array de actividadesacts = samsungData['activity'].unique() # secuencia de colorescols = 'bgrcmy'

    Out[162]: Index([u'tBodyAcc-mean()-X', u'tBodyAcc-mean()-Y', u'tBodyAcc-mean()-Z',

     u'tBodyAcc-std()-X', u'tBodyAcc-std()-Y', u'tBodyAcc-std()-Z', u'tBodyAcc-mad()-X', u'tBodyAcc-mad()-Y', u'tBodyAcc-mad()-Z', u'tBodyAcc-max()-X'], dtype=object)

    Out[163]: laying 1407standing 1374sitting 1286walk 1226walkup 1073walkdown 986dtype: int64

    Out[178]:tBodyAcc-

    mean()-X

    tBodyAcc-

    mean()-Y

    tBodyAcc-

    mean()-Z

    tBodyAcc-

    std()-X

    tBodyAcc-

    std()-Y

    tBodyAcc-

    std()-Z  subject

    985   0.198992 -0.002455 -0.117281 0.100775 0.355080 -0.266647 5

    986   0.158101 -0.040474 -0.134750 0.063741 0.265524 -0.299606 5

    987   0.281287 -0.034803 -0.089352 -0.064575 0.314012 -0.280909 5

    988   0.418010 -0.016577 -0.153921 -0.047141 0.283657 -0.178543 5

    989   0.428925 -0.037568 -0.169470 -0.033747 0.300829 -0.229894 5

    990   0.292996 -0.036746 -0.111782 -0.953571 -0.863929 -0.870786 6

    991   0.276552 -0.028512 -0.110449 -0.987560 -0.945003 -0.944290 6

    992   0.271818 -0.032274 -0.113994 -0.995988 -0.959353 -0.955563 6

    993   0.275229 -0.010966 -0.089999 -0.995814 -0.958768 -0.976571 6

    994   0.279222 -0.005795 -0.092436 -0.996173 -0.969167 -0.980864 6

    995   0.276892 -0.018711 -0.109727 -0.994897 -0.972814 -0.963744 6

  • 8/20/2019 Estadistica Con Python I

    28/30

    In [206]:  # Crear un diccionario de coloresdic_col = {acts[i]:cols[i] for i in range(len(acts))}dic_col

    In [207]:  # Seleccionamos las filas del primer sujetosubj1 = samsungData[samsungData['subject']==1]

     # Ahora creamos un objeto "groupby" para agrupar por actividad grouped = subj1.groupby('activity')

    In [212]:  #En abcisas vamos a representar los valores de la primera variable # Y en ordenadas el número de la observación (la lectura)

     # Siempre referido al sujeto 1

    fig, (ax1, ax2) = plt.subplots(1,2, sharey=True)fig.set_size_inches(10, 5)

    for act, df in grouped:  ax1.scatter(df.ix[:,0], df.index, c=dic_col[act], label=act)  ax2.scatter(df.ix[:,1], df.index, c=dic_col[act], label=act)  ax1.set_xlabel(samsungData.columns[0])  ax1.set_ylabel(u'# Observación')  ax2.set_xlabel(samsungData.columns[1])

      ax2.legend(loc='upper left')

    Conclusión: las dos primeras variables no nos permiten discriminar por tipos de actividad

     Ahora vamos a probar a hacer un agrupamiento jerárquico basado en las tres primeras columnas, a ver si

    esto nos permite separar por grupos con un tipo de actividad

    Out[206]: {'laying': 'r',

     'sitting': 'g', 'standing': 'b', 'walk': 'c', 'walkdown': 'm',

     'walkup': 'y'}

  • 8/20/2019 Estadistica Con Python I

    29/30

  • 8/20/2019 Estadistica Con Python I

    30/30

    In [245]:  #En abcisas vamos a representar los valores de la primera variable # Y en ordenadas el número de la observación (la lectura) # Siempre referido al sujeto 1

    fig, (ax1, ax2) = plt.subplots(1,2, sharey=True)fig.set_size_inches(10, 5)

    for act, df in grouped:

      ax1.scatter(df.ix[:,9], df.index, c=dic_col[act], label=act)  ax2.scatter(df.ix[:,10], df.index, c=dic_col[act], label=act)  ax1.set_xlabel(samsungData.columns[9])  ax1.set_ylabel(u'# Observación')  ax2.set_xlabel(samsungData.columns[10])  ax2.legend(loc='upper left')

    Continuaremos este ejemplo más adelante una vez hayamos visto el análisis de componentes principales

    (PCA)

    In []: