Pybonacci

Computación Científica con Python en castellano

Curso de Python en la UPM: una oportunidad para el software libre

with 2 comments

Durante tres semanas del mes de marzo de 2014 dos alumnos de la ETSI Aeronáuticos nos lanzamos a impartir un curso de Python para alumnos de Ingeniería Aeronáutica de los tres planes que aún conviven (dos en extinción y el nuevo Grado), es decir, para nuestros propios compañeros.

A lo largo de las 18 horas que duró el curso nuestros alumnos no solo aprendieron la sintaxis básica del lenguaje, sino que también utilizaron varias bibliotecas ubicuas en el ámbito científico (NumPy, matplotlib, SciPy) y las aplicaron para resolver problemas típicos de la carrera: visualización del perfil de Yukovski, resolución de la ecuación de Kepler… por mencionar algunos.

Como más tarde explicaremos el curso fue un éxito y los alumnos no solo terminaron muy contentos: han seguido usando el lenguaje e incluso explorando sus posibilidades en campos totalmente distintos como el desarrollo web o las finanzas. Pero ¿cuál era la motivación para nosotros?

Así empieza el artículo que hemos escrito en el blog Software Libre en la UPM acerca del curso de Python en la ETSI Aeronáutica y del Espacio que impartimos mi amigo Álex y yo el pasado marzo. Espero que os guste :)

http://blogs.upm.es/softwarelibre/2014/07/14/curso-de-python-en-la-upm-una-oportunidad-para-el-software-libre/

¡Un saludo!

Written by Juanlu001

14 de julio de 2014 at 19:21

Publicado en Artículos

MicroEntradas: scipy.constants

with 2 comments

¿No te acuerdas de la constante de gravitación universal?, ¿no sabes cuanta área es un acre?, ¿por qué me pasan esos resultados en pulgadas?, ¿a qué altura estamos volando cuando me dicen que estamos a 10.000 pies?,… Puedes responder a todo eso usando el, creo que infrautilizado, módulo constants dentro del paquete scipy.

Primero de todo, vamos a importat el módulo en cuestión:


from scipy import constants as constantes

En este módulo disponemos de varias constantes físicas y matemáticas de uso muy habitual en determinados campos. El número pi, la constante de gravitación universal, la constante de Plank o la masa del electrón están en la punta de tus dedos.


print(constantes.pi, 
      constantes.gravitational_constant, 
      constantes.Plank, 
      constantes.m_e)

Nos dará el siguiente resultado:


3.141592653589793 6.67384e-11 6.62606957e-34 9.10938291e-31

Podemos acceder a otras constantes (no tan constantes) usando un diccionario con el nombre de la constante según la base de datos del Committee on Data for Science and Technology (CODATA):


constantes.value('standard atmosphere')

Podemos obtener el valor de varias unidades en el sistema internacional simplemente poniendo su nombre


print(constantes.foot, constantes.inch)

Incluso tenemos funciones para hacer conversiones de unidades


print(constantes.C2K(10))

Nos daría el valor de 10ºC en grados Kelvin.

Saludos.

 

Written by Kiko

13 de julio de 2014 at 11:23

Tutorial de OpenLayers usando IPython, Brython y brythonmagic

with 2 comments

No, no habéis leído mal, hoy vamos a hablar de OpenLayers, una librería javascript para hacer ‘mapping’ en el cliente (navegador).

Como sabéis, hablamos principalmente de Python porque nos gusta y porque nos divierte y este tutorial de una librería javascript lo vamos a realizar usando una sintaxis pythónica y, mientras aprendemos el uso básico de OpenLayers, también veremos algo de sintaxis javascript.

Para poder seguir este tutorial necesitaréis tener una versión moderna de IPython notebook instalada e instalar brythonmagic del que ya hablamos por aquí hace poco. El tutorial lo podéis descargar desde aquí.

Finalmente, quería agradecer a Roger Veciana por ofrecerse a revisar el tutorial y aportar mejoras importantes al mismo. Gracias monstruo.

Saludos.

P.D.: El tutorial está en inglés, si alguien no es capaz de seguirlo que avise por aquí y si el tiempo lo permite intentaré hacer una versión en la lengua de Cervantes.

Written by Kiko

11 de junio de 2014 at 17:31

Publicado en Artículos, Tutoriales

Tagged with , , , , ,

Pandas (VI)

leave a comment »

Y mucho más

Esto solo ha sido un pequeño vistazo con cosas que considero importantes pero que no tienen que ser las más importantes. Podéis echarle un ojo a:

  • sort, max, min, head, tail, unique, groupby, apply, transform, stack, unstack, mean, std, isnull, value_counts, notnull, rank, dropna, fillna, describe, cov, corr, duplicated, drop, pivot, pivot_table, drop_duplicates, quantile,…

para seguir viendo cosas útiles.

Finalmente, después de haceros sufrir con el formateo del código dentro del wordpress os he dejado un notebook en el github de Pybonacci donde tenéis todo lo que hemos visto en esta serie además de un pequeño caso práctico de aplicación.

Written by Kiko

10 de junio de 2014 at 6:00

Pandas (V)

leave a comment »

Antes de nada, el contexto, para esta serie de entradas se va a usar lo siguiente:

Versión de Python:      3.3.1 (default, Apr 10 2013, 19:05:32) 
[GCC 4.6.3]
Versión de Pandas:      0.13.1
Versión de Numpy:       1.8.1
Versión de Matplotlib:  1.3.1

 

Y sin más preámbulos seguimos con esta quinta parte de la serie.

Unir (merge/join)

Pandas dispone de la función merge (documentación oficial) que permite ‘unir’ datos al estilo de como se hace con bases de datos relacionales (usando SQL). También se puede acceder al método merge disponible en las instancias a un Dataframe.

Por su parte, join es un método disponible en un DataFrame y sirve para hacer uniones de índices sobre índices o de índices sobre columnas. Las uniones que hace join las hace sobre los índices, en lugar de hacerlo sobre columnas comunes como se hace con merge. A ver si viendo los ejemplos queda un poco mejor este último párrafo y las diferencias entre join y merge.

Las uniones pueden ser uno-a-uno, muchos-a-uno o muchos-a-muchos.

Una unión uno-a-uno sería cuando unimos dos tablas (DataFrames) con índices únicos como hemos hecho en la entrega anterior con las concatenaciones.


datos1 = pd.DataFrame(np.random.randn(10), columns = ['columna1'])
datos2 = pd.DataFrame(np.random.randn(14), columns = ['columna2'], index = np.arange(1,15))
datos1j = datos1.join(datos2)
datos2j = datos2.join(datos1)
print('datos1j \n{}\n'.format(datos1j))
print('datos2j \n{}'.format(datos2j))

datos1j 
   columna1  columna2
0 -0.209303       NaN
1 -0.430892  1.052453
2  0.766200 -0.346896
3  1.773694 -0.249700
4 -2.259187 -0.588739
5 -0.930647  0.160590
6  0.029990  0.421446
7  0.812770 -0.315913
8  0.681786  0.256745
9 -0.115109  0.524278

[10 rows x 2 columns]

datos2j 
    columna2  columna1
1   1.052453 -0.430892
2  -0.346896  0.766200
3  -0.249700  1.773694
4  -0.588739 -2.259187
5   0.160590 -0.930647
6   0.421446  0.029990
7  -0.315913  0.812770
8   0.256745  0.681786
9   0.524278 -0.115109
10 -1.707269       NaN
11 -1.140342       NaN
12 -1.751337       NaN
13 -0.481319       NaN
14  1.604800       NaN

[14 rows x 2 columns]

 

En los anteriores ejemplos, datos1j es el resultado de unir los datos datos2 a los datos datos1 en todos los índices comunes que tienen ambos teniendo solo en cuenta el rango de índices definido en datos1. Si algún dato en datos2 no tiene un índice presente en datos1 se rellenará con un NaN. Con datos2j sucede lo mismo que con datos1j lo que el índice que tiene relevancia ahora es el perteneciente a datos2j. No sé si habrá quedado más o menos claro.

Ahora vamos a unir pero usando la palabra clave how que nos permite decir como se van a tener en cuenta los índices. Normalmente le pasaremos el parámetro outer o inner. El primero, outer, indica que los índices de los DataFrames se unen como en una unión de conjuntos, el segundo, inner, une los índices como si hiciéramos una intersección de conjuntos. Veamos un par de ejemplos para que se vea de forma práctica, el primero usando outer y el segundo usando inner:


datos3j1 = datos1.join(datos2, how = 'outer')
datos3j2 = datos2.join(datos1, how = 'outer')
print('datos3j1 \n{}\n'.format(datos3j1))
print('datos3j2 recolocados\n{}\n'.format(datos3j2.ix[:, ['columna1','columna2']]))
print('datos3j2 \n{}'.format(datos3j2))

datos3j1 
    columna1  columna2
0  -0.209303       NaN
1  -0.430892  1.052453
2   0.766200 -0.346896
3   1.773694 -0.249700
4  -2.259187 -0.588739
5  -0.930647  0.160590
6   0.029990  0.421446
7   0.812770 -0.315913
8   0.681786  0.256745
9  -0.115109  0.524278
10       NaN -1.707269
11       NaN -1.140342
12       NaN -1.751337
13       NaN -0.481319
14       NaN  1.604800

[15 rows x 2 columns]

datos3j2 recolocados
    columna1  columna2
0  -0.209303       NaN
1  -0.430892  1.052453
2   0.766200 -0.346896
3   1.773694 -0.249700
4  -2.259187 -0.588739
5  -0.930647  0.160590
6   0.029990  0.421446
7   0.812770 -0.315913
8   0.681786  0.256745
9  -0.115109  0.524278
10       NaN -1.707269
11       NaN -1.140342
12       NaN -1.751337
13       NaN -0.481319
14       NaN  1.604800

[15 rows x 2 columns]

datos3j2 
    columna2  columna1
0        NaN -0.209303
1   1.052453 -0.430892
2  -0.346896  0.766200
3  -0.249700  1.773694
4  -0.588739 -2.259187
5   0.160590 -0.930647
6   0.421446  0.029990
7  -0.315913  0.812770
8   0.256745  0.681786
9   0.524278 -0.115109
10 -1.707269       NaN
11 -1.140342       NaN
12 -1.751337       NaN
13 -0.481319       NaN
14  1.604800       NaN

[15 rows x 2 columns]

datos4j1 = datos1.join(datos2, how = 'inner')
datos4j2 = datos2.join(datos1, how = 'inner')
print('datos4j1 \n{}\n'.format(datos4j1))
print('datos4j2 recolocados\n{}\n'.format(datos4j2.ix[:, ['columna1','columna2']]))
print('datos4j2 \n{}'.format(datos4j2))

datos4j1 
   columna1  columna2
1 -0.430892  1.052453
2  0.766200 -0.346896
3  1.773694 -0.249700
4 -2.259187 -0.588739
5 -0.930647  0.160590
6  0.029990  0.421446
7  0.812770 -0.315913
8  0.681786  0.256745
9 -0.115109  0.524278

[9 rows x 2 columns]

datos4j2 recolocados
   columna1  columna2
1 -0.430892  1.052453
2  0.766200 -0.346896
3  1.773694 -0.249700
4 -2.259187 -0.588739
5 -0.930647  0.160590
6  0.029990  0.421446
7  0.812770 -0.315913
8  0.681786  0.256745
9 -0.115109  0.524278

[9 rows x 2 columns]

datos4j2 
   columna2  columna1
1  1.052453 -0.430892
2 -0.346896  0.766200
3 -0.249700  1.773694
4 -0.588739 -2.259187
5  0.160590 -0.930647
6  0.421446  0.029990
7 -0.315913  0.812770
8  0.256745  0.681786
9  0.524278 -0.115109

[9 rows x 2 columns]

 

Todo lo anterior se puede hacer también usando la función o método merge pero encuentro que es una forma un poco más rebuscada por lo que no la vamos a mostrar aquí ya que añade complejidad. Veremos usos de merge más adelante.

Ahora vamos a mostrar una unión muchos-a-uno. Estas uniones se hacen sobre una o más columnas como referencia, no a partir de índices, por lo que los valores contenidos pueden no ser únicos. Como siempre, vamos a ver un poco de código para ver si clarifica un poco más la teoría:


datos1 = pd.DataFrame(np.random.randn(10), columns = ['columna1'])
datos1['otra_columna'] = ['hola', 'mundo'] * 5
datos2 = pd.DataFrame(np.random.randn(2,2), columns = ['col1', 'col2'], index = ['hola', 'mundo'])
print('datos1 \n {} \n'.format(datos1))
print('datos2 \n {} \n'.format(datos2))
print(u'Unión de datos \n {} \n'.format(datos1.join(datos2, on = 'otra_columna')))

datos1 
    columna1 otra_columna
0 -2.086230         hola
1 -1.015736        mundo
2 -0.919460         hola
3  0.923531        mundo
4 -0.445977         hola
5  0.719787        mundo
6  1.064480         hola
7 -0.235803        mundo
8  1.395844         hola
9  1.492875        mundo

[10 rows x 2 columns] 

datos2 
            col1      col2
hola   0.400267 -0.678126
mundo  0.855735  0.619193

[2 rows x 2 columns] 

Unión de datos 
    columna1 otra_columna      col1      col2
0 -2.086230         hola  0.400267 -0.678126
1 -1.015736        mundo  0.855735  0.619193
2 -0.919460         hola  0.400267 -0.678126
3  0.923531        mundo  0.855735  0.619193
4 -0.445977         hola  0.400267 -0.678126
5  0.719787        mundo  0.855735  0.619193
6  1.064480         hola  0.400267 -0.678126
7 -0.235803        mundo  0.855735  0.619193
8  1.395844         hola  0.400267 -0.678126
9  1.492875        mundo  0.855735  0.619193

[10 rows x 4 columns]

 

Estamos uniendo sobre los valores de la columna del DataFrame datos1 que presenta valores presentes en los índices del DataFrame datos2. En el anterior ejemplo hemos unido teniendo en cuenta una única columna, si queremos unir teniendo en cuenta varias columnas, el DataFrame que se le pase deberá presentar un MultiÍndice con tantos índices como columnas usemos (ver documentación sobre MultiÍndices y sobre unión con ellos).

Para hacer uniones de muchos-a-muchos usaremos merge que ofrece mayor libertad para poder hacer uniones de cualquier tipo (también las que hemos visto hasta ahora de uno-a-uno y de muchos-a-uno).

En el siguiente ejemplo vamos a hacer una unión de dos DataFrames usando merge y luego iremos explicando lo que hemos estado haciendo poco a poco para ver si se entiende un poco mejor.


datos_dcha = pd.DataFrame({'clave': ['foo'] * 3, 'valor_dcha': np.arange(3)})
datos_izda = pd.DataFrame({'clave': ['foo'] * 3, 'valor_izda': np.arange(5, 8)})
datos_unidos = pd.merge(datos_izda, datos_dcha, on = 'clave')
print('datos_dcha \n {} \n'.format(datos_dcha))
print('datos_izda \n {} \n'.format(datos_izda))
print('datos_unidos \n {}'.format(datos_unidos))

datos_dcha 
   clave  valor_dcha
0   foo           0
1   foo           1
2   foo           2

[3 rows x 2 columns] 

datos_izda 
   clave  valor_izda
0   foo           5
1   foo           6
2   foo           7

[3 rows x 2 columns] 

datos_unidos 
   clave  valor_izda  valor_dcha
0   foo           5           0
1   foo           5           1
2   foo           5           2
3   foo           6           0
4   foo           6           1
5   foo           6           2
6   foo           7           0
7   foo           7           1
8   foo           7           2

[9 rows x 3 columns]

 

Vemos que si hacemos una unión de la anterior forma, a cada valor de datos_dcha le ‘asocia’ cada uno de los valores de datos_izda que tengan la misma clave. En la siquiente celda de código vemos otro ejemplo de lo anterior un poco más completo teniendo en cuenta dos columnas de claves y usando el método outer de ‘unión’:


datos_dcha = pd.DataFrame({'clave1': ['foo', 'foo', 'bar', 'bar'],
                           'clave2': ['one', 'one', 'one', 'two'],
                           'val_dcha': [4, 5, 6, 7]})
datos_izda = pd.DataFrame({'clave1': ['foo', 'foo', 'bar'],
                           'clave2': ['one', 'two', 'one'],
                           'val_izda': [1, 2, 3]})
datos_unidos = pd.merge(datos_izda, datos_dcha, how='outer')
print('datos_dcha \n {} \n'.format(datos_dcha))
print('datos_izda \n {} \n'.format(datos_izda))
print('datos_unidos \n {}'.format(datos_unidos))

datos_dcha 
   clave1 clave2  val_dcha
0    foo    one         4
1    foo    one         5
2    bar    one         6
3    bar    two         7

[4 rows x 3 columns] 

datos_izda 
   clave1 clave2  val_izda
0    foo    one         1
1    foo    two         2
2    bar    one         3

[3 rows x 3 columns] 

datos_unidos 
   clave1 clave2  val_izda  val_dcha
0    foo    one         1         4
1    foo    one         1         5
2    foo    two         2       NaN
3    bar    one         3         6
4    bar    two       NaN         7

[5 rows x 4 columns]

 

Otra vez hemos llegado al final. ¡¡Estad atentos a la última entrega!!

Written by Kiko

8 de junio de 2014 at 6:00

Pandas (IV)

leave a comment »

Antes de nada, el contexto, para esta serie de entradas se va a usar lo siguiente:

Versión de Python:      3.3.1 (default, Apr 10 2013, 19:05:32) 
[GCC 4.6.3]
Versión de Pandas:      0.13.1
Versión de Numpy:       1.8.1
Versión de Matplotlib:  1.3.1

 

Y sin más preámbulos seguimos con esta cuarta parte de la serie.

Concatenando datos

Para concatenar ficheros se usa la función pd.concat (documentación oficial]. Un ejemplo rápido sería el siguiente:


datos1 = pd.DataFrame(np.random.randn(5,3))
datos2 = pd.DataFrame(np.random.randn(5,3))
piezas = [datos1, datos2]
datos_concatenados_a = pd.concat(piezas)
print('datos1\n {}'.format(datos1))
print('datos2\n {}'.format(datos2))
print('datos_concatenados\n {}'.format(datos_concatenados_a))

Cuyo resultado sería:

datos1
          0         1         2
0 -1.691985 -1.181241 -0.714437
1  0.955094 -0.238498  1.137918
2 -0.533739 -0.285976 -0.990184
3 -0.626446  0.664830  0.278803
4 -0.183818 -0.013190  0.505786

[5 rows x 3 columns]
datos2
          0         1         2
0 -2.063044  2.328388  0.043275
1 -1.720170 -0.039871  0.954244
2 -0.173751  0.047003 -0.979577
3 -0.293044  1.928332 -1.323554
4  0.705127  3.711652 -0.535096

[5 rows x 3 columns]
datos_concatenados
          0         1         2
0 -1.691985 -1.181241 -0.714437
1  0.955094 -0.238498  1.137918
2 -0.533739 -0.285976 -0.990184
3 -0.626446  0.664830  0.278803
4 -0.183818 -0.013190  0.505786
0 -2.063044  2.328388  0.043275
1 -1.720170 -0.039871  0.954244
2 -0.173751  0.047003 -0.979577
3 -0.293044  1.928332 -1.323554
4  0.705127  3.711652 -0.535096

[10 rows x 3 columns]

Interesante, rápido y limpio, como me gusta. Pero, si nos fijamos, tenemos un problema con los índices ya que algunos están repetidos. Si accedemos al índice 0, por ejemplo, obtendríamos dos filas de valores en lugar de una.


datos_concatenados_a.ix[0]

              0            1            2
0     -1.691985    -1.181241    -0.714437
0     -2.063044     2.328388     0.043275

2 rows × 3 columns

Lo anterior podría llevar a equívocos. Esto lo podemos solventar de varias formas. Una sería reescribiendo la columna de índices para que no haya malentendidos al hacer cualquier operación. Por ejemplo:


datos_concatenados_aa = datos_concatenados_a
datos_concatenados_aa.index = range(datos_concatenados_aa.shape[0])
print('datos_concatenados\n {}'.format(datos_concatenados_aa))

datos_concatenados
          0         1         2
0 -1.691985 -1.181241 -0.714437
1  0.955094 -0.238498  1.137918
2 -0.533739 -0.285976 -0.990184
3 -0.626446  0.664830  0.278803
4 -0.183818 -0.013190  0.505786
5 -2.063044  2.328388  0.043275
6 -1.720170 -0.039871  0.954244
7 -0.173751  0.047003 -0.979577
8 -0.293044  1.928332 -1.323554
9  0.705127  3.711652 -0.535096

[10 rows x 3 columns]

O usando la palabra clave ignore_index pasándole el valor True al crear la concatenación. Por ejemplo:


datos_concatenados_aa = pd.concat(piezas, ignore_index = True)
print(datos_concatenados_aa)

          0         1         2
0 -1.691985 -1.181241 -0.714437
1  0.955094 -0.238498  1.137918
2 -0.533739 -0.285976 -0.990184
3 -0.626446  0.664830  0.278803
4 -0.183818 -0.013190  0.505786
5 -2.063044  2.328388  0.043275
6 -1.720170 -0.039871  0.954244
7 -0.173751  0.047003 -0.979577
8 -0.293044  1.928332 -1.323554
9  0.705127  3.711652 -0.535096

[10 rows x 3 columns]

Vale, hemos solventado el anterior problema pero que pasa si, por la razón que sea, nos interesase conservar los índices originales. Podríamos usar palabras clave para cada ‘cosa’ concatenada en el DataFrame final. Ejemplo:


#datos1 = pd.DataFrame(np.random.randn(5,3))
#datos2 = pd.DataFrame(np.random.randn(5,3))
#piezas = [datos1, datos2]
datos_concatenados_b = pd.concat(piezas, keys = ['datos1', 'datos2'])
print('datos1\n {}'.format(datos1))
print('datos2\n {}'.format(datos2))
print('datos_concatenados\n {}'.format(datos_concatenados_b))

datos1
          0         1         2
0 -1.691985 -1.181241 -0.714437
1  0.955094 -0.238498  1.137918
2 -0.533739 -0.285976 -0.990184
3 -0.626446  0.664830  0.278803
4 -0.183818 -0.013190  0.505786

[5 rows x 3 columns]
datos2
          0         1         2
0 -2.063044  2.328388  0.043275
1 -1.720170 -0.039871  0.954244
2 -0.173751  0.047003 -0.979577
3 -0.293044  1.928332 -1.323554
4  0.705127  3.711652 -0.535096

[5 rows x 3 columns]
datos_concatenados
                 0         1         2
datos1 0 -1.691985 -1.181241 -0.714437
       1  0.955094 -0.238498  1.137918
       2 -0.533739 -0.285976 -0.990184
       3 -0.626446  0.664830  0.278803
       4 -0.183818 -0.013190  0.505786
datos2 0 -2.063044  2.328388  0.043275
       1 -1.720170 -0.039871  0.954244
       2 -0.173751  0.047003 -0.979577
       3 -0.293044  1.928332 -1.323554
       4  0.705127  3.711652 -0.535096

[10 rows x 3 columns]

Vemos que hay índices repetidos pero están en ‘grupos’ diferentes. De esta forma, si queremos acceder a la fila con índice 0 del primer grupo de datos concatenados (datos1) podemos hacer lo siguiente:


print(datos_concatenados_b.ix['datos1'].ix[0])

<pre>0   -1.691985
1   -1.181241
2   -0.714437
Name: 0, dtype: float64</pre>

Estamos viendo filas, pero podemos hacer los mismo para las columnas, por supuesto, usando el nombre de la columna (en el ejemplo siguiente, la columna 0):

1

print(datos_concatenados_b.ix['datos1'][0])

0   -1.691985
1    0.955094
2   -0.533739
3   -0.626446
4   -0.183818
Name: 0, dtype: float64

Vemos qué tipo de índice es este índice 'compuesto' que hemos creado:


datos_concatenados_b.index

MultiIndex(levels=[['datos1', 'datos2'], [0, 1, 2, 3, 4]],
labels=[[0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]])

Vemos que es un MultiIndex. No vamos a ver mucho más pero os lo dejo anotado para que sepáis que existen combinaciones de índices (o de columnas) y se manejan de forma un poco más compleja que un índice 'simple'. Se conoce como indexación jerárquica y permiten ser un poco más descriptivos (verbose) con nuestros DataFrames aunque conlleva un punto más de complejidad a la hora de trabajar con los datos.

¿Qué pasa cuando una de las columnas no es igual en los grupos de datos que queramos concatenar? El nuevo DataFrame tendrá en cuenta este aspecto rellenando con NaNs donde convenga. Veamos el siguiente código de ejemplo:


datos1 = pd.DataFrame(np.random.randn(5,3))
datos2 = pd.DataFrame(np.random.randn(5,4))
piezas = [datos1, datos2]
datos_concatenados_c = pd.concat(piezas, ignore_index = True)
print('datos1\n {}'.format(datos1))
print('datos2\n {}'.format(datos2))
print('datos_concatenados\n {}'.format(datos_concatenados_c))

datos1
          0         1         2
0 -0.082729 -0.016452 -1.280156
1  0.606336 -0.504770 -2.017690
2 -2.147009 -0.632275  0.023689
3 -0.255461 -0.042007  0.661835
4  2.351576  0.735611 -0.187072

[5 rows x 3 columns]
datos2
          0         1         2         3
0 -0.223023  0.070622 -0.577119 -1.430177
1 -1.661289 -0.214221  0.709818 -0.642611
2 -0.098368 -0.489105 -1.373906 -2.104431
3  0.880578 -0.601151 -1.450542 -0.289738
4 -1.461346 -0.539262  0.327825 -0.944431

[5 rows x 4 columns]
datos_concatenados
          0         1         2         3
0 -0.082729 -0.016452 -1.280156       NaN
1  0.606336 -0.504770 -2.017690       NaN
2 -2.147009 -0.632275  0.023689       NaN
3 -0.255461 -0.042007  0.661835       NaN
4  2.351576  0.735611 -0.187072       NaN
5 -0.223023  0.070622 -0.577119 -1.430177
6 -1.661289 -0.214221  0.709818 -0.642611
7 -0.098368 -0.489105 -1.373906 -2.104431
8  0.880578 -0.601151 -1.450542 -0.289738
9 -1.461346 -0.539262  0.327825 -0.944431

[10 rows x 4 columns]

Vemos que el primer grupo de datos, datos1, solo tiene tres columnas mientras que el segundo grupo, datos2, tiene 4 columnas. El resultado final tendrá en cuenta esto y rellenerá la columna 3 que pertenece a los datos del primer grupo de datos, datos1. Cool!

Lo visto hasta ahora para concatenar Series o DataFrames lo podemos hacer también usando el método append. Veamos un ejemplo similar a lo anterior:


datos1 = pd.DataFrame(np.random.randn(5,3))
datos2 = pd.DataFrame(np.random.randn(5,4))
datos_concatenados_d = datos1.append(datos2, ignore_index = True)
print('datos1\n {}'.format(datos1))
print('datos2\n {}'.format(datos2))
print('datos_concatenados\n {}'.format(datos_concatenados_d))

datos1
          0         1         2
0 -0.974367  1.732370  0.354479
1 -0.021746  2.215287  1.107243
2  0.018506  1.301015  1.103651
3 -1.857281 -1.181981  0.097104
4 -0.595689  0.140885  1.993213

[5 rows x 3 columns]
datos2
          0         1         2         3
0 -0.211180 -0.093403  0.215210 -0.154284
1  0.206997  1.277379 -0.893895 -0.216731
2 -1.138390 -0.067240  1.688928 -2.191215
3  0.938069  0.174496 -1.722735 -0.873746
4  0.177425  0.823896 -0.595673 -0.426416

[5 rows x 4 columns]
datos_concatenados
          0         1         2         3
0 -0.974367  1.732370  0.354479       NaN
1 -0.021746  2.215287  1.107243       NaN
2  0.018506  1.301015  1.103651       NaN
3 -1.857281 -1.181981  0.097104       NaN
4 -0.595689  0.140885  1.993213       NaN
5 -0.211180 -0.093403  0.215210 -0.154284
6  0.206997  1.277379 -0.893895 -0.216731
7 -1.138390 -0.067240  1.688928 -2.191215
8  0.938069  0.174496 -1.722735 -0.873746
9  0.177425  0.823896 -0.595673 -0.426416

[10 rows x 4 columns]

Otra vez hemos llegado al final. ¡¡Estad atentos a la próxima entrega!!

Written by Kiko

6 de junio de 2014 at 6:00

Pandas (III)

with one comment

Antes de nada, el contexto, para esta serie de entradas se va a usar lo siguiente:

Versión de Python:      3.3.1 (default, Apr 10 2013, 19:05:32) 
[GCC 4.6.3]
Versión de Pandas:      0.13.1
Versión de Numpy:       1.8.1
Versión de Matplotlib:  1.3.1

 

Y sin más preámbulos seguimos con esta tercera parte de la serie.

Trabajando con datos, indexación, selección,…

¿Cómo podemos seleccionar, añadir, eliminar, mover,…, columnas, filas,…?

Para seleccionar una columna solo hemos de usar el nombre de la columna y pasarlo como si fuera un diccionario (o un atributo).

Para añadir una columna simplemente hemos de usar un nombre de columna no existente y pasarle los valores para esa columna.

Para eliminar una columna podemos usar del o el método pop del DataFrame.

Para mover una columna podemos usar una combinación de las metodologías anteriores.

Por ejemplo, vemos a seleccionar los valores de una columna:


df = pd.DataFrame(np.random.randn(5,3),
                       index = ['primero','segundo','tercero','cuarto','quinto'],
                       columns = ['velocidad', 'temperatura','presion'])
print(df['velocidad'])
print(df.velocidad)

Hemos creado un DataFrame y para acceder a la columna velocidad lo podemos hacer de dos formas. O bien usando el nombre de la columna como si fuera una clave de un diccionario o bien usando el nombre de la columna como si fuera un atributo. En el caso de que los nombres de las columnas sean números, la segunda opción no podríais usarla…

Vamos a añadir una columna nueva al DataFrame. Es algo tan sencillo como usar un nombre de columna no existente y pasarle los datos:


df['velocidad_maxima'] = np.random.randn(df.shape[0])
print(df)

Y el resultado sería:

         velocidad  temperatura   presion  velocidad_maxima
primero   0.175374     0.384571 -0.575126         -0.474630
segundo  -0.133466     0.987833  0.305844         -0.746577
tercero  -0.418224     0.603431  0.128822          1.545612
cuarto   -0.320517    -0.643183  0.319838          0.634203
quinto    0.955521    -0.295541 -1.277743          2.389485

[5 rows x 4 columns]

 

Pero qué pasa si quiero añadir la columna en un lugar específico. Para ello podemos usar el método insert (y de paso vemos como podemos borrar una columna):


# forma 1 (borramos la columna 'velocidad_maxima' que está al final del df usando del)
#         (Colocamos la columna eliminada en la posición que especifiquemos)
print(df)
columna = df['velocidad_maxima']
del df['velocidad_maxima']
print(df)
print(columna)
df.insert(1, 'velocidad_maxima', columna)
print(df)

El resultado del DataFrame paso a paso sería:

         velocidad  temperatura   presion  velocidad_maxima
primero   0.175374     0.384571 -0.575126         -0.474630
segundo  -0.133466     0.987833  0.305844         -0.746577
tercero  -0.418224     0.603431  0.128822          1.545612
cuarto   -0.320517    -0.643183  0.319838          0.634203
quinto    0.955521    -0.295541 -1.277743          2.389485

[5 rows x 4 columns]
         velocidad  temperatura   presion
primero   0.175374     0.384571 -0.575126
segundo  -0.133466     0.987833  0.305844
tercero  -0.418224     0.603431  0.128822
cuarto   -0.320517    -0.643183  0.319838
quinto    0.955521    -0.295541 -1.277743

[5 rows x 3 columns]
primero   -0.474630
segundo   -0.746577
tercero    1.545612
cuarto     0.634203
quinto     2.389485
Name: velocidad_maxima, dtype: float64
         velocidad  velocidad_maxima  temperatura   presion
primero   0.175374         -0.474630     0.384571 -0.575126
segundo  -0.133466         -0.746577     0.987833  0.305844
tercero  -0.418224          1.545612     0.603431  0.128822
cuarto   -0.320517          0.634203    -0.643183  0.319838
quinto    0.955521          2.389485    -0.295541 -1.277743

[5 rows x 4 columns]

 

Una forma alternativa sería usando el método pop:


# forma 2 (borramos usando el método pop y añadimos la columna borrada en la última posición de nuevo)
print(df)
columna = df.pop('velocidad_maxima')
print(df)
print(columna)
df.insert(3, 'velocidad_maxima', columna)
print(df)

Cuyo resultado, paso a paso sería:

         velocidad  velocidad_maxima  temperatura   presion
primero   0.175374         -0.474630     0.384571 -0.575126
segundo  -0.133466         -0.746577     0.987833  0.305844
tercero  -0.418224          1.545612     0.603431  0.128822
cuarto   -0.320517          0.634203    -0.643183  0.319838
quinto    0.955521          2.389485    -0.295541 -1.277743

[5 rows x 4 columns]
         velocidad  temperatura   presion
primero   0.175374     0.384571 -0.575126
segundo  -0.133466     0.987833  0.305844
tercero  -0.418224     0.603431  0.128822
cuarto   -0.320517    -0.643183  0.319838
quinto    0.955521    -0.295541 -1.277743

[5 rows x 3 columns]
primero   -0.474630
segundo   -0.746577
tercero    1.545612
cuarto     0.634203
quinto     2.389485
Name: velocidad_maxima, dtype: float64
         velocidad  temperatura   presion  velocidad_maxima
primero   0.175374     0.384571 -0.575126         -0.474630
segundo  -0.133466     0.987833  0.305844         -0.746577
tercero  -0.418224     0.603431  0.128822          1.545612
cuarto   -0.320517    -0.643183  0.319838          0.634203
quinto    0.955521    -0.295541 -1.277743          2.389485

[5 rows x 4 columns]

 

Para seleccionar datos concretos de un DataFrame podemos usar el índice, una rebanada, valores booleanos, la columna,…


print('Seleccionamos la columna de velocidades')
print(df['velocidad'])

Seleccionamos la columna de velocidades
primero    0.175374
segundo   -0.133466
tercero   -0.418224
cuarto    -0.320517
quinto     0.955521
Name: velocidad, dtype: float64

print('Seleccionamos todas las columnas cuyo índice es igual a tercero')
print(df.xs('tercero'))

Seleccionamos todas las columnas cuyo índice es igual a tercero
velocidad        -0.418224
temperatura       0.603431
presion           0.128822
velocidad_maxima  1.545612
Name: tercero, dtype: float64

print('Seleccionamos todas las columnas cuyo índice está entre tercero y quinto')
print('Daos cuenta que en este caso los índices son inclusivos')
print(df.ix['tercero':'quinto'])

Seleccionamos todas las columnas cuyo índice está entre tercero y quinto
Daos cuenta que en este caso los índices son inclusivos
         velocidad  temperatura   presion  velocidad_maxima
tercero  -0.418224     0.603431  0.128822          1.545612
cuarto   -0.320517    -0.643183  0.319838          0.634203
quinto    0.955521    -0.295541 -1.277743          2.389485

[3 rows x 4 columns]

print('Seleccionamos todos los valores de velocidad donde la temperatura > 0')
print(df[df['temperatura' > 0]['velocidad'])

 
Seleccionamos todos los valores de velocidad donde la temperatura > 0
primero    0.175374
segundo   -0.133466
tercero   -0.418224
Name: velocidad, dtype: float64

print('Seleccionamos todos los valores de una columna por índice usando una')
print('rebanada (slice) de enteros')
print('Daos cuenta que en este caso el límite superior de la rebanada no se')
print('incluye (Python tradicional)')
print(df.ix[1:3])

Seleccionamos todos los valores de una columna por índice usando una
rebanada (slice) de enteros
Daos cuenta que en este caso el límite superior de la rebanada no se
incluye (Python tradicional)
         velocidad  temperatura   presion  velocidad_maxima
segundo  -0.133466     0.987833  0.305844         -0.746577
tercero  -0.418224     0.603431  0.128822          1.545612

[2 rows x 4 columns]

print(u'Seleccionamos filas y columnas')
print(df.ix[1:3, ['velocidad', 'presion']])

Seleccionamos filas y columnas
         velocidad   presion
segundo  -0.133466  0.305844
tercero  -0.418224  0.128822

[2 rows x 2 columns]

# Algunas de las cosas anteriores se pueden realizar sin usar los métodos .ix() o .xs()</span>
print(df['velocidad]

segundo   -0.133466
tercero   -0.418224
Name: velocidad, dtype: float64

# Da igual si colocamos el slice primero y después las columnas:
df['velocidad'][1:3] == df[1:3]['velocidad']

segundo    True
tercero    True
Name: velocidad, dtype: bool

 

En lo anterior he estado usando los métodos .ix(), .xs() para obtener partes del DataFrame. Son herramientas muy flexibles que nos permiten acceder a los datos de forma muy personalizada. Otras opciones sería usar los métodos .loc(), .iloc(), .select(). Es importante tener en cuenta que las series devueltas cuando se indexa un DataFrame son solo vistas y no una copia de los propios datos. Por tanto, debes ser precavido cuando manipulas los datos (al igual que sucede con los numpy arrays y otros tipos de datos). Lo siguiente (hecho con numpy arrays) es equivalente para las estructuras de datos de Pandas.


# Vista, ¡Cuidado!
a = np.random.rand(5)
data = a[0:2]
data[:] = -999
print(a)

# Copias
a = np.random.rand(5)
data = a[0:2].copy()
data[:] = -999
print(a)

a = np.random.rand(5)
data = 1 * a[0:2]
data[:] = -999
print(a)

a = np.random.rand(5)
np.copyto(data, a[0:2]) # En este caso, data tiene que existir
data[:] = -999
print(a)

a = np.random.rand(5)
data = np.array(a[0:2])
data[:] = -999
print(a)

[ -9.99000000e+02  -9.99000000e+02   7.18723608e-01   5.30962716e-01
   3.43706883e-01]
[ 0.20812195  0.36386055  0.17570252  0.31071035  0.38838464]
[ 0.37175682  0.36962863  0.14481144  0.80786818  0.82803089]
[ 0.89958739  0.00190588  0.14769624  0.3378831   0.74536315]
[ 0.19285654  0.51489647  0.19612007  0.52342758  0.2006809 ]

 

Para acceder a los valores de los índices podemos usar .index.


df.index

Index(['primero', 'segundo', 'tercero', 'cuarto', 'quinto'], dtype='object')

 

Para acceder a los valores de las columnas podemos usar .columns.


df.columns

Index(['velocidad', 'temperatura', 'presion', 'velocidad_maxima'], dtype='object')

 

Otra vez hemos llegado al final. ¡¡Estad atentos a la próxima entrega!!

Written by Kiko

4 de junio de 2014 at 6:00

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 1.269 seguidores

%d personas les gusta esto: