Clase 4 — Pandas I: lectura y exploración de datos#

Python y Políticas Públicas


Contenidos#

  1. ¿Qué es pandas?

  2. Series y DataFrames

  3. Crear DataFrames desde cero

  4. Leer archivos: CSV, Excel, y otros formatos

  5. Exploración inicial de un dataset

  6. Selección básica de datos

  7. 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í