Pandas. Analisis y Modelado

Introducción a Pandas

Pandas es un API para el análisis de datos orientados en columnas. Es perfecto para analizar y manipular datos y muchos frameworks de ML soportan estructuras de datos pandas como entradas. En este post vamos a mencionar conceptos basicos y contretos, ya que una buena introduccion al Panda API ocuparía varias pagina. La información completa se referencia en panda_docs_site

Conceptos Basicos

Para importar el panda API e imprimir su version de API ejecuta:

from __future__ import print_function

import pandas as pd
pd.__version__
>u'0.22.0'

Las estructuras de datos basicas implementan dos clases:

  • DataFrame, que se asemejan a una tabla de datos relacional con filas y columnas nombradas
  • Series, columnas simples. Un DataFrame contien uno o mas Series y un nombre para cada Series

Los data frames son una abstracción utilizada para manipular datos similarmente a los existentes en Spark y R.

Ejemplo de creación de Series

pd.Series(['San Francisco', 'San Jose', 'Sacramento'])
0 San Francisco
1 San Jose
2 Sacramento 
dtype: object

Un DataFrame puede ser creado pasando un mapa en el cual tengamos un nombre string para la columna y su Series. Si las Series no son de la misma longitud, los valores que faltan se completan con valores NA/NaN.

city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])
population = pd.Series([852469, 1015785, 485199])

pd.DataFrame({ 'City name': city_names, 'Population': population })
>
 City name Population
0 San Francisco 852469
1 San Jose 1015785
2 Sacramento 485199

Aunque nos sirve de ejemplo, la mayoría de las veces cargaremos un fichero entero en un DataFrame.  El siguiente código carga un fichero con los datos de las casas de Carolina.

california_housing_dataframe = pd.read_csv("https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv", sep=",")
california_housing_dataframe.describe()

Usando DataFrame.describe() muestra estadistica interesante sobre el DataFrame. Otra función muy usada es DataFrame.head() que muestra unos primeros registros.

california_housing_dataframe.head()

Acceso a Datos

Se puede acceder a los datos de un DataFrame usando las operaciones familiarizadas de Python dict/list

cities = pd.DataFrame({ 'City name': city_names, 'Population': population })
print(type(cities['City name']))
cities['City name']
>
<class 'pandas.core.series.Series'>
0    San Francisco
1         San Jose
2       Sacramento
Name: City name, dtype: object
print(type(cities['City name'][1]))
cities['City name'][1]
>
<type 'str'>
'San Jose'
print(type(cities[0:2]))
cities[0:2]
>
<class 'pandas.core.frame.DataFrame'>
City name Population
0 San Francisco 852469
1 San Jose 1015785

Manipulating Data

Puedes aplicar las operaciones aritméticas básicas de Python a Series

population / 1000.
>
0     852.469
1    1015.785
2     485.199
dtype: float64

NumPy  es una popular herramienta para la computación cientifica, cuyas funciones pueden usar como argumentos panda Series.

import numpy as np

np.log(population)
>
0    13.655892
1    13.831172
2    13.092314
dtype: float64

Si se necesitan trasformaciones más complejas, puedes utilizar Series.apply; al igual que la función map de Python. Series.apply acepta como argumento la función lambda que se aplica a cada valor.

population.apply(lambda val: val > 1000000)
> 
0    False
1     True
2    False
dtype: bool

Modificar DataFrames es también directo.

cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92])
cities['Population density'] = cities['Population'] / cities['Area square miles']
cities
>
City name Population Area square miles Population density
0 San Francisco 852469 46.87 18187.945381
1 San Jose 1015785 176.53 5754.177760
2 Sacramento 485199 97.92 4955.055147

Si queremos combinar dos operaciones para modificar el DataFrame en función de condiciones aplicadas a otras Series. En el ejemplo siguiente vemos como hacerlo:

cities['Is wide and has saint name'] = (cities['Area square miles'] > 50) & cities['City name'].apply(lambda name: name.startswith('San'))
cities

>
City name Population Area square miles Population density Is wide and has saint name
0 San Francisco 852469 46.87 18187.945381 False
1 San Jose 1015785 176.53 5754.177760 True
2 Sacramento 485199 97.92 4955.055147 False

Indicés

Tanto los objetos Series como los DataFrames definen una propiedad llamada index que asigna un identificador a cada elemento de la Series o a cada fila del DataFrame.

Por defectos, en construcción, pandas asigna indices que reflejan el orden de los datos fuente. Una vez creado, los valores de los indices son estable, lo que quiere decir que no cambian cando los datos sean reordenados.

city_names.index
>
RangeIndex(start=0, stop=3, step=1)

Utiliza DataFrame.reindex para reordenar manualmente las filas. El siguiente ejemplo tiene el mismo efecto que ordenar por nombre de ciudad.

cities.reindex([2, 0, 1])
>
	City name	Population	Area square miles	Population density	Is wide and has saint name
2	Sacramento	485199	97.92	4955.055147	False
0	San Francisco	852469	46.87	18187.945381	False
1	San Jose	1015785	176.53	5754.177760	True

La reindexación es una estupenda manera de mezclar un DataFrame. A continuación, tomamos el indice, que es como una matriz y lo pasamos a la función random.permutacion de NumPy que barajea los valores en su lugar.

cities.reindex(np.random.permutation(cities.index))
>
   City name Population Area square miles Population density Is wide and has saint name

1 San Jose 1015785 176.53 5754.177760 True
2 Sacramento 485199 97.92 4955.055147 False
0 San Francisco 852469 46.87 18187.945381 False

Además de esto el método reindex permite indexar valores que no están en los valores del DataFrame original.

cities.reindex([0, 4, 5, 2])
>
    City name	Population	Area square miles	Population density	Is wide and has saint name
0	San Francisco	852469.0	46.87	18187.945381	False
4	NaN	NaN	NaN	NaN	NaN
5	NaN	NaN	NaN	NaN	NaN
2	Sacramento	485199.0	97.92	4955.055147	False

Esto es posible porque los indices a menudo son cadenas obtenidas de los datos reales. Los indices "faltantes" usan una lista externa ya que no se procupan de ser una entrada válida.