Clase 4 — Pandas I: lectura y exploración de datos#
Python y Políticas Públicas
Contenidos#
¿Qué es pandas?
Series y DataFrames
Crear DataFrames desde cero
Leer archivos: CSV, Excel, y otros formatos
Exploración inicial de un dataset
Selección básica de datos
Ejercicios
import pandas as pd
import numpy as np
# Mostrar todas las columnas
pd.set_option('display.max_columns', None)
pd.set_option('display.float_format', '{:.2f}'.format)
import matplotlib as mpl
import matplotlib.pyplot as plt
# --- Paleta de identidad del curso ---
C = ['#2A6496', '#E07B3F', '#3D9970', '#8E5EA2', '#C0A830', '#637A8A']
mpl.rcParams.update({
'figure.figsize' : (10, 5),
'font.size' : 11,
'axes.titlesize' : 12,
'axes.titleweight' : 'normal',
'axes.spines.top' : False,
'axes.spines.right' : False,
'legend.frameon' : False,
'axes.prop_cycle' : mpl.cycler(color=C),
'figure.dpi' : 110,
})
def tit(ax, t, **kw):
"""Título sin negrita, alineado a la izquierda."""
ax.set_title(t, loc='left', fontweight='normal', **kw)
1. ¿Qué es pandas?#
pandas es la librería central para el análisis de datos en Python. Permite:
Leer y escribir datos en docenas de formatos (CSV, Excel, JSON, SQL, Parquet, etc.)
Manipular tablas de datos de forma intuitiva (similar a Excel, pero programático y escalable)
Combinar, limpiar, transformar y resumir datos
Integrarse perfectamente con matplotlib para visualización
Si venís de Stata o R, pandas es el equivalente a los DataFrames de R o las bases de datos de Stata.
2. Series y DataFrames#
pandas tiene dos estructuras de datos fundamentales:
Series: una columna de datos (array 1D con etiquetas)DataFrame: una tabla (colección de Series)
# Series
pobreza = pd.Series(
[42.3, 38.1, 39.7, 34.2, 48.6],
index=["Buenos Aires", "Córdoba", "Santa Fe", "Mendoza", "Tucumán"],
name="tasa_pobreza_2023"
)
print(pobreza)
print(f"\nMedia: {pobreza.mean():.1f}%")
print(f"Provincia con mayor pobreza: {pobreza.idxmax()}")
Buenos Aires 42.30
Córdoba 38.10
Santa Fe 39.70
Mendoza 34.20
Tucumán 48.60
Name: tasa_pobreza_2023, dtype: float64
Media: 40.6%
Provincia con mayor pobreza: Tucumán
# DataFrame: es la estructura que más usaremos
df = pd.DataFrame({
"provincia": ["Buenos Aires", "Córdoba", "Santa Fe", "Mendoza", "Tucumán"],
"poblacion": [17_500_000, 3_900_000, 3_600_000, 1_950_000, 1_700_000],
"pobreza_pct": [42.3, 38.1, 39.7, 34.2, 48.6],
"pbi_per_capita": [8200, 9100, 8800, 7500, 6300],
})
df
| provincia | poblacion | pobreza_pct | pbi_per_capita | |
|---|---|---|---|---|
| 0 | Buenos Aires | 17500000 | 42.30 | 8200 |
| 1 | Córdoba | 3900000 | 38.10 | 9100 |
| 2 | Santa Fe | 3600000 | 39.70 | 8800 |
| 3 | Mendoza | 1950000 | 34.20 | 7500 |
| 4 | Tucumán | 1700000 | 48.60 | 6300 |
3. Crear DataFrames desde cero#
Hay varias formas de crear un DataFrame:
# Desde un diccionario de listas
df1 = pd.DataFrame({
"year": [2021, 2022, 2023],
"inflacion": [50.9, 94.8, 211.4],
"desempleo": [10.2, 7.0, 6.9],
})
print("Desde diccionario:")
print(df1)
# Desde una lista de diccionarios (cada dict es una fila)
registros = [
{"programa": "AUH", "beneficiarios": 4_200_000, "organismo": "ANSES"},
{"programa": "Progresar", "beneficiarios": 1_100_000, "organismo": "ANSES"},
{"programa": "Potenciar", "beneficiarios": 1_300_000, "organismo": "MDS"},
]
df2 = pd.DataFrame(registros)
print("\nDesde lista de dicts:")
print(df2)
Desde diccionario:
year inflacion desempleo
0 2021 50.90 10.20
1 2022 94.80 7.00
2 2023 211.40 6.90
Desde lista de dicts:
programa beneficiarios organismo
0 AUH 4200000 ANSES
1 Progresar 1100000 ANSES
2 Potenciar 1300000 MDS
4. Leer archivos#
En la práctica, los datos vienen de archivos externos. pandas puede leer la gran mayoría de formatos.
# CSV — el formato más común en portales de datos abiertos
# df = pd.read_csv("datos/indicadores_sociales.csv")
# Opciones útiles:
# df = pd.read_csv("archivo.csv", sep=";") # separador punto y coma
# df = pd.read_csv("archivo.csv", encoding="latin1") # encoding de archivos argentinos
# df = pd.read_csv("archivo.csv", decimal=",") # decimal con coma
# df = pd.read_csv("archivo.csv", skiprows=2) # saltear primeras filas
# df = pd.read_csv("archivo.csv", nrows=1000) # leer solo primeras 1000 filas
# Excel
# df = pd.read_excel("archivo.xlsx", sheet_name="Hoja1")
# JSON
# df = pd.read_json("archivo.json")
# Desde una URL directa
# df = pd.read_csv("https://datos.gob.ar/dataset/.../archivo.csv")
print("Ver ejemplos de lectura en el código comentado arriba.")
print("En la práctica, adaptaremos el read_csv según el archivo de cada fuente.")
Ver ejemplos de lectura en el código comentado arriba.
En la práctica, adaptaremos el read_csv según el archivo de cada fuente.
# Para esta clase trabajaremos con un dataset simulado de programas sociales
np.random.seed(42)
n = 200
provincias_lista = [
"Buenos Aires", "Córdoba", "Santa Fe", "Mendoza", "Tucumán",
"Salta", "Entre Ríos", "Chaco", "Misiones", "Corrientes"
]
programas_lista = ["AUH", "Progresar", "Potenciar Trabajo", "Alimentar", "Crédito Argenta"]
df = pd.DataFrame({
"id_beneficiario": range(1001, 1001 + n),
"provincia": np.random.choice(provincias_lista, n),
"programa": np.random.choice(programas_lista, n),
"edad": np.random.randint(18, 65, n),
"genero": np.random.choice(["F", "M", "X"], n, p=[0.58, 0.40, 0.02]),
"ingreso_mensual": np.random.lognormal(10.2, 0.6, n).round(0),
"anios_educacion": np.random.randint(6, 18, n),
"tiene_empleo_formal": np.random.choice([True, False], n, p=[0.35, 0.65]),
"monto_transferencia": np.random.choice([18000, 25000, 42000, 55000, 10000], n),
"fecha_alta": pd.date_range("2020-01-01", periods=n, freq="3D"),
})
# Introducir algunos valores faltantes
df.loc[np.random.choice(df.index, 15, replace=False), 'ingreso_mensual'] = np.nan
df.loc[np.random.choice(df.index, 8, replace=False), 'anios_educacion'] = np.nan
print(f"Dataset creado: {df.shape[0]} filas × {df.shape[1]} columnas")
df.head()
Dataset creado: 200 filas × 10 columnas
| id_beneficiario | provincia | programa | edad | genero | ingreso_mensual | anios_educacion | tiene_empleo_formal | monto_transferencia | fecha_alta | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1001 | Entre Ríos | Potenciar Trabajo | 54 | M | 43412.00 | 6.00 | True | 10000 | 2020-01-01 |
| 1 | 1002 | Mendoza | AUH | 39 | M | 29352.00 | 7.00 | False | 55000 | 2020-01-04 |
| 2 | 1003 | Chaco | AUH | 46 | F | 78992.00 | 8.00 | True | 25000 | 2020-01-07 |
| 3 | 1004 | Tucumán | Alimentar | 31 | F | 25727.00 | 6.00 | False | 42000 | 2020-01-10 |
| 4 | 1005 | Entre Ríos | Potenciar Trabajo | 45 | F | 37790.00 | 14.00 | False | 55000 | 2020-01-13 |
5. Exploración inicial de un dataset#
Antes de cualquier análisis, siempre hay que entender la estructura y calidad del dataset.
# Dimensiones
print(f"Filas: {df.shape[0]}, Columnas: {df.shape[1]}")
print(f"Columnas: {df.columns.tolist()}")
Filas: 200, Columnas: 10
Columnas: ['id_beneficiario', 'provincia', 'programa', 'edad', 'genero', 'ingreso_mensual', 'anios_educacion', 'tiene_empleo_formal', 'monto_transferencia', 'fecha_alta']
# Tipos de datos y valores nulos
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id_beneficiario 200 non-null int64
1 provincia 200 non-null object
2 programa 200 non-null object
3 edad 200 non-null int64
4 genero 200 non-null object
5 ingreso_mensual 185 non-null float64
6 anios_educacion 192 non-null float64
7 tiene_empleo_formal 200 non-null bool
8 monto_transferencia 200 non-null int64
9 fecha_alta 200 non-null datetime64[ns]
dtypes: bool(1), datetime64[ns](1), float64(2), int64(3), object(3)
memory usage: 14.4+ KB
# Estadísticas descriptivas de variables numéricas
df.describe().round(1)
| id_beneficiario | edad | ingreso_mensual | anios_educacion | monto_transferencia | fecha_alta | |
|---|---|---|---|---|---|---|
| count | 200.00 | 200.00 | 185.00 | 192.00 | 200.00 | 200 |
| mean | 1100.50 | 41.20 | 35302.00 | 11.60 | 30965.00 | 2020-10-25 12:00:00 |
| min | 1001.00 | 18.00 | 3162.00 | 6.00 | 10000.00 | 2020-01-01 00:00:00 |
| 25% | 1050.80 | 29.00 | 18465.00 | 8.00 | 18000.00 | 2020-05-29 06:00:00 |
| 50% | 1100.50 | 42.00 | 29728.00 | 12.00 | 25000.00 | 2020-10-25 12:00:00 |
| 75% | 1150.20 | 52.20 | 43671.00 | 15.00 | 45250.00 | 2021-03-23 18:00:00 |
| max | 1200.00 | 64.00 | 180713.00 | 17.00 | 55000.00 | 2021-08-20 00:00:00 |
| std | 57.90 | 13.10 | 25192.70 | 3.50 | 16924.40 | NaN |
# Descripción de variables categóricas
df.describe(include='object')
| provincia | programa | genero | |
|---|---|---|---|
| count | 200 | 200 | 200 |
| unique | 10 | 5 | 3 |
| top | Entre Ríos | AUH | F |
| freq | 26 | 45 | 123 |
# Conteo de valores nulos por columna
nulos = df.isnull().sum()
print("Valores nulos por columna:")
print(nulos[nulos > 0])
Valores nulos por columna:
ingreso_mensual 15
anios_educacion 8
dtype: int64
# Frecuencias de variables categóricas
print("Distribución por programa:")
print(df['programa'].value_counts())
print()
print("Distribución por género:")
print(df['genero'].value_counts(normalize=True).mul(100).round(1).astype(str) + '%')
Distribución por programa:
programa
AUH 45
Alimentar 44
Potenciar Trabajo 40
Progresar 37
Crédito Argenta 34
Name: count, dtype: int64
Distribución por género:
genero
F 61.5%
M 36.5%
X 2.0%
Name: proportion, dtype: object
6. Selección básica de datos#
Hay tres formas principales de seleccionar datos en un DataFrame:
[]: seleccionar columnas.loc[]: seleccionar por etiqueta (nombre de columna/índice).iloc[]: seleccionar por posición numérica
# Seleccionar una columna → devuelve una Series
provincias = df['provincia']
print(type(provincias))
print(provincias.head())
# Seleccionar múltiples columnas → devuelve un DataFrame
subdf = df[['provincia', 'programa', 'monto_transferencia']]
subdf.head()
<class 'pandas.core.series.Series'>
0 Entre Ríos
1 Mendoza
2 Chaco
3 Tucumán
4 Entre Ríos
Name: provincia, dtype: object
| provincia | programa | monto_transferencia | |
|---|---|---|---|
| 0 | Entre Ríos | Potenciar Trabajo | 10000 |
| 1 | Mendoza | AUH | 55000 |
| 2 | Chaco | AUH | 25000 |
| 3 | Tucumán | Alimentar | 42000 |
| 4 | Entre Ríos | Potenciar Trabajo | 55000 |
# .loc[filas, columnas] — por etiquetas
df.loc[0:4, ['provincia', 'programa', 'edad']]
| provincia | programa | edad | |
|---|---|---|---|
| 0 | Entre Ríos | Potenciar Trabajo | 54 |
| 1 | Mendoza | AUH | 39 |
| 2 | Chaco | AUH | 46 |
| 3 | Tucumán | Alimentar | 31 |
| 4 | Entre Ríos | Potenciar Trabajo | 45 |
# .iloc[filas, columnas] — por posición
df.iloc[0:5, 1:4] # primeras 5 filas, columnas 1 a 3
| provincia | programa | edad | |
|---|---|---|---|
| 0 | Entre Ríos | Potenciar Trabajo | 54 |
| 1 | Mendoza | AUH | 39 |
| 2 | Chaco | AUH | 46 |
| 3 | Tucumán | Alimentar | 31 |
| 4 | Entre Ríos | Potenciar Trabajo | 45 |
# Filtrar filas con condiciones
# Beneficiarios del programa AUH
auh = df[df['programa'] == 'AUH']
print(f"Beneficiarios AUH: {len(auh)}")
# Mujeres mayores de 30 años
mujeres_adultas = df[(df['genero'] == 'F') & (df['edad'] > 30)]
print(f"Mujeres mayores de 30: {len(mujeres_adultas)}")
# Provincias del norte
norte = df[df['provincia'].isin(['Tucumán', 'Salta', 'Chaco', 'Misiones', 'Corrientes'])]
print(f"Beneficiarios en el norte: {len(norte)}")
Beneficiarios AUH: 45
Mujeres mayores de 30: 82
Beneficiarios en el norte: 97
# Crear nuevas columnas
df['ingreso_total'] = df['ingreso_mensual'].fillna(0) + df['monto_transferencia']
df['edad_grupo'] = pd.cut(df['edad'], bins=[18, 30, 45, 65], labels=['18-30', '31-45', '46-65'])
df[['edad', 'edad_grupo', 'ingreso_mensual', 'monto_transferencia', 'ingreso_total']].head(8)
| edad | edad_grupo | ingreso_mensual | monto_transferencia | ingreso_total | |
|---|---|---|---|---|---|
| 0 | 54 | 46-65 | 43412.00 | 10000 | 53412.00 |
| 1 | 39 | 31-45 | 29352.00 | 55000 | 84352.00 |
| 2 | 46 | 46-65 | 78992.00 | 25000 | 103992.00 |
| 3 | 31 | 31-45 | 25727.00 | 42000 | 67727.00 |
| 4 | 45 | 31-45 | 37790.00 | 55000 | 92790.00 |
| 5 | 22 | 18-30 | 44316.00 | 18000 | 62316.00 |
| 6 | 64 | 46-65 | 9436.00 | 18000 | 27436.00 |
| 7 | 47 | 46-65 | 129317.00 | 55000 | 184317.00 |
# Ordenar por columna
df.sort_values('ingreso_mensual', ascending=False).head(5)[['provincia', 'programa', 'ingreso_mensual']]
| provincia | programa | ingreso_mensual | |
|---|---|---|---|
| 80 | Entre Ríos | Crédito Argenta | 180713.00 |
| 7 | Entre Ríos | Alimentar | 129317.00 |
| 166 | Corrientes | Potenciar Trabajo | 125847.00 |
| 150 | Santa Fe | Progresar | 115602.00 |
| 26 | Corrientes | Progresar | 112863.00 |
7. Ejercicios#
Usá el DataFrame df creado en esta clase para responder las siguientes preguntas:
Ejercicio 1#
¿Cuántos beneficiarios hay en total por provincia? Mostrá el resultado ordenado de mayor a menor.
# Tu solución aquí
Ejercicio 2#
Filtrá el DataFrame para quedarte solo con beneficiarios del programa «Potenciar Trabajo» que no tienen empleo formal. ¿Cuál es su ingreso mensual promedio?
# Tu solución aquí
Ejercicio 3#
Creá una nueva columna "alto_monto" que sea True si monto_transferencia > 40000 y False en caso contrario. ¿Qué porcentaje de beneficiarios recibe un monto alto?
# Tu solución aquí