Dataframes en Pandas

En este tema vamos a hablar de los dataframes en pandas, probablemente la estructura que más emplearemos. Un dataframe es asimilable a una tabla, es una estructura bidimensional de filas y columnas.

En la practica, cargaremos nuestros datos desde un fichero o una base de datos en un dataframe. Y esta será la estructura de datos que emplearemos en nuestros programas para trabajar con los datos. Cuando hallamos finalizado las operaciones que queramos con los datos, guardaremos el dataframe en algún sistema de persistencia de datos: un fichero o una base de datos.

Sigue leyendo Dataframes en Pandas

Series en pandas

Los objetos Series en pandas son arrays de una dimensión. Recuerda que en el post de introducción a pandas explicaba brevemente las tres estructuras de datos que provee esta libreria.

Creación de objetos Series

En pandas, vamos a poder crear los objetos Series de diversas maneras. En general, como se trata de arrays de una dimensión, tendremos que indicar los indices y los valores que se almacenan en dichos indices.

1.- Con una lista: Para crear un objeto Serie a partir de una lista, se pasa esta como parámetro a la función Series():

import pandas as pd
nombres=["Veronica","Pablo","Fabiola"]
listado = pd.Series(nombres)
print(listado)

Como en este caso sólo pasamos los valores, los indices los toma desde 0 a la longitud de la lista-1. El resultado de ejecutar el código anterior es:

0    Veronica
1       Pablo
2     Fabiola
dtype: object

2.- Con dos listas, una para los indices y otra para los valores. La primera se pasa en el parámetro index y la segunda en el parámetro data

import pandas as pd
nombres=["Veronica","Pablo","Fabiola"]
edades=[50,20,18]
se = pd.Series(index=nombres,data=edades)
print(se)

El resultado de ejecutar el código anterior es:

Veronica    50
Pablo       20
Fabiola     18
dtype: int64

3.- Con un diccionario: Se pasa el diccionario como parámetro a la función Series()

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
edades = pd.Series(dicc)
print(edades)

En este caso, se toman las claves del diccionario como indices, y los valores como valores del array. El resultado de ejecutar el programa anterior:

Veronica    50
Pablo       20
Fabiola     18
dtype: int64

4.- Con un array de Numpy: con un ndarray, más los indices:

import numpy as np
import pandas as pd
datos = np.array([50, 20, 18])
indice = ["Veronica", "Pablo", "Fabiola"]
edades = pd.Series(datos, index=indice)
print(edades)

Parámetros para crear un objeto Series

Los parámetros disponibles para crear un objeto Series son:

  • data: Los datos.
  • index: Los índices.
  • columns: Los nombres de las columnas. Si no se especifica, pandas intenta inferirlos.
  • dtype: Tipo de datos de las columnas.
  • copy: Si es True, se copia la estructura de datos en lugar de modificar el original.

Atributos de los objetos Series

Los principales atributos de los objetos Series de pandas:

  • index: Devuelve el índice de la Serie
  • values: Devuelve un array de NumPy con los valores de la Serie.
  • name: Nombre de la serie
  • dtype: Devuelve el tipo de datos
  • size: Número total de elementos en la Serie.
  • shape: Devuelve una tupla con el número de elementos.
  • empty: Indica si la serie está vacía
  • hasnans: Indica si la serie contiene valores NaN

En el código siguiente puedes ver como acceder a algunos atributos. Es como siempre, atraves del objeto y el nombre del atributo.

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
print("Índice:", serie.index)
print("Valores:", serie.values)
print("Nombre:", serie.name)
print("Tipo de datos:", serie.dtype)
print("Número de elementos:", serie.size)
print("¿Tiene datos?", not serie.empty)

El resultado de ejecución es:

Índice: Index(['Veronica', 'Pablo', 'Fabiola'], dtype='object')
Valores: [50 20 18]
Nombre: None
Tipo de datos: int64
Número de elementos: 3
¿Tiene datos? True

Métodos de los objetos Series

La colección de métodos de los objetos Series es bastante amplia. Algunos de los más habituales son:

  • head(n): Muestra las primeras n filas (valor por defecto: 5)
  • tail(n): Muestra las últimas n filas.
  • info(): Muestra información sobre el índice y el tipo de datos
  • describe(): Genera estadísticas descriptivas básicas
  • value_counts(): Cuenta las ocurrencias de cada valor en la Serie.
  • isna() / isnull(): Devuelve True si el valor es NaN.
  • notna() / notnull(): Devuelve True si el valor no es NaN.
  • dropna(): Elimina los valores NaN de la Serie.
  • fillna(valor): Reemplaza valores NaN con un valor específico.
  • loc[]: Selección basada en etiquetas del índice.
  • iloc[]: Selección basada en posición numérica.
  • sum(): Suma de todos los valores.
  • mean(): Media aritmética.
  • median(): Mediana.
  • min() / max(): Valor mínimo y máximo.

En el código siguiente, puedes ver el uso de algunos de ellos.

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
serie.info()
print("Estadisticos de la serie\n",serie.describe())
print(f"Mediana de la serie: {serie.median()}")

El resultado:

<class 'pandas.core.series.Series'>
Index: 3 entries, Veronica to Fabiola
Series name: None
Non-Null Count  Dtype
--------------  -----
3 non-null      int64
dtypes: int64(1)
memory usage: 48.0+ bytes
Estadisticos de la serie
 count     3.000000
mean     29.333333
std      17.925773
min      18.000000
25%      19.000000
50%      20.000000
75%      35.000000
max      50.000000
dtype: float64
Mediana de la serie: 20.0

Los métodos info() para obtener información general y describe() para obtener los estadísticos, los usaremos a todas horas. También con los DataFrames de pandas.

Principales operaciones con objetos Series

Trabajando con los elementos

Acceso por indice: La forma recomendada para acceder a un elemento de un objeto Series es a través de su índice empleando el método loc[] .

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
edad=serie.loc["Pablo"]
print(f"edad: {edad}")

El resultado:

edad: 20

Aunque también podemos acceder directamente a través del indice sin emplear el método loc(). Si bien se recomienda usar este método

edad=serie["Pablo"]

La sentencia de arriba da el mismo resultado que haciendolo a traves del método.

Acceso por posición: En este caso, empleamos el método iloc() al que pasamos como parámetro la posición, entendida con el índice numérico. Recordemos que los indices empiezan por cero, así que para acceder a la segunda posición pasaríamos el valor de 1.

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
edad=serie.iloc[1]
print(f"edad: {edad}")
edades=serie.iloc[:2]
print(f"edades:\n {edades}")

Podemos referenciar varias posiciones, siguiendo el criterio de selección de indices que empleabamos con las listas. El resultado de ejecución sería:

edad: 20
edades:
 Veronica    50
Pablo       20
dtype: int64

Trabajando con los índices

Ya hemos visto que el parámetro index nos permite definir los indices del objeto Series. Así que podemos cambiar los indices a traves de este parámetro. Eso sí, hay que tener la precaución de definir todos los índices, si nuestra lista de índices tiene un número de valores distinto al número de indices del objeto Series, se producirá un error de Length mismatch: las longitudes de ambos no coinciden.

Aquí te dejo un ejemplo sencillito de la actualización de indices en un objeto Series

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
serie.index=["Francisco","Carmen","Rodrigo"]
print(serie)

El resultado de ejecución es:

Francisco 50
Carmen 20
Rodrigo 18
dtype: int64

No obstante, es extraño que queramos cambiar todos los indices, lo habitual sería que quisieramos cambiar unos pocos indices. En este caso, podemos usar el método rename(), como en el siguiente ejemplo:

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":18}
serie = pd.Series(dicc)
serie.index=["Francisco","Carmen","Rodrigo"]
nueva_serie=serie.rename(index={"Francisco":"Paco"})
print(nueva_serie)

Nos generamos una serie nueva a partir de la serie con la que estabamos trabajando, cambiando los nombres de los indices que queramos. Para ello pasamos un diccionario con los cambios al método rename. Este diccionario tiene por claves los indices actuales y por valores los nuevos indices a los que queremos cambiar. De esta forma, sólo se actua sobre los indices que se quieran cambiar.

Filtrando datos

Otra de las operaciones habituales que tendremos que hacer sobre cualquier estructura de datos es buscar los datos que cumplan unas determinadas condiciones: realizar una consulta. Para ello, definiremos las condiciones que queramos empleando operadores lógicos. Por ejemplo:

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":17}
edades = pd.Series(dicc)
mayores=edades[edades>=18]
print(mayores)
jovenes_mayores=edades[(edades>=18) & (edades<40)]
print(jovenes_mayores)

El resultado:

Veronica    50
Pablo       20
dtype: int64
Pablo    20
dtype: int64

Funciones matemáticas

Los objetos Series son muy similares a los Arrays de Numpy, y en la práctica podemos emplear muchas de las funciones del paquete Numpy con los objetos Series. También las principales funciones estadísticas de Numpy.

import pandas as pd
import numpy as np
dicc={"Veronica":50,"Pablo":20,"Fabiola":17}
edades = pd.Series(dicc)
media_edad=np.mean(edades)
print(f"La media de edad es: {media_edad}")
mayor_edad=np.max(edades)
print(f"El mayor es: {mayor_edad}")
mayores = edades.index[edades.eq(mayor_edad)].to_list()
print(f"Los mayores son: {mayores}")  

El resultado de ejecutar el código anterior es:

La media de edad es: 29.0
El mayor es: 50
Los mayores son: ['Veronica']

Nulos

Finalmente, tenemos que conocer también los métodos que provee pandas para lidiar con los nulos. Estos son los mismos que emplearemos más tarde para trabajar con los objetos DataFrame de pandas.

Los principales métodos para trabajar con nulos son:

  • isna(): Filtra los valores nulos.
  • notna(): Filtra los valores no nulos
  • dropna(): Elimina los valores nulos
  • fillna(): Remplaza los valores nulos por el valor que se pasa al método.

Por ejemplo, si quisieramos detectar los nulos de un objeto Series, empleamos isna():

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":17,"Elena":None}
edades = pd.Series(dicc)
nulos=edades.isna()
print(f"nulos:\n{nulos}")

Este código nos da el resultado:

nulos:
Veronica    False
Pablo       False
Fabiola     False
Elena        True
dtype: bool

Y si ahora queremos sustituir los nulos por la media de la edad, usamos el método fillna():

import pandas as pd
dicc={"Veronica":50,"Pablo":20,"Fabiola":17,"Elena":None}
edades = pd.Series(dicc)
nulos=edades.isna()
print("nulos:\n------")
print(nulos)
edades_sin_nulos=edades.fillna(edades.mean())
print("edades sin nulos:\n-----------------")
print(edades_sin_nulos)

El resultado seria:

nulos:
------
Veronica    False
Pablo       False
Fabiola     False
Elena        True
dtype: bool
edades sin nulos:
-----------------
Veronica    50.0
Pablo       20.0
Fabiola     17.0
Elena       29.0
dtype: float64

NOTA:

Este post es parte de la colección “Python”. Puedes ver el índice de esta colección aquí.