9. DataFrames de pandas
#
Pandas es una librería de Python para leer, manipular y analizar datos que se combina muy bien con NumPy y Matplotlib
Pandas nos provee de:
Dos nuevas estructuras de datos:
DataFrame
ySeries
Herramientas de análisis de datos que operan sobre estas estructuras de datos
A continuación nos enfocaremos en la creación y manipulación de objetos tipo DataFrame
import pandas as pd
pd.__version__
'1.4.1'
9.1. Objeto pandas.DataFrame
#
El objeto pandas.DataFrame
es la estructura de datos principal de pandas
Es un arreglo de dos dimensiones (matriz) que representa una tabla
Las filas y columnas de la tabla se identifican con un índice etiquetado denominado label
Cada columna puede tener tipo distinto
Ejemplo: Considere el siguiente DataFrame
que representa datos de la bolsa de Santiago:
Monto [M$] |
Variación [%] |
|
---|---|---|
AGUAS-A |
1653 |
0.36 |
BSANTANDER |
3531 |
-0.31 |
CMPC |
5998 |
-0.6 |
ENTEL |
1408 |
0.0 |
En este ejemplo
La etiqueta de las filas son los nombres de las empresas
La etiqueta de las columnas son los nombres de los atributos medidas por la bolsa
El atributo Monto es un entero
El atributo Variación es un flotante
A continuación veremos como crear y manipular objetos DataFrame
Construcción de un DataFrame
Se usa el constructor
pandas.DataFrame(data=None,
index: Optional[Collection] = None,
columns: Optional[Collection] = None,
dtype: Union[str, numpy.dtype, ExtensionDtype, None] = None,
copy: bool = False)
El argumento más importante es data
, que puede ser un iterable (lista), un diccionario, un ndarray, entre otros
Consideremos lo siguiente
Si usamos un diccionario los
keys
se interpretan como etiquetas de columnasSi usamos un diccionario de diccionarios los
keys
más internos se interpretan como etiquetas de filasSi usamos un
ndarray
y no especificamosindex
y/ocolumns
se crean etiquetas por defecto
A continuación se muestra como se crea un DataFrame en base a un diccionario
names = ["AGUAS-A", "BSANTANDER", "CMPC", "ENTEL"]
ventas = {
'Monto [M$]': [1653, 3351, 5998, 1408],
'Variación [%]': [0.36, 0.31, -0.6, 0.0]
}
df = pd.DataFrame(ventas, index=names)
df
Monto [M$] | Variación [%] | |
---|---|---|
AGUAS-A | 1653 | 0.36 |
BSANTANDER | 3351 | 0.31 |
CMPC | 5998 | -0.60 |
ENTEL | 1408 | 0.00 |
Atributos básicos de un DataFrame
Una vez que haz creado un DataFrame es conveniente revisarlo
Si tu DataFrame se llama df
, puedes usar
df.head(5)
ydf.tail(5)
para imprimir las 5 primeras y 5 últimas filas, respectivamentedf.columns
para recuperar un objetopandas.Index
con las etiquetas de columnadf.index
para recuperar un objetopandas.Index
con las etiquetas de filadf.shape
para recuperar una tupla con el número de filas y número de columnasdf.dtypes
para recuperar una lista con los tipos asignados a cada columnadf.info()
nos da un resumen de lo anterior
Por ejemplo:
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 4 entries, AGUAS-A to ENTEL
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Monto [M$] 4 non-null int64
1 Variación [%] 4 non-null float64
dtypes: float64(1), int64(1)
memory usage: 96.0+ bytes
9.2. Indexación y slicing de DataFrames#
Indexado en base a etiquetas
Podemos usar las etiquetas para indexar un DataFrame ya sea en sus filas o en sus columnas
También podemos hacer slicing, es decir recuperar un subconjunto de un DataFrame
Importante
Se usa la función loc[]
para indexar y hacer slicing en base a la etiqueta. Notar que se ocupa paréntesis cuadrado
Ejemplos
Consideremos el DataFrame
anterior con información de la bolsa de Santiago
Para recuperar la fila de la empresa Entel:
df.loc["ENTEL"]
Monto [M$] 1408.0
Variación [%] 0.0
Name: ENTEL, dtype: float64
Para recuperar un slice con las filas entre Aguas andina hasta CMPC:
df.loc["AGUAS-A":"CMPC"]
Monto [M$] | Variación [%] | |
---|---|---|
AGUAS-A | 1653 | 0.36 |
BSANTANDER | 3351 | 0.31 |
CMPC | 5998 | -0.60 |
Nota
El operador A:B
indica una secuencia que empieza con A y termina con B
Para recuperar un slice con las filas Entel y Aguas Andina:
df.loc[["ENTEL", "AGUAS-A"]]
Monto [M$] | Variación [%] | |
---|---|---|
ENTEL | 1408 | 0.00 |
AGUAS-A | 1653 | 0.36 |
Nota
Esto se conoce como fancy indexing
, corresponde a indexar en base a una lista (o ndarray) que contiene los índices
Para recuperar un slice con las filas anteriores pero sólo con la columna monto total:
df.loc[["ENTEL", "AGUAS-A"], "Monto [M$]"]
ENTEL 1408
AGUAS-A 1653
Name: Monto [M$], dtype: int64
Advertencia
Si una etiqueta está mal escrita pandas retornará un KeyError
Consejo
Si un nombre es complicado de escribir o tiene caracteres especiales podemos usar en su lugar df.columns
, por ejemplo
df.loc[["ENTEL", "AGUAS-A"], df.columns[0]]
que sería equivalente a la expresión anterior
Para recuperar la columna de monto /completa) de forma eficiente:
df["Monto [M$]"]
Advertencia
Tenga presente la siguiente “ambigüedad” de pandas para no confudirse:
df["Monto [M$]"] # Esto es equivalente a df.loc[:, "Monto [M$]"]
df["Monto [M$]":] # Esto retorna error
df["BSANTANDER"] # Esto retorna error
df["BSANTANDER":] # Esto es equivalente a df.loc["BSANTANDER":]
Indexado en base a posición
Internamente, pandas asigna un número entero a cada fila y cada columna que corresponde a su posición
0 |
1 |
|
---|---|---|
0 |
1653 |
0.36 |
1 |
3531 |
-0.31 |
2 |
5998 |
-0.6 |
3 |
1408 |
0.0 |
Importante
Se usa la función iloc[]
para indexar y hacer slicing en base a la posición
Por ejemplo
df.loc["ENTEL"]
y
df.iloc[3]
son equivalentes.
Nota
iloc[]
soporta todo lo que vimos anteriormente para loc[]
Indexado rápido de un elemento
Si queremos recuperar sólo un elemento del DataFrame se recomienda usar las funciones at[]
e iat[]
df.at["ENTEL", "Monto [M$]"]
df.iat[3, 0]
Estas son mucho más rápidas que loc[]
e iloc[]
pero no permiten hacer slicing ni fancy indexing
9.3. Combinación y manipulación de DataFrames#
A continuación veremos como
Combinar dos o más tablas
Intersectar dos tablas
Eliminar filas y columnas de nuestra tabla
Para combinar dos o más DataFrames a nivel de fila o columna se utiliza
pd.concat(objs, # Lista de DataFrames
axis=0, # Eje de concatenación 0:filas, 1: columnas
join='inner', # Especifica que se hace con el eje que no se une
ignore_index: bool = False, # Igual que append
verify_integrity: bool = False, # Igual que append
El argumento principal es una lista de DataFrames
Podemos juntar más de dos DataFrames
Podemos juntarlos en filas o en columnas con
axis
Podemos especificar que ocurre cuando las filas/columnas no calzan con
join
Nota
Por defecto los valores que no se alinean al juntar dos dataframes se llenarán con NaNs
Por ejemplo:
df_nuevo = pd.DataFrame([[100, -30.0]],
index=['JabonCopito'],
columns=['Monto [M$]', 'Variación [%]'])
pd.concat([df, df_nuevo], axis=0)
Monto [M$] | Variación [%] | |
---|---|---|
AGUAS-A | 1653 | 0.36 |
BSANTANDER | 3351 | 0.31 |
CMPC | 5998 | -0.60 |
ENTEL | 1408 | 0.00 |
JabonCopito | 100 | -30.00 |
Finalmente podemos agregar una columna nueva usando el atributo insert
df.insert(loc, # Ubicación de la nueva columna
column, # Nombre de la nueva columna
value, # Lista o ndarray con valores
allow_duplicates=False # Impide agregar una columna con una etiqueta existente
)
La operación insert
es por defecto inplace es decir que modifica directamente a df
Unión e intersección de DataFrames
Podemos combinar DataFrames de acuerdo a sus contenidos con la función pandas.merge
pandas.merge(left, # Primer DataFrame
right, # Segundo DataFrame
how: str = 'inner', # Especifica como se uniran los DataFrames
on=None, # Especifica la columna que se usa para combinar la DataFrames
...
)
Algunos detalles:
La columna que se especifica en
on
debe existir en ambos DataFramesEl argumento
how
tiene las siguientes opcionesleft
: Se usan las llaves del primer DataFrameright
: Se usan las llaves del segundo DataFrameinner
: Se usa una intersección de las llaves de ambos DataFramesouter
: Se usa una unión de las llaves de ambos DataFrames
Eliminando filas y columnas
Podemos eliminar filas o columnas de un DataFrame df
con el atributo drop
df.drop(labels=None, # Etiquetas que quiero elimina, un string o una lista
axis=0, # Eliminar la etiqueta de las filas: 0 o de las columnas: 1
inplace=False, # No modifica df, en su lugar retorna un DataFrame nuevo
...
)
También podemos extraer y eliminar una columna usando el atributo pop
Ordenando DataFrames
Se puede ordenar un DataFrame
según los valores de una columna usando el atributo sort_values
df.sort_values(by, # Columna que guia el ordenamiento
axis=0, # Se ordena según 0: filas o 1:columnas
ascending=True, # Se ordenan los valores de menor a mayor
inplace=False, # No se modifica df, se retorna un nuevo DataFrame
El argumento by
recibe una etiqueta o una lista de etiquetas. En el segundo caso se ordenan jerarquicamente
También existe el atributo sort_index
que permite ordenar filas y columnas según su etiqueta
9.4. Operando con DataFrames#
Operaciones aritméticas y lógicas
Podemos hacer operaciones aritméticas simples sobre DataFrames con los atributos
add
ysub
mul
ydiv
pow
y sus versiones reversas
radd
,rdiv
,rpow
etc
También podemos hacer operaciones lógicas con los atributos
eq
ynq
(igual y no igual)lt
,gt
(menor que y mayor que)le
,ge
(menor o igual y mayor o igual
Todos estos atributos tienen argumento axis
para especificar si la operación es en fila o columna
Por ejemplo
df.add(10) # Le suma 10 a todos los valores de df
df.add([0, 2]) # Le suma 0 a la primera columna y 2 a la segunda columna de df
df.add(df2) # Suma los valores de df y df2 siguiendo los índices
Si usamos una lista tiene que ser tan larga como columnas tenga el DataFrame
En general las operaciones entre columnas se guian por el índice de fila
Si un índice no es compartido se rellena con un NaN
También podemos especificar
fill_value
Si tenemos NaN en filas o columnas podemos
Eliminarlos con el atributo
dropna(axis='columns', how='any')
Rellenarlos con el atributo
fillna(valor)
Operaciones de reducciones
Se pueden aplicar las reducciones que vimos en NumPy directamente sobre las columnas
Algunos atributos para hacer reducción son
count()
sum()
yprod()
mean()
ystd()
max()
eidxmax()
entre otros
Consejo
También podemos usar el atributo describe()
que nos entrega varios estadísticos de nuestras columnas
df.describe()
Monto [M$] | Variación [%] | |
---|---|---|
count | 4.000000 | 4.00000 |
mean | 3102.500000 | 0.01750 |
std | 2114.872179 | 0.44139 |
min | 1408.000000 | -0.60000 |
25% | 1591.750000 | -0.15000 |
50% | 2502.000000 | 0.15500 |
75% | 4012.750000 | 0.32250 |
max | 5998.000000 | 0.36000 |
Conectando con NumPy
Podemos extraer un ndarray
a partir de un DataFrame
usando el atributo values
df.values
array([[ 1.653e+03, 3.600e-01],
[ 3.351e+03, 3.100e-01],
[ 5.998e+03, -6.000e-01],
[ 1.408e+03, 0.000e+00]])
Esto permite aplicar cualquiera de las funciones de librería NumPy
Nota
Si nuestro DataFrame tiene strings entonces values
retornará un arreglo de tipo genérico object
En este caso tenemos dos opciones:
Podemos usar el atributo
select_dtypes
para obtener un nuevo DataFrame con columnas de tipo numéricoPodemos convertir los atributos string a numérico (más adelante detallaremos esto)