Introducción a Pandas#
Pandas es una biblioteca de Python fundamental para la manipulación y el análisis de datos, ampliamente utilizada en el ámbito de la ciencia de datos y la ingeniería de datos. Proporciona estructuras de datos fáciles de usar y de alto rendimiento, como DataFrames y Series, que permiten realizar operaciones complejas de limpieza, transformación, y análisis de datos con una sintaxis intuitiva y eficiente.
#
# ¿Cómo importamos la librería?
import pandas as pd
Series de pandas#
Una pandas Series es una estructura de datos unidimensional en la biblioteca pandas de Python, similar a una columna en una tabla de datos o a un array en otros lenguajes de programación. Cada Series tiene un índice asociado, que son las etiquetas de las filas que permiten acceder a los elementos de la serie de manera rápida y eficiente. Los elementos de una Series pueden ser de diferentes tipos, como enteros, flotantes, cadenas, entre otros.
Lo que hace que las Series sean especialmente poderosas es su capacidad para manejar datos faltantes y realizar operaciones vectorizadas, lo que significa que puedes aplicar operaciones a todos los elementos de una Series de una sola vez, en lugar de tener que iterar sobre cada elemento individualmente. Además, las Series ofrecen una variedad de métodos y propiedades para realizar operaciones estadísticas, manipulación de datos, y transformaciones, lo que las convierte en una herramienta versátil y esencial para el análisis de datos en Python.
# Vamos a crear una serie a partir de un diccionario
test_balance_data = {
'pasan': 20.00,
'treasure': 20.18,
'ashley': 1.05,
'craig': 42.42,
}
# Para crear una serie se debe partir de un objeto de tipo diccionario
balances = pd.Series(test_balance_data)
balances
pasan 20.00
treasure 20.18
ashley 1.05
craig 42.42
dtype: float64
# Acá tenemos una serie de pandas, que es una estructura de datos que se parece a un diccionario (pero más bonito y poderoso)
#las etiquetas de las filas se obtienen
balances.keys()
# tal cual como el un diccionario.
Index(['pasan', 'treasure', 'ashley', 'craig'], dtype='object')
# Alternativamente, se pueden obtener con el método index
balances.index
Index(['pasan', 'treasure', 'ashley', 'craig'], dtype='object')
# Si solo quieres los valores, puedes usar:
balances.values
array([20. , 20.18, 1.05, 42.42])
# Ahora, creemos una serie a partir de una lista
unlabeled_balances = pd.Series([20.00, 20.18, 1.05, 42.42])
unlabeled_balances # Note que las etiquetas de las filas son números enteros que van de 0 a n-1
0 20.00
1 20.18
2 1.05
3 42.42
dtype: float64
# Si quieres personalizar esos index, puedes hacerlo de la siguiente manera
labeled_balances = pd.Series(
[20.00, 20.18, 1.05, 42.42],
index=['pasan', 'treasure', 'ashley', 'craig']
)
labeled_balances
pasan 20.00
treasure 20.18
ashley 1.05
craig 42.42
dtype: float64
# Si pasas un escalar, recuerda que es un valor único,
# este se transmitirá a cada una de las claves especificadas en el argumento de palabra clave index.
pd.Series(20.0, index=["guil", "jay", "james", "ben", "nick"])
guil 20.0
jay 20.0
james 20.0
ben 20.0
nick 20.0
dtype: float64
Acceso a los elementos de una Serie#
import pandas as pd
# Creemos una serie a partir de un diccionario que tiene nombres de personas como claves y saldos como valores
test_balance_data = {
'pasan': 20.00,
'treasure': 20.18,
'ashley': 1.05,
'craig': 42.42,
}
balances = pd.Series(test_balance_data)
Acceso por indexación como lista#
# Una Serie es ordenada e indexada (como las listas de Python)
print(balances[0])
print(type(balances[0]))
20.0
<class 'numpy.float64'>
/var/folders/yj/2_4_0_ln42j05fvg3t6db0pc0000gn/T/ipykernel_46975/363276410.py:2: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
print(balances[0])
/var/folders/yj/2_4_0_ln42j05fvg3t6db0pc0000gn/T/ipykernel_46975/363276410.py:3: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
print(type(balances[0]))
# La forma correcta es:
balances.iloc[0]
np.float64(20.0)
# Si quieres obtener el ultimo balance
balances[-1]
/var/folders/yj/2_4_0_ln42j05fvg3t6db0pc0000gn/T/ipykernel_46975/3307448855.py:2: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
balances[-1]
np.float64(42.42)
Acceso por la clace de índice#
# Acceder a un elemento por su clave(index según pandas)
balances['pasan']
np.float64(20.0)
# Las series de pandas se comportan como diccionarios
for label,value in balances.items():
print(label, value)
pasan 20.0
treasure 20.18
ashley 1.05
craig 42.42
# Acceder a un elemento usando su clave como si fuera un atributo
balances.ashley
np.float64(1.05)
# Acceder a los elementos explicitamente como lo menciona la documentación
print(balances.loc['ashley'])
print(balances.iloc[0])
1.05
20.0
# Acceder a una porción de la serie
balances['treasure':'craig']
treasure 20.18
ashley 1.05
craig 42.42
dtype: float64
balances.iloc[0:3]
pasan 20.00
treasure 20.18
ashley 1.05
dtype: float64
Vectorización y broadcasting#
se trata de la capacidad de pandas para realizar operaciones element-wise (elemento a elemento) en estructuras de datos con diferentes formas, alineando automáticamente las dimensiones según sea necesario.
import pandas as pd
test_balance_data = {
'pasan': 20.00,
'treasure': 20.18,
'ashley': 1.05,
'craig': 42.42,
}
test_deposit_data = {
'pasan': 20,
'treasure': 10,
'ashley': 100,
'craig': 55
}
balances = pd.Series(test_balance_data)
deposits = pd.Series(test_deposit_data)
Vectorización#
Aunque es posible recorrer cada elemento y aplicarlo a otro…
for label, value in deposits.items():
print(f"Depositando {value} en la cuenta de {label}, que tiene {balances[label]}")
balances[label] += value
balances
Depositando 20 en la cuenta de pasan, que tiene 20.0
Depositando 10 en la cuenta de treasure, que tiene 20.18
Depositando 100 en la cuenta de ashley, que tiene 1.05
Depositando 55 en la cuenta de craig, que tiene 42.42
pasan 40.00
treasure 30.18
ashley 101.05
craig 97.42
dtype: float64
…es importante recordar apoyarse en la vectorización y omitir los bucles por completo. La vectorización es más rápida y, como puedes ver, más fácil de leer y escribir.
# Se debe considerar que para esta operacion los indices deben coincidir, de lo contrario se generará un NaN
balances += deposits
balances
# agrega 10 a la cuenta de 'james' en el diccionario de depositos para y ejecuta nuevamente esta celda. revisa lo que pasa
pasan 60.00
treasure 40.18
ashley 201.05
craig 152.42
dtype: float64
balances -= deposits
balances
pasan 40.00
treasure 30.18
ashley 101.05
craig 97.42
dtype: float64
Broadcasting#
Esto lo que permite es sumar un escalar a cada uno de los elementos de la serie.
balances + 5
pasan 45.00
treasure 35.18
ashley 106.05
craig 102.42
dtype: float64
Hacer broadcast de una serie con otra serie es posible siempre y cuando ambas tengan el mismo índice. En caso de que no sea así, se devolverá NaN.
coupons = pd.Series(1, ['craig', 'ashley', 'james'])
coupons
craig 1
ashley 1
james 1
dtype: int64
# Sumemos los balances con los cupones, el resultado es una nueva serie
balances + coupons
ashley 102.05
craig 98.42
james NaN
pasan NaN
treasure NaN
dtype: float64
balances
pasan 40.00
treasure 30.18
ashley 101.05
craig 97.42
dtype: float64
balances.add(coupons, fill_value=0) # Así se puede evitar el NaN y que no se pierda la información
ashley 102.05
craig 98.42
james 1.00
pasan 40.00
treasure 30.18
dtype: float64
DataFrames de pandas#
Un DataFrame es una estructura de datos bidimensional en la biblioteca pandas de Python, similar a una tabla de datos o una hoja de cálculo en Excel. Cada DataFrame tiene un índice asociado, que son las etiquetas de las filas, y columnas, que son las etiquetas de las columnas. Los elementos de un DataFrame pueden ser de diferentes tipos, como enteros, flotantes, cadenas, entre otros.
import pandas as pd
test_users_list = [
['Craig', 'Dennis', 42.42],
['Treasure', 'Porth', 25.00]
]
pd.DataFrame(test_users_list)
0 | 1 | 2 | |
---|---|---|---|
0 | Craig | Dennis | 42.42 |
1 | Treasure | Porth | 25.00 |
pd.DataFrame(test_users_list, index=['craigsdennis', 'treasure'],
columns=['first_name', 'last_name', 'balance']) # Note que acá se especifican los nombres de las columnas
first_name | last_name | balance | |
---|---|---|---|
craigsdennis | Craig | Dennis | 42.42 |
treasure | Treasure | Porth | 25.00 |
# Si se tiene un diccionario es más fácil crear un dataframe
test_user_data = {
'first_name': ['Craig', 'Treasure'],
'last_name': ['Dennis', 'Porth'],
'balance': [42.42, 25.00]
}
pd.DataFrame(test_user_data)
first_name | last_name | balance | |
---|---|---|---|
0 | Craig | Dennis | 42.42 |
1 | Treasure | Porth | 25.00 |
# Se puede especificar los indices de las filas
pd.DataFrame(test_user_data, index=['craigsdennis', 'treasure'])
first_name | last_name | balance | |
---|---|---|---|
craigsdennis | Craig | Dennis | 42.42 |
treasure | Treasure | Porth | 25.00 |
Acceso a los elementos de un DataFrame#
# Vamos a crear un dataframe con algunos datos de usuarios
import pandas as pd
test_user_data = { # Estos son los datos de los usuarios y el balance
'first_name': ['Craig', 'Treasure', 'Ashley', 'Guil'],
'last_name': ['Dennis', 'Porth', 'Boucher', 'Hernandez'],
'balance': [42.42, 25.00, 2.02, 87.00]
}
test_user_names = ['craigsdennis', 'treasure', 'lindsay2000', 'guil'] # Supongamos que estos son los usernames
users = pd.DataFrame(test_user_data, index=test_user_names)
users
first_name | last_name | balance | |
---|---|---|---|
craigsdennis | Craig | Dennis | 42.42 |
treasure | Treasure | Porth | 25.00 |
lindsay2000 | Ashley | Boucher | 2.02 |
guil | Guil | Hernandez | 87.00 |
Obtener una columna específica#
Cada columna en un DataFrame es una Serie de pandas, por lo que puedes acceder a una columna específica utilizando la notación de corchetes y el nombre de la columna y el retorno es una serie.
balances = users['balance']
balances
craigsdennis 42.42
treasure 25.00
lindsay2000 2.02
guil 87.00
Name: balance, dtype: float64
# La serie que retorna la celda anterior tiene una propiedad llamada name que retorna el nombre de la columna
balances.name
'balance'
Puedes obtener una fila de un Dataframe usando la propiedad .loc[] y pasando el índice de la fila, o el número.
users.loc['guil']
first_name Guil
last_name Hernandez
balance 87.0
Name: guil, dtype: object
users.iloc[3]
first_name Guil
last_name Hernandez
balance 87.0
Name: guil, dtype: object
Recuperar un valor específico mediante encadenamiento#
users['first_name']['craigsdennis']
'Craig'
users.loc['craigsdennis']['first_name']
'Craig'
users.loc['craigsdennis', 'first_name']
'Craig'
users.at['craigsdennis', 'first_name']
'Craig'
Usando las propiedades loc e iloc puedes dividir un DataFrame existente en uno nuevo.
En el ejemplo a continuación, usamos : en el eje de filas para seleccionar todas las filas, y especificamos qué columnas queremos recuperar usando una lista en el eje de columnas
# Esto se lee como:
# En el dataframe users, selecciona todas las filas, y dame solo las columnas 'balance' y 'last_name'
users.loc[:, ['balance', 'last_name']]
balance | last_name | |
---|---|---|
craigsdennis | 42.42 | Dennis |
treasure | 25.00 | Porth |
lindsay2000 | 2.02 | Boucher |
guil | 87.00 | Hernandez |
# O lo puedes indexar por posición como en las listas
users.iloc[1:3, 1:]
last_name | balance | |
---|---|---|
treasure | Porth | 25.00 |
lindsay2000 | Boucher | 2.02 |
Cargar Fuentes externas de datos#
Naturalmente, cuando se trabaja con datos en la vida real, no siempre se tienen los datos en un DataFrame de pandas. A menudo, los datos se almacenan en archivos CSV, Excel, bases de datos SQL, o en la web. Afortunadamente, pandas proporciona una variedad de funciones para cargar datos desde diferentes fuentes y formatos, lo que facilita la importación y exportación de datos en Python.
La sintaxis general para cargar datos en un DataFrame de pandas es la siguiente:#
import pandas as pd
pd.read_<formato>('ruta/al/archivo')
La sintaxis general para guardar datos desde un DataFrame de pandas es la siguiente:#
import pandas as pd
df.to_<formato>('ruta/al/archivo')
Los formatos más comunes para cargar y guardar datos en pandas son los siguientes:
# Vamos a importar los datos de un archivo CSV
# 1. Veamos lo que tiene el archivo users.csv
!head -n 5 data/users.csv # Este comando solo es una muestra del contenido del archivo, no tiene nada que ver con python o pandas.
,first_name,last_name,email,email_verified,signup_date,referral_count,balance
aaron,Aaron,Davis,aaron6348@gmail.com,True,2018-08-31,6,18.14
acook,Anthony,Cook,cook@gmail.com,True,2018-05-12,2,55.45
adam.saunders,Adam,Saunders,adam@gmail.com,False,2018-05-29,3,72.12
adrian,Adrian,Fang,adrian.fang@teamtreehouse.com,True,2018-04-28,3,30.01
import pandas as pd
my_data = pd.read_csv('data/users.csv')
my_data # Note que hay una columna sin nombre, que es el índice y carga como Unnamed: 0. vamos a arreglar eso
Unnamed: 0 | first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|---|
0 | aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
1 | acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
2 | adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
3 | adrian | Adrian | Fang | adrian.fang@teamtreehouse.com | True | 2018-04-28 | 3 | 30.01 |
4 | adrian.blair | Adrian | Blair | adrian9335@gmail.com | True | 2018-06-16 | 7 | 25.85 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
470 | wilson | Robert | Wilson | robert@yahoo.com | False | 2018-05-16 | 5 | 59.75 |
471 | wking | Wanda | King | wanda.king@holt.com | True | 2018-06-01 | 2 | 67.08 |
472 | wright3590 | Jacqueline | Wright | jacqueline.wright@gonzalez.com | True | 2018-02-08 | 6 | 18.48 |
473 | young | Jessica | Young | jessica4028@yahoo.com | True | 2018-07-17 | 4 | 75.39 |
474 | zachary.neal | Zachary | Neal | zneal@gmail.com | True | 2018-07-26 | 1 | 39.90 |
475 rows × 8 columns
# Usemos los parámetros para cargar el archivo CSV
my_data = pd.read_csv('data/users.csv',index_col=0)
# Otros parametros que se pueden usar son:
# sep: el separador de los datos. Algunos archivos CSV usan ; en lugar de ,
# header: la fila que se usará como encabezado.
# names: una lista con los nombres de las columnas.
# skiprows: las filas que se deben omitir
# na_values: los valores que se deben tratar como NaN
# nrows: el número de filas que se deben leer
# encoding: la codificación de caracteres que se debe usar
# Existen otros parámetros que se pueden usar, pero estos son los más comunes.
# Vamos a ver otros parámetros a lo largo de los ejemplos.
my_data
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
adrian | Adrian | Fang | adrian.fang@teamtreehouse.com | True | 2018-04-28 | 3 | 30.01 |
adrian.blair | Adrian | Blair | adrian9335@gmail.com | True | 2018-06-16 | 7 | 25.85 |
... | ... | ... | ... | ... | ... | ... | ... |
wilson | Robert | Wilson | robert@yahoo.com | False | 2018-05-16 | 5 | 59.75 |
wking | Wanda | King | wanda.king@holt.com | True | 2018-06-01 | 2 | 67.08 |
wright3590 | Jacqueline | Wright | jacqueline.wright@gonzalez.com | True | 2018-02-08 | 6 | 18.48 |
young | Jessica | Young | jessica4028@yahoo.com | True | 2018-07-17 | 4 | 75.39 |
zachary.neal | Zachary | Neal | zneal@gmail.com | True | 2018-07-26 | 1 | 39.90 |
475 rows × 7 columns
Exploremos los datos que importamos#
La forma más fácil si cargamos correctamente, podemos usar el método .head() para ver las primeras filas de un DataFrame, o .tail() para ver las últimas filas.
my_data.head() # Muestra las primeras 5 filas del dataframe
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
adrian | Adrian | Fang | adrian.fang@teamtreehouse.com | True | 2018-04-28 | 3 | 30.01 |
adrian.blair | Adrian | Blair | adrian9335@gmail.com | True | 2018-06-16 | 7 | 25.85 |
my_data.head(3)
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
# Revisemos la cantidad de muestras que tiene el dataframe
len(my_data)
475
my_data.shape # Shape es un atributo que tiene todo DF (dataFrame) y retorna una tupla con la cantidad de filas y columnas
(475, 7)
Explorando la información#
Info#
El método .info() proporciona una descripción concisa de un DataFrame, incluyendo el número de filas y columnas, el número de valores no nulos, el tipo de datos de cada columna, y la cantidad de memoria utilizada.
my_data.info() # Info es un método que retorna información sobre el dataframe
<class 'pandas.core.frame.DataFrame'>
Index: 475 entries, aaron to zachary.neal
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 first_name 475 non-null object
1 last_name 430 non-null object
2 email 475 non-null object
3 email_verified 475 non-null bool
4 signup_date 475 non-null object
5 referral_count 475 non-null int64
6 balance 475 non-null float64
dtypes: bool(1), float64(1), int64(1), object(4)
memory usage: 26.4+ KB
Counts#
El método .count() cuenta, por cada columna, cuantos valores no nulos hay.
my_data.count()
first_name 475
last_name 430
email 475
email_verified 475
signup_date 475
referral_count 475
balance 475
dtype: int64
La mayoría de nuestras columnas incluyen valores para cada fila, pero parece que last_name tiene algunos valores faltantes. Los datos faltantes aparecerán como np.nan – no es un número de NumPy – en esos registros.
Dtype#
# Revisemos los tipos de datos de las columnas
my_data.dtypes
first_name object
last_name object
email object
email_verified bool
signup_date object
referral_count int64
balance float64
dtype: object
a mayoría de los tipos de datos de estas columnas fueron inferidos o asumidos correctamente. Observa cómo email_verified es automáticamente bool, referral_count es un entero y balance un flotante. Esto es lo que hace pandas cuando usamos pd.read_csv.
Sin embargo, una cosa a tener en cuenta es que el campo signup_date es un objeto y no un datetime. Puedes convertir estos datos durante o después de la importación si lo necesitas, y haremos algunas de esas conversiones más adelante en este curso.
Describe#
El método DataFrame.describe es una excelente manera de obtener una idea general de todos los datos numéricos en tu DataFrame. Notarás que solo se devuelven las columnas que tienen datos numéricos, mientras que las que tienen valores booleanos o texto, como email_verified y first_name, se omiten.
Verás muchas agregaciones diferentes.
my_data.describe()
referral_count | balance | |
---|---|---|
count | 475.000000 | 475.000000 |
mean | 3.429474 | 49.933263 |
std | 2.281085 | 28.280448 |
min | 0.000000 | 0.050000 |
25% | 2.000000 | 25.305000 |
50% | 3.000000 | 51.570000 |
75% | 5.000000 | 74.480000 |
max | 7.000000 | 99.900000 |
Otros métodos#
# El promedio
my_data.balance.mean()
np.float64(49.933263157894736)
# La desviación estándar
my_data.balance.std()
np.float64(28.28044849675191)
# La suma de todo el balance
my_data.balance.sum()
np.float64(23718.3)
# El valor minimo del balance
my_data.balance.min()
np.float64(0.05)
# El valor máximo del balance
my_data.balance.max()
np.float64(99.9)
# Obtener, de una columna en específico, el conteo de los valores únicos
my_data.email_verified.value_counts()
email_verified
True 389
False 86
Name: count, dtype: int64
Reordenar columnas#
Con pandas puedes ordenar rápidamente las columnas de un DF.
Ordenemos el DataFrame para que el usuario con el saldo más alto esté en la parte superior. Por defecto, se asume el orden ascendente, pero puedes cambiar eso configurando el argumento de palabra clave ascending en False.
my_data.sort_values(by='balance', ascending=False).head()
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
twhite | Timothy | White | white5136@hotmail.com | True | 2018-07-06 | 5 | 99.90 |
karen.snow | Karen | Snow | ksnow@yahoo.com | True | 2018-05-06 | 2 | 99.38 |
king | Billy | King | billy.king@hotmail.com | True | 2018-05-29 | 4 | 98.80 |
king3246 | Brittney | King | brittney@yahoo.com | True | 2018-04-15 | 6 | 98.79 |
crane203 | Valerie | Crane | valerie7051@hotmail.com | True | 2018-05-12 | 3 | 98.69 |
my_data.head()
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
adrian | Adrian | Fang | adrian.fang@teamtreehouse.com | True | 2018-04-28 | 3 | 30.01 |
adrian.blair | Adrian | Blair | adrian9335@gmail.com | True | 2018-06-16 | 7 | 25.85 |
Notarás que la llamada a sort_values en realidad creó un nuevo DataFrame. Si deseas cambiar permanentemente el orden predeterminado (ordenado por índice), puedes pasar True como argumento al parámetro de palabra clave inplace.
# Ordenar primero por last_name y luego por first_name. Por defecto, np.nan aparece al final
my_data.sort_values(by=['last_name', 'first_name'], inplace=True)
# El orden ahora ha cambiado
my_data.head()
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
darlene.adams | Darlene | Adams | adams@hotmail.com | True | 2018-09-15 | 2 | 67.02 |
lauren | Lauren | Aguilar | lauren.aguilar@summers.com | False | 2018-05-31 | 4 | 69.90 |
daniel | Daniel | Allen | allen@hotmail.com | False | 2018-07-01 | 2 | 21.21 |
kallen | Kathy | Allen | kathy@hotmail.com | False | 2018-02-20 | 1 | 43.72 |
alvarado | Denise | Alvarado | alvarado@hotmail.com | True | 2018-09-07 | 6 | 26.72 |
# Si quieres ordenar por el índice, puedes usar sort_index
my_data.sort_index(inplace=True)
my_data.head()
first_name | last_name | email_verified | signup_date | referral_count | balance | ||
---|---|---|---|---|---|---|---|
aaron | Aaron | Davis | aaron6348@gmail.com | True | 2018-08-31 | 6 | 18.14 |
acook | Anthony | Cook | cook@gmail.com | True | 2018-05-12 | 2 | 55.45 |
adam.saunders | Adam | Saunders | adam@gmail.com | False | 2018-05-29 | 3 | 72.12 |
adrian | Adrian | Fang | adrian.fang@teamtreehouse.com | True | 2018-04-28 | 3 | 30.01 |
adrian.blair | Adrian | Blair | adrian9335@gmail.com | True | 2018-06-16 | 7 | 25.85 |
Más fuentes de información con pandas#
Exploremos la forma en la que se cargan diferentes fuentes de datos con pandas, una vez cargados en un dataframe, se pueden aplicar los métodos y propiedades que hemos visto anteriormente.
Cargar JSON#
filepath = 'data/anscombe.json'
# Leer el archivo JSON
data = pd.read_json(filepath)
# Verificar los datos
data.head()
Series | X | Y | |
---|---|---|---|
0 | I | 10 | 8.04 |
1 | I | 8 | 6.95 |
2 | I | 13 | 7.58 |
3 | I | 9 | 8.81 |
4 | I | 11 | 8.33 |
data.to_json('data/mi_archivojson.json')
Cargar Excel#
Carguemos el archivo data/datos_rrss.xlsx en un DataFrame de pandas.
Primero revisemos el contenido del archivo Excel.
# Para cargar los datos de un archivo Excel, se usa la función read_excel
# Debemos instalar la librería openpyxl
%pip install openpyxl
Collecting openpyxl
Downloading openpyxl-3.1.4-py2.py3-none-any.whl.metadata (2.5 kB)
Collecting et-xmlfile (from openpyxl)
Using cached et_xmlfile-1.1.0-py3-none-any.whl.metadata (1.8 kB)
Downloading openpyxl-3.1.4-py2.py3-none-any.whl (251 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 251.4/251.4 kB 1.4 MB/s eta 0:00:00a 0:00:01m
?25hUsing cached et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.1.0 openpyxl-3.1.4
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
my_excel = pd.read_excel('data/datos_rrss.xlsx')
my_excel.head()
Nombre | Cantidad | ES_FBK | Año | |
---|---|---|---|---|
0 | 2449 | True | 2006 | |
1 | 339 | False | 2006 | |
2 | 1000 | True | 2010 | |
3 | YouTube | 2000 | False | 2005 |
4 | 663 | False | 2003 |
Esto por defecto carga la primera hoja del archivo Excel. Si deseas cargar una hoja específica, puedes hacerlo pasando el nombre de la hoja al argumento sheet_name.
my_excel = pd.read_excel('data/datos_rrss.xlsx',sheet_name='Hoja3')
my_excel.head() # Note que este no tiene encabezado de la información
2449 | True | 2006 | ||
---|---|---|---|---|
0 | 339 | False | 2006 | |
1 | 1000 | True | 2010 | |
2 | YouTube | 2000 | False | 2005 |
3 | 663 | False | 2003 | |
4 | 1600 | True | 2009 |
# Ajustar el encabezado
my_excel = pd.read_excel('data/datos_rrss.xlsx',sheet_name='Hoja3')
my_excel.columns = ['Nombre', 'Cantidad', 'ES_FBK', 'Año'] # De esta forma asignamos las columnas.
my_excel.head()
Nombre | Cantidad | ES_FBK | Año | |
---|---|---|---|---|
0 | 339 | False | 2006 | |
1 | 1000 | True | 2010 | |
2 | YouTube | 2000 | False | 2005 |
3 | 663 | False | 2003 | |
4 | 1600 | True | 2009 |
my_excel.to_excel('data/mi_archivo_excel.xlsx', index=False) # Guardar el archivo sin el índice (los números de las filas)
Cargar Directamente desde la web#
Pandas también puede cargar datos directamente desde una URL. Esto es útil si los datos que deseas cargar están disponibles en la web y no necesitas descargarlos localmente.
Leer CSV desde una URL#
# Ejemplo para cargar datos desde la web
import pandas as pd
url = 'https://raw.githubusercontent.com/datasets/investor-flow-of-funds-us/master/data/weekly.csv'
data = pd.read_csv(url)
data.head()
Date | Total Equity | Domestic Equity | World Equity | Hybrid | Total Bond | Taxable Bond | Municipal Bond | Total | |
---|---|---|---|---|---|---|---|---|---|
0 | 2012-12-05 | -7426 | -6060 | -1367 | -74 | 5317 | 4210 | 1107 | -2183 |
1 | 2012-12-12 | -8783 | -7520 | -1263 | 123 | 1818 | 1598 | 219 | -6842 |
2 | 2012-12-19 | -5496 | -5470 | -26 | -73 | 103 | 3472 | -3369 | -5466 |
3 | 2012-12-26 | -4451 | -4076 | -375 | 550 | 2610 | 3333 | -722 | -1291 |
4 | 2013-01-02 | -11156 | -9622 | -1533 | -158 | 2383 | 2103 | 280 | -8931 |
Leer HTML desde una URL#
Para este necesitamos instalar la librería lxml, que es una librería de Python que permite trabajar con archivos XML y HTML.
%pip install lxml
Requirement already satisfied: lxml in ./.venv/lib/python3.11/site-packages (5.2.2)
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
# URL de la página web que contiene la tabla
url = 'https://en.wikipedia.org/wiki/List_of_countries_by_inflation_rate'
# Leer todas las tablas en la página web
tables = pd.read_html(url)
tables
[ 0 1
0 NaN This article needs to be updated. Please help ...,
Country/Territory/Region/Group \
Country/Territory/Region/Group
0 Aruba
1 Afghanistan
2 Angola
3 Albania
4 Andorra
.. ...
245 European Union
246 G20 (Group of Twenty)
247 G-77 (Group of 77)
248 OECD (Organisation for Economic Cooperation an...
249 Notes: WB: Consumer price index reflects chang...
World Bank consumer price indices (in %) \
2010
0 2.08
1 2.18
2 14.47
3 3.63
4 NaN
.. ...
245 1.53
246 NaN
247 NaN
248 1.81
249 Notes: WB: Consumer price index reflects chang...
\
2011
0 4.32
1 11.80
2 13.48
3 3.43
4 NaN
.. ...
245 3.29
246 NaN
247 NaN
248 3.37
249 Notes: WB: Consumer price index reflects chang...
\
2012
0 0.63
1 6.44
2 10.28
3 2.03
4 NaN
.. ...
245 2.66
246 NaN
247 NaN
248 2.53
249 Notes: WB: Consumer price index reflects chang...
\
2013
0 −2.37
1 7.39
2 8.78
3 1.94
4 NaN
.. ...
245 1.22
246 NaN
247 NaN
248 1.45
249 Notes: WB: Consumer price index reflects chang...
\
2014
0 0.42
1 4.67
2 7.28
3 1.63
4 NaN
.. ...
245 0.20
246 NaN
247 NaN
248 0.62
249 Notes: WB: Consumer price index reflects chang...
\
2015
0 0.47
1 −0.66
2 9.35
3 1.90
4 NaN
.. ...
245 −0.06
246 NaN
247 NaN
248 0.34
249 Notes: WB: Consumer price index reflects chang...
\
2016
0 −0.93
1 4.38
2 30.70
3 1.28
4 NaN
.. ...
245 0.18
246 NaN
247 NaN
248 0.44
249 Notes: WB: Consumer price index reflects chang...
\
2017
0 −1.03
1 4.98
2 29.84
3 1.99
4 NaN
.. ...
245 1.43
246 NaN
247 NaN
248 1.82
249 Notes: WB: Consumer price index reflects chang...
\
2018
0 3.63
1 0.63
2 19.63
3 2.03
4 NaN
.. ...
245 1.74
246 NaN
247 NaN
248 1.93
249 Notes: WB: Consumer price index reflects chang...
\
2019
0 4.26
1 2.30
2 17.08
3 1.41
4 NaN
.. ...
245 1.63
246 NaN
247 NaN
248 1.74
249 Notes: WB: Consumer price index reflects chang...
\
2020
0 NaN
1 NaN
2 22.27
3 1.62
4 NaN
.. ...
245 0.48
246 NaN
247 NaN
248 0.73
249 Notes: WB: Consumer price index reflects chang...
\
2021
0 NaN
1 NaN
2 25.75
3 2.04
4 NaN
.. ...
245 2.55
246 NaN
247 NaN
248 2.82
249 Notes: WB: Consumer price index reflects chang...
\
2022
0 NaN
1 NaN
2 NaN
3 6.73
4 NaN
.. ...
245 8.83
246 NaN
247 NaN
248 8.24
249 Notes: WB: Consumer price index reflects chang...
WB Consumer price index (2010=100) \
Index
0 109.5
1 149.9
2 583.7
3 131.8
4 NaN
.. ...
245 NaN
246 NaN
247 NaN
248 NaN
249 Notes: WB: Consumer price index reflects chang...
Year
0 2019
1 2019
2 2021
3 2022
4 NaN
.. ...
245 NaN
246 NaN
247 NaN
248 NaN
249 Notes: WB: Consumer price index reflects chang...
[250 rows x 16 columns],
Country/Territory/Region/Group \
Country/Territory/Region/Group
Country/Territory/Region/Group
0 Aruba
1 Afghanistan
2 Angola
3 Albania
4 Andorra
.. ...
245 European Union
246 G20 (Group of Twenty)
247 G-77 (Group of 77)
248 OECD (Organisation for Economic Cooperation an...
249 Notes: WB: Consumer price index reflects chang...
United Nations consumer price indices \
2010
Unnamed: 1_level_2
0 2.08
1 NaN
2 2.15
3 3.61
4 14.48
.. ...
245 1.69
246 2.54
247 5.15
248 1.74
249 Notes: WB: Consumer price index reflects chang...
\
2011
Unnamed: 2_level_2
0 4.38
1 12.17
2 4.70
3 3.44
4 13.48
.. ...
245 2.74
246 3.75
247 6.87
248 2.79
249 Notes: WB: Consumer price index reflects chang...
\
2012
Unnamed: 3_level_2
0 0.57
1 −28.64
2 1.43
3 2.04
4 10.28
.. ...
245 2.53
246 2.77
247 5.45
248 2.13
249 Notes: WB: Consumer price index reflects chang...
\
2013
Unnamed: 4_level_2
0 −2.37
1 −9.90
2 0.19
3 1.93
4 8.78
.. ...
245 1.37
246 2.40
247 5.15
248 1.53
249 Notes: WB: Consumer price index reflects chang...
\
2014
Unnamed: 5_level_2
0 0.42
1 4.67
2 −0.28
3 1.61
4 7.30
.. ...
245 0.44
246 2.38
247 4.56
248 1.59
249 Notes: WB: Consumer price index reflects chang...
\
2015
Unnamed: 6_level_2
0 0.47
1 −1.55
2 −0.94
3 1.87
4 9.16
.. ...
245 0.17
246 1.72
247 4.08
248 0.53
249 Notes: WB: Consumer price index reflects chang...
\
2016
Unnamed: 7_level_2
0 −0.93
1 2.12
2 −0.57
3 1.29
4 30.69
.. ...
245 0.22
246 2.08
247 5.03
248 1.00
249 Notes: WB: Consumer price index reflects chang...
\
2017
Unnamed: 8_level_2
0 −1.03
1 −10.57
2 1.34
3 1.98
4 29.84
.. ...
245 1.56
246 2.34
247 3.98
248 2.07
249 Notes: WB: Consumer price index reflects chang...
\
2018
Unnamed: 9_level_2
0 3.63
1 0.63
2 0.38
3 2.03
4 19.63
.. ...
245 1.81
246 2.72
247 4.59
248 2.37
249 Notes: WB: Consumer price index reflects chang...
\
2019
Unnamed: 10_level_2
0 3.94
1 2.30
2 0.75
3 1.41
4 17.08
.. ...
245 1.36
246 2.68
247 5.23
248 1.80
249 Notes: WB: Consumer price index reflects chang...
\
2020
Unnamed: 11_level_2
0 −1.31
1 5.60
2 −0.47
3 1.62
4 22.28
.. ...
245 0.53
246 2.06
247 5.63
248 1.10
249 Notes: WB: Consumer price index reflects chang...
\
2021
Unnamed: 12_level_2
0 0.73
1 5.13
2 1.78
3 2.04
4 25.77
.. ...
245 2.77
246 3.67
247 6.28
248 3.65
249 Notes: WB: Consumer price index reflects chang...
2022
Unnamed: 13_level_2
0 5.59
1 13.71
2 3.04
3 6.73
4 21.36
.. ...
245 8.89
246 7.97
247 7.72
248 8.94
249 Notes: WB: Consumer price index reflects chang...
[250 rows x 14 columns],
vteLists of countries by financial rankings \
0 Trade
1 Investment
2 Funds
3 Budget and debt
4 Income and taxes
5 Bank rates
6 Currency
7 Other
8 Lists of countries by GDP rankings List of int...
vteLists of countries by financial rankings.1
0 Account balance % of GDP Exports by product me...
1 FDI received past FDI abroad GFI
2 Forex reserves Gold reserves Sovereign wealth ...
3 Government budget PPP % of GDP per capita Cred...
4 Tax rates Inheritance tax Tax revenue Wage ave...
5 Central bank interest rate Commercial bank pri...
6 Exchange rates to US$ Inflation rate
7 Financial Development Index Average annual lab...
8 Lists of countries by GDP rankings List of int... ]
# Imprimir el número total de tablas que se traen de la página web
print(f'Número total de tablas: {len(tables)}')
Número total de tablas: 4
# Revisemos las tablas que tenemos
tables[0].head()
0 | 1 | |
---|---|---|
0 | NaN | This article needs to be updated. Please help ... |
tables[1].head()
Country/Territory/Region/Group | World Bank consumer price indices (in %) | WB Consumer price index (2010=100) | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Country/Territory/Region/Group | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | Index | Year | |
0 | Aruba | 2.08 | 4.32 | 0.63 | −2.37 | 0.42 | 0.47 | −0.93 | −1.03 | 3.63 | 4.26 | NaN | NaN | NaN | 109.5 | 2019 |
1 | Afghanistan | 2.18 | 11.80 | 6.44 | 7.39 | 4.67 | −0.66 | 4.38 | 4.98 | 0.63 | 2.30 | NaN | NaN | NaN | 149.9 | 2019 |
2 | Angola | 14.47 | 13.48 | 10.28 | 8.78 | 7.28 | 9.35 | 30.70 | 29.84 | 19.63 | 17.08 | 22.27 | 25.75 | NaN | 583.7 | 2021 |
3 | Albania | 3.63 | 3.43 | 2.03 | 1.94 | 1.63 | 1.90 | 1.28 | 1.99 | 2.03 | 1.41 | 1.62 | 2.04 | 6.73 | 131.8 | 2022 |
4 | Andorra | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
tables[2].head()
Country/Territory/Region/Group | United Nations consumer price indices | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Country/Territory/Region/Group | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | |
Country/Territory/Region/Group | Unnamed: 1_level_2 | Unnamed: 2_level_2 | Unnamed: 3_level_2 | Unnamed: 4_level_2 | Unnamed: 5_level_2 | Unnamed: 6_level_2 | Unnamed: 7_level_2 | Unnamed: 8_level_2 | Unnamed: 9_level_2 | Unnamed: 10_level_2 | Unnamed: 11_level_2 | Unnamed: 12_level_2 | Unnamed: 13_level_2 | |
0 | Aruba | 2.08 | 4.38 | 0.57 | −2.37 | 0.42 | 0.47 | −0.93 | −1.03 | 3.63 | 3.94 | −1.31 | 0.73 | 5.59 |
1 | Afghanistan | NaN | 12.17 | −28.64 | −9.90 | 4.67 | −1.55 | 2.12 | −10.57 | 0.63 | 2.30 | 5.60 | 5.13 | 13.71 |
2 | Angola | 2.15 | 4.70 | 1.43 | 0.19 | −0.28 | −0.94 | −0.57 | 1.34 | 0.38 | 0.75 | −0.47 | 1.78 | 3.04 |
3 | Albania | 3.61 | 3.44 | 2.04 | 1.93 | 1.61 | 1.87 | 1.29 | 1.98 | 2.03 | 1.41 | 1.62 | 2.04 | 6.73 |
4 | Andorra | 14.48 | 13.48 | 10.28 | 8.78 | 7.30 | 9.16 | 30.69 | 29.84 | 19.63 | 17.08 | 22.28 | 25.77 | 21.36 |
tables[3].head()
vteLists of countries by financial rankings | vteLists of countries by financial rankings.1 | |
---|---|---|
0 | Trade | Account balance % of GDP Exports by product me... |
1 | Investment | FDI received past FDI abroad GFI |
2 | Funds | Forex reserves Gold reserves Sovereign wealth ... |
3 | Budget and debt | Government budget PPP % of GDP per capita Cred... |
4 | Income and taxes | Tax rates Inheritance tax Tax revenue Wage ave... |
Vamos a leer otras tablas a partir de HTML
import pandas as pd
table_MN = pd.read_html('https://en.wikipedia.org/wiki/Minnesota')
print(f'Total de tablas obtenidas: {len(table_MN)}')
Total de tablas obtenidas: 28
Con 38 tablas, puede ser un desafío encontrar la que necesitas. Para facilitar la selección de la tabla, utiliza el parámetro match para seleccionar un subconjunto de tablas. Podemos usar el título “Resultados de las elecciones en carreras estatales” para seleccionar la tabla:
%pip install html5lib
Requirement already satisfied: html5lib in ./.venv/lib/python3.11/site-packages (1.1)
Requirement already satisfied: six>=1.9 in ./.venv/lib/python3.11/site-packages (from html5lib) (1.16.0)
Requirement already satisfied: webencodings in ./.venv/lib/python3.11/site-packages (from html5lib) (0.5.1)
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
table_MN = pd.read_html('https://en.wikipedia.org/wiki/Minnesota', match='presidential election results')
len(table_MN)
1
df = table_MN[0]
df.head()
Year | Republican | Democratic | Third party | ||||
---|---|---|---|---|---|---|---|
Year | No. | % | No. | % | No. | % | |
0 | 2020 | 1484065 | 45.28% | 1717077 | 52.40% | 76029 | 2.32% |
1 | 2016 | 1323232 | 44.93% | 1367825 | 46.44% | 254176 | 8.63% |
2 | 2012 | 1320225 | 44.96% | 1546167 | 52.65% | 70169 | 2.39% |
3 | 2008 | 1275409 | 43.82% | 1573354 | 54.06% | 61606 | 2.12% |
4 | 2004 | 1346695 | 47.61% | 1445014 | 51.09% | 36678 | 1.30% |
df.to_html('data/mi_tabla_descargada.html')
df.to_json('data/mi_tabla_descargada.json')
df.to_csv('data/mi_tabla_descargada.csv')
df.to_excel('data/mi_tabla_descargada.xlsx')
Cargar datos de una base de datos SQL (Mid-level)#
Pandas permite cargar datos directamente desde una base de datos SQL o NoSQL, como SQLite, MySQL, PostgreSQL, MongoDB, entre otros. Para ello, necesitas instalar los controladores de la base de datos correspondiente, como sqlalchemy, psycopg2, pymysql, pymongo, entre otros.
SQL#
%pip install sqlalchemy
Collecting sqlalchemy
Downloading SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl.metadata (9.6 kB)
Requirement already satisfied: typing-extensions>=4.6.0 in ./.venv/lib/python3.11/site-packages (from sqlalchemy) (4.12.2)
Downloading SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl (2.1 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 5.4 MB/s eta 0:00:00a 0:00:010m
?25hInstalling collected packages: sqlalchemy
Successfully installed sqlalchemy-2.0.31
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
from sqlalchemy import create_engine # Este es el método para crear la conexión con la base de datos
# Crear la conexión con la base de datos
engine = create_engine('sqlite:///data/mibase.db') # Para otros motores debes cambiar el nombre del motor
# y la string de de conexión de la DB
# Leer los datos de una tabla específica. En este caso, la tabla rock_songs que es una tabla de la base de datos
data = pd.read_sql('SELECT * FROM rock_songs', engine)
data.head()
Song | Artist | Release_Year | PlayCount | |
---|---|---|---|---|
0 | Caught Up in You | .38 Special | 1982.0 | 82 |
1 | Hold On Loosely | .38 Special | 1981.0 | 85 |
2 | Rockin' Into the Night | .38 Special | 1980.0 | 18 |
3 | Art For Arts Sake | 10cc | 1975.0 | 1 |
4 | Kryptonite | 3 Doors Down | 2000.0 | 13 |
data = pd.read_sql_query("SELECT * FROM rock_songs WHERE Artist='AC/DC' ", engine)
data.head()
Song | Artist | Release_Year | PlayCount | |
---|---|---|---|---|
0 | Back In Black | AC/DC | 1980.0 | 97 |
1 | Big Gun | AC/DC | 1993.0 | 6 |
2 | Dirty Deeds Done Dirt Cheap | AC/DC | 1976.0 | 85 |
3 | For Those About To Rock | AC/DC | 1981.0 | 46 |
4 | Hard As A Rock | AC/DC | 1995.0 | 1 |
Estas son formas de traer datos desde una base de datos, depende del tamaño de los datos, o del problema, si es conveniente hacer la query y traer el resultado o traer toda la tabla y trabajar con ella.
# Guardar el DataFrame en una tabla de la base de datos
data.to_sql('rock_song_filtered', con=engine, if_exists='replace', index=False)
20