Arreglo unidimensional con etiquetas en los ejes (incluidas series de tiempo). Los parámetros de una Serie son: data (matriz, diccionario o escalar), index (arreglo de índices), dtype (numpy.dtype o None) y copy (booleano o por defecto False)
Importamos la biblioteca Pandas
In [1]:
import pandas as pd
pd.Series?
Podemos convertir una lista en una serie y pandas asigna de manera inmediata una lista de índices que empieza en 0.
In [2]:
animales = ['Tigre', 'Oso', 'Camello']
pd.Series(animales)
Out[2]:
In [3]:
numeros = [1, 2, 3]
pd.Series(numeros)
Out[3]:
In [4]:
animales = ['Tigre', 'Oso', None]
pd.Series(animales)
Out[4]:
Es importante saber como NumPy y Pandas manejan los datos faltantes. En Python tenemos el tipo None para indicar un dato faltante. Si tenemos una lista de números, Pandas automáticamente convierte este valor None en un valor designado como NaN, que significa Not a Number.
In [5]:
numeros = [1, 2, None]
pd.Series(numeros)
Out[5]:
Importamos la biblioteca NumPy. También es importante saber que NaN no es None. Cuando hacemos un test para saber si NaN es NaN tambien obtendremos False.
In [6]:
import numpy as np
np.nan == None
Out[6]:
In [7]:
np.nan == np.nan
Out[7]:
Se necesita la función especial isnan de NumPy para chequear la presencia de un no número en nuestros datos.
In [8]:
print(np.isnan(np.nan))
print(None is None)
print(np.nan is np.nan)
¿Cómo creamos una serie en Pandas? Podemos utilizar una estructura de datos diccionario con sus claves y convertirlo en una serie, donde los índices de la serie son las claves del diccionario.
In [9]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes)
s
Out[9]:
Luego, podemos chequear la lista de índices con el atributo .index
In [10]:
s.index
Out[10]:
En este otro ejemplo, pasamos directamente una lista con su conjunto de índices para crear la Serie.
In [11]:
s = pd.Series(['Tigre', 'Oso', 'Camello'], index=['India', 'America', 'Africa'])
s
Out[11]:
Aquí tenemos un ejemplo de un elemento nuevo en la lista de índices que no tiene un valor asignado, no existe un país asociado al índice Natación y Pandas representa este valor faltante con NaN.
In [12]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes, index=['Capoeira', 'Sumo', 'Pelota Vasca', 'Natación'])
s
Out[12]:
In [13]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes)
s
Out[13]:
Podemos hacer búsquedas en las series por posición de índices o por etiqueta de índices. Si queremos hacer búsqueda por ubicación numérica (empezando desde 0) utilizamos el atributo iloc. Si por otra parte, hacemos búqueda por etiqueta de índice entonces usamos el atributo loc.
In [14]:
s.iloc[4]
Out[14]:
In [15]:
s.loc['Pelota Vasca']
Out[15]:
Pandas trata de que el código sea más legible. Si le pasamos por parámetro un valor numérico a la Serie esta se comportará como si la búsqueda se hace con el atributo iloc, si en cambio le pasamos un objeto, hará la búsqueda por etiqueta como con el atributo loc.
In [16]:
s[4]
Out[16]:
In [17]:
s['Pelota Vasca']
Out[17]:
¿Qué pasa cuando tenemos una lista de índices que son enteros?
In [18]:
deportes = {99: 'Brasil',
100: 'Chile',
101: 'País Vasco',
102: 'Cuba',
103: 'Gales',
104: 'Escocia',
105: 'España',
106: 'Japón'}
s = pd.Series(deportes)
s
Out[18]:
Cuando tenemos un caso como este es más seguro utilizar los atributos iloc o loc según sea el caso.
In [19]:
s[0] #Esta instrucción no llamará s.iloc[0] como esperaríamos y va a generar un error
In [ ]:
s.iloc[0]
In [ ]:
s.loc[99]
Ya que sabemos hacer búsquedas en las Series, ahora vamos a trabajar con los datos (encontrar valores, resumir los datos o transformarlos).
In [20]:
s = pd.Series([105.00, 223.00, 5, 102.00, 27, -126])
s
Out[20]:
Una forma de trabajar es iterar sobre un conjunto de datos e invocar una operación de interés
In [21]:
total = 0
for elemento in s:
total+=elemento
print(total)
Con NumPy podemos tener acceso a las funciones universales binarias o unarias (vectorizadas, cálculos más rápidos). En este ejemplo, np.sum hará la suma de todos los elementos en la serie.
In [22]:
import numpy as np
total = np.sum(s)
print(total)
También podemos generar una serie grande de números aleatorios y con el método .head() podemos desplegar un encabezado con los 5 primeros elementos de la serie y con len chequear el tamaño de la misma.
In [23]:
s = pd.Series(np.random.randint(0,1000,10000))
print(s.head())
print(len(s))
Los cuadernos de Jupyter tienen funciones mágicas que pueden ser útiles. Una de ellas es %%timeit que nos servirá para ver cuál de los dos métodos para sumar elementos de una serie es más rápido.
Basta con tipear el símbolo % y la tecla Tab para obtener una lista de las funciones mágicas de Jupyter.
In [24]:
%%timeit -n 100
sumar = 0
for elemento in s:
sumar+=elemento
In [25]:
%%timeit -n 100
sumar = np.sum(s)
NumPy y Pandas tienen el broadcasting, se puede aplicar una operación a cada valor de la serie y modificarla.
In [26]:
s+=2 #Suma 2 a cada elemento de la serie usando broadcasting
s.head()
Out[26]:
Una manera poco eficiente de hacer esto es iterar sobre cada elemento de la serie para hacer la suma. El método .iteritems() devuelve un iterador sobre los pares (key, value) (clave, valor) de un diccionario, en este caso de nuestra serie s.
In [27]:
for etiqueta, valor in s.iteritems():
s.set_value(etiqueta, valor+2)
s.head()
Out[27]:
In [ ]:
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
for etiqueta, valor in s.iteritems():
s.loc[etiqueta]= valor+2
In [ ]:
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
s+=2
Podemos agregar elementos a una serie de la siguiente forma:
In [2]:
import pandas as pd
s = pd.Series([1, 2, 3])
s.loc['Animal'] = 'Oso'
s
Out[2]:
Este es un ejemplo de una serie donde los valores del conjunto de índices no son únicos. Esto hace que las tablas de datos funcionen diferente y es por ello que agregar nuevos elementos debe hacerse con el método append, que en primera instancia, no modificará la serie sino que devuelve una nueva serie con los elementos que se agregaron.
In [4]:
deportes_originales = pd.Series({'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'})
paises_que_aman_el_beisbol = pd.Series(['Venezuela',
'USA',
'Cuba',
'Puerto Rico',
'Dominicana'],
index=['Béisbol',
'Béisbol',
'Béisbol',
'Béisbol',
'Béisbol'])
todos_los_paises = deportes_originales.append(paises_que_aman_el_beisbol)
In [5]:
deportes_originales
Out[5]:
In [6]:
paises_que_aman_el_beisbol
Out[6]:
In [7]:
todos_los_paises
Out[7]:
In [8]:
todos_los_paises.loc['Béisbol']
Out[8]:
El DataFrame o Tabla de Datos es el corazón de la biblioteca Pandas. Es el objeto primario para el análisis de datos. Es una especie de arreglo bidimensional con etiquetas en los ejes. En este ejemplo, crearemos tres diccionarios que serán luego las filas de nuestro DataFrame.
In [9]:
import pandas as pd
compra_1 = pd.Series({'Nombre': 'Adelis',
'Artículo comprado': 'Libro',
'Costo': 1200})
compra_2 = pd.Series({'Nombre': 'Miguel',
'Artículo comprado': 'Raspberry pi 3',
'Costo': 15000})
compra_3 = pd.Series({'Nombre': 'Jaime',
'Artículo comprado': 'Balón',
'Costo': 5000})
df = pd.DataFrame([compra_1, compra_2, compra_3], index=['Tienda 1', 'Tienda 1', 'Tienda 2'])
df.head()
Out[9]:
En un DataFrame también se puede extraer información usando los atributos loc y iloc.
In [10]:
df.loc['Tienda 2']
Out[10]:
También podemos chequear el tipo de dato usando la función type de Python.
In [11]:
type(df.loc['Tienda 2'])
Out[11]:
En los DataFrame también se pueden tener listas de índices no únicos. En el ejemplo, hay dos índices con el mismo nombre Tienda 1.
In [12]:
df.loc['Tienda 1']
Out[12]:
También podemos seleccionar columnas agregando un parámetro extra al atributo loc.
In [13]:
df.loc['Tienda 1', 'Costo']
Out[13]:
Usar el atributo .T para obtener la transpuesta del DataFrame o Tabla de Datos.
In [14]:
df.T
Out[14]:
Usando .T.loc[] se puede seleccionar una columna usando como parámetro la etiqueta de su nombre.
In [15]:
df.T.loc['Costo']
Out[15]:
In [17]:
df['Costo']
Out[17]:
In [18]:
df.loc['Tienda 1']['Costo']
Out[18]:
loc también tiene soporte para rebanar o seleccionar del DataFrame con la notación []
In [19]:
df.loc[:,['Nombre', 'Costo']]
Out[19]:
También podemos eliminar datos del DataFrame con la función drop(). Esta función toma un solo parámetro que es el índice del conjunto de datos que deseamos eliminar.
In [20]:
df.drop('Tienda 1')
Out[20]:
Podemos ver que nuestro DataFrame original sigue intacto. Solo hicimos una extracción de información.
In [21]:
df
Out[21]:
También podemos hacer una copia del DataFrame con la función copy() para guardar la extracción de información.
In [24]:
copiar_df = df.copy()
copiar_df = copiar_df.drop('Tienda 1')
copiar_df
Out[24]:
In [26]:
copiar_df.drop?
Podemos eliminar una columna de manera sencilla, usando simplemente la palabra clave del y el índice o nombre de la comuna.
In [27]:
del copiar_df['Costo']
copiar_df
Out[27]:
Finalmente, es muy sencillo agregar una columna al DataFrame.
In [28]:
df['Ubicación'] = ['Venezuela', 'Chile', 'Argentina']
df
Out[28]:
Usemos !cat para leer un archivo de formato CSV. Nota: !cat funciona para Linux y Mac pero puede no funcionar para Windows :(
In [2]:
!cat olympics.csv
Pero ... no hay que preocuparse mucho por eso! Podemos leer este archivo en formato CSV en un DataFrame usando la función read_csv.
In [5]:
import pandas as pd
df = pd.read_csv('olympics.csv')
df.head()
Out[5]:
Aquí podemos ignorar la primera fila del DataFrame para dejar más limpia la tabla de información no relevante.
In [6]:
df = pd.read_csv('olympics.csv', index_col = 0, skiprows=1)
df.head()
Out[6]:
El atributo .columns nos permite ver el nombre de las comlumnas del DataFrame y el atributo .rename modificar el nombre.
In [7]:
df.columns
Out[7]:
In [8]:
for col in df.columns:
if col[:2]=='01':
df.rename(columns={col:'Gold' + col[4:]}, inplace=True)
if col[:2]=='02':
df.rename(columns={col:'Silver' + col[4:]}, inplace=True)
if col[:2]=='03':
df.rename(columns={col:'Bronze' + col[4:]}, inplace=True)
if col[:1]=='№':
df.rename(columns={col:'#' + col[1:]}, inplace=True)
df.head()
Out[8]:
Podemos buscar en el DataFrame con una máscara Booleana qué países tienen (True) o no (False) una medalla de oro.
In [9]:
df['Gold'] > 0
Out[9]:
La función .where() toma una máscara booleana como condición en el argumento, la aplica al DataFrame, y devuelve un DataFrame de la misma forma. En nuestro ejemplo, reemplaza con NaN los casos False y con su valor original, los casos True.
In [10]:
only_gold = df.where(df['Gold'] > 0)
only_gold.head()
Out[10]:
Podemos contar cuántas países hay medallas de oro hay en total con count()
In [11]:
only_gold['Gold'].count()
Out[11]:
Si contamos sobre los datos originales, veremos que hay 147 países. Cuenta los países para los cuales la máscara Booleana dio False >.<
In [12]:
df['Gold'].count()
Out[12]:
Podemos establecer otro tipo de condiciones para hacer búsquedas más complejas. Por ejemplo, buscar la cantidad de países que han ganado medalla de oro alguna vez.
In [15]:
len(df[(df['Gold'] > 0) | (df['Gold.1'] > 0)])
Out[15]:
Buscar qué países han ganado sólo medallas de oro en Invierno y nunca en Verano.
In [16]:
df[(df['Gold.1'] > 0) & (df['Gold'] == 0)]
Out[16]:
In [ ]: