Este bloque muestra cómo crear y manipular distintos tipos de objetos básicos en Python: tuplas, listas, diccionarios, conjuntos y funciones personalizadas. También se presentan algunos ejemplos de comprensión de listas y funciones que retornan múltiples valores.
import os
import rootbc as r
from datetime import datetime as dt
# Tupla
tup = 1, 2, 3
tup
## (1, 2, 3)
# Iterar sobre una lista de tuplas
seq = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
for a, b, c in seq:
print('a={0}, b={1}, c={2}'.format(a, b, c))
## a=1, b=2, c=3
## a=4, b=5, c=6
## a=7, b=8, c=9
# Lista y slicing
lista = [4, 5, 6]
lista
## [4, 5, 6]
lista[0:1]
## [4]
# Diccionario y acceso/modificación
dc = {'a': 'hello', 'b': [1, 2, 3]}
dc
## {'a': 'hello', 'b': [1, 2, 3]}
dc[4] = 'good bye'
dc
## {'a': 'hello', 'b': [1, 2, 3], 4: 'good bye'}
dc['b']
## [1, 2, 3]
# Conjuntos (eliminan duplicados)
{2, 2, 2, 1, 3, 3}
## {1, 2, 3}
set([2, 2, 2, 1, 3, 3])
## {1, 2, 3}
# Comprensión de listas
# Aplica una función a los elementos de una lista si cumplen alguna condición.
# Funciona como un filtro
strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
[x.upper() for x in strings if len(x) > 2]
## ['BAT', 'CAR', 'DOVE', 'PYTHON']
# Definición de una función simple
def suma(x, y):
return x + y
suma(2, 5)
## 7
# Función con múltiples outputs
def suma2(x, y):
s = x + y
m = x * y
return s, m
a, b = suma2(2, 5)
a
## 7
b
## 10
Este bloque muestra cómo utilizar bucles for-in
y
while
, junto con estructuras condicionales if
,
elif
, else
para controlar el flujo del
programa.
# Bucle for simple
a = [0, 1, 2, 3, 4]
for x in a:
print(x)
## 0
## 1
## 2
## 3
## 4
# Condicionales en un for
for x in a:
if x % 2 == 0:
print('{0} is even'.format(x))
else:
print('{0} is odd'.format(x))
## 0 is even
## 1 is odd
## 2 is even
## 3 is odd
## 4 is even
# Uso de elif
for x in a:
if x == 0:
print('{0} is zero'.format(x))
elif x % 2 == 0:
print('{0} is even'.format(x))
else:
print('{0} is odd'.format(x))
## 0 is zero
## 1 is odd
## 2 is even
## 3 is odd
## 4 is even
# Bucle while
# En este caso x debe ir cambiando dentro del loop
# para cumplir una condición y así poder detenerse
x = 4
i = 0
while (x >= 0):
i = i + 1
print('In iter {0}: x = {1}'.format(i, x))
x = x - 1
## In iter 1: x = 4
## In iter 2: x = 3
## In iter 3: x = 2
## In iter 4: x = 1
## In iter 5: x = 0
Una clase en Python es un tipod de dato personalizado por el usuario
(antes vimos int, float, strings). Aquí se
define una clase llamada Persona
con atributos como nombre,
DNI y edad, junto con métodos personalizados para representar el objeto
y extraer iniciales del nombre.
class Persona:
def __init__(self, nombre, dni, edad):
self.nombre = nombre
self.dni = dni
self.edad = edad
def iniciales(self):
cadena = ''
for c in self.nombre.title():
if c >= 'A' and c <= 'Z':
cadena = cadena + c + '. '
return cadena
def __str__(self):
cadena = 'Nombre: {0}\n'.format(self.nombre)
cadena += 'D.N.I.: {0}\n'.format(self.dni)
cadena += 'Edad: {0}\n'.format(self.edad)
return cadena
juan = Persona('Juan Perez', 52123654, 15)
print(juan)
## Nombre: Juan Perez
## D.N.I.: 52123654
## Edad: 15
print('Nombre: {0} \nDNI: {1} \nEdad: {2}'.format(juan.nombre, juan.dni, juan.edad))
## Nombre: Juan Perez
## DNI: 52123654
## Edad: 15
print('Iniciales: {0}'.format(juan.iniciales()))
## Iniciales: J. P.
peter = Persona('pedro paz', 53356987, 20)
print('Iniciales: {0}'.format(peter.iniciales()))
## Iniciales: P. P.
NumPy es uno de los paquetes más importantes para el cálculo numérico en Python.
import numpy as np
my_arr = np.arange(10) # arange es la funcion range de Numpy
my_arr # Vector
## array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
matriz = np.array([[1.5, -0.1, 3], [0, -3, 6.5]])
matriz
## array([[ 1.5, -0.1, 3. ],
## [ 0. , -3. , 6.5]])
matriz.shape
## (2, 3)
matriz.dtype
## dtype('float64')
np.eye(3)
## array([[1., 0., 0.],
## [0., 1., 0.],
## [0., 0., 1.]])
array
: Convierte datos de entrada (lista, tupla, matriz
u otro tipo de secuencia) en un ndarray
arange
: Como el comando range, pero devuelve un
ndarray
en lugar de una lista
ones
: Produce una matriz de todos los 1s con la forma y
el tipo de datos dados
zeros
: Igual que el anterior pero genera 0s.
empty
: Crea nuevas matrices asignando nueva memoria,
pero no las rellenes con ningún valor
eye
, identity
: Crea una matriz identidad
cuadrada N × N (1 en la diagonal y 0 en el resto)
arr = np.arange(10)
arr
## array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr[5]
## np.int64(5)
arr[5:8]
## array([5, 6, 7])
arr[5:8] = 12
arr
## array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
Una primera distinción importante con respecto a las listas integradas de Python es que las segmentaciones de un array son vistas del array original. Esto significa que los datos no se copian y cualquier modificación en la vista se reflejará en el array de origen.
arr_slice = arr[5:8]
arr_slice
## array([12, 12, 12])
arr_slice[1] = 12345
arr
## array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8,
## 9])
arr_slice[:] = 64
arr
## array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
Para evitar este comportamiento exite la posibilidad de generar una copia
arr = np.arange(10)
arr_copy = arr.copy()
arr_copy[1] = 12345
arr
## array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d
## array([[1, 2, 3],
## [4, 5, 6],
## [7, 8, 9]])
arr2d[2]
## array([7, 8, 9])
Se puede acceder a elementos individuales recursivamente
arr2d[0][2]
. Sin embargo, esto es demasiado trabajo, por lo
que se puede pasar una lista de índices separados por comas para
seleccionar elementos individuales.
arr2d[0][2]
## np.int64(3)
arr2d[0, 2]
## np.int64(3)
También se puede cortar a lo largo del eje 0 (el primer eje, es
decir, las filas). Por lo tanto, un corte selecciona un rango de
elementos a lo largo de un eje. Puede ser útil interpretar la expresión
arr2d[:2]
como “seleccionar las dos primeras filas de
arr2d”.
arr2d[:2]
## array([[1, 2, 3],
## [4, 5, 6]])
Alternativamente:
arr2d[:2, 1:]
## array([[2, 3],
## [5, 6]])
arr = np.arange(15).reshape((3, 5))
arr
## array([[ 0, 1, 2, 3, 4],
## [ 5, 6, 7, 8, 9],
## [10, 11, 12, 13, 14]])
arr.T
## array([[ 0, 5, 10],
## [ 1, 6, 11],
## [ 2, 7, 12],
## [ 3, 8, 13],
## [ 4, 9, 14]])
np.random.seed(1234) # Fijar semilla
samples = np.random.standard_normal(size=(4, 3))
samples
## array([[ 0.47143516, -1.19097569, 1.43270697],
## [-0.3126519 , -0.72058873, 0.88716294],
## [ 0.85958841, -0.6365235 , 0.01569637],
## [-2.24268495, 1.15003572, 0.99194602]])
arr = np.arange(10)
arr
## array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.sqrt(arr)
## array([0. , 1. , 1.41421356, 1.73205081, 2. ,
## 2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
np.exp(arr)
## array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
## 5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
## 2.98095799e+03, 8.10308393e+03])
np.log(arr) # logaritmo natural (base e)
## array([ -inf, 0. , 0.69314718, 1.09861229, 1.38629436,
## 1.60943791, 1.79175947, 1.94591015, 2.07944154, 2.19722458])
# Forma alternativa de generar numeros aleatorios
# Más moderno, controlado y recomendable para proyectos grandes o reproducibles
rng = np.random.default_rng(5678) # Crear generador con semilla
arr = rng.standard_normal((3, 3))
arr
## array([[-0.31156563, 0.66906888, 1.17536046],
## [-0.67848623, 0.51281091, -0.5091413 ],
## [-0.68022347, 0.0724691 , -1.72629606]])
arr.mean()
## np.float64(-0.1640003711947179)
np.mean(arr)
## np.float64(-0.1640003711947179)
arr.sum()
## np.float64(-1.4760033407524609)
arr.mean(axis=1)
## array([ 0.51095457, -0.22493887, -0.77801681])
(arr > 0).sum() # cantidad de valores positivos
## np.int64(4)
names = np.array(["Bob", "Will", "Joe", "Bob", "Will", "Joe", "Joe"])
np.unique(names)
## array(['Bob', 'Joe', 'Will'], dtype='<U4')
x = np.array([[1., 2., 3.], [4., 5., 6.]])
y = np.array([[6., 23.], [-1, 7], [8, 9]])
x
## array([[1., 2., 3.],
## [4., 5., 6.]])
y
## array([[ 6., 23.],
## [-1., 7.],
## [ 8., 9.]])
np.dot(x, y)
## array([[ 28., 64.],
## [ 67., 181.]])
En esta sección se utiliza el paquete pandas
que
contiene estructuras de datos y herramientas de manipulación de datos
diseñadas para que la limpieza y el análisis de datos sean rápidos.
se leen archivos Excel, se realiza el tratamiento típico de una base de datos: merge, renombrar variables, generar nuevas, eliminar columnas, agrupar, filtrar y reshaping.
import pandas as pd
os.chdir(r.path + '/OneDrive - BCRA/Python')
df = pd.read_excel('datos_WB.xlsx', sheet_name='1')
df.describe()
## year gdp_pc2015 ... imports popu
## count 90.000000 90.000000 ... 90.000000 9.000000e+01
## mean 2017.000000 24964.154669 ... 25.482920 7.648676e+07
## std 4.344698 13881.889348 ... 8.272329 5.975849e+07
## min 2010.000000 8435.011433 ... 11.780574 1.718146e+07
## 25% 2013.000000 12895.438247 ... 15.734811 4.438039e+07
## 50% 2017.000000 22024.599515 ... 28.936204 6.154354e+07
## 75% 2021.000000 36866.008807 ... 31.813062 6.754635e+07
## max 2024.000000 47682.761535 ... 39.594989 2.119986e+08
##
## [8 rows x 8 columns]
meta = pd.read_excel('datos_WB.xlsx', sheet_name='2')
# Merge entre bases
df = pd.merge(df, meta , on='ccode', how='left')
# Renombrar
df.rename(columns={'popu': 'poblacion'}, inplace=True)
# Generar variable
df['open'] = df['exports'] + df['imports']
# Eliminar columnas
df.drop(['gdp_pc2015','poblacion','exports','imports'], axis=1, inplace=True)
# Info general
df.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 90 entries, 0 to 89
## Data columns (total 8 columns):
## # Column Non-Null Count Dtype
## --- ------ -------------- -----
## 0 year 90 non-null int64
## 1 cname 90 non-null object
## 2 ccode 90 non-null object
## 3 gdp_pc2021 90 non-null float64
## 4 credit_ps 89 non-null float64
## 5 inv 90 non-null float64
## 6 region 90 non-null object
## 7 open 90 non-null float64
## dtypes: float64(4), int64(1), object(3)
## memory usage: 5.8+ KB
# Filtrar años posteriores a 2022
df2 = df[df.year > 2022]
# Agrupar por región y año, promediando variables
df3 = df2.groupby(['region','year']).mean(numeric_only=True).round(1)
df3
## gdp_pc2021 credit_ps inv open
## region year
## Europe & Central Asia 2023 53082.2 98.7 21.1 67.1
## 2024 53366.0 94.0 20.6 64.3
## Latin America & Caribbean 2023 25249.5 65.3 19.7 40.3
## 2024 25459.2 64.8 18.8 42.5
df3.reset_index(inplace=True)
df3
## region year gdp_pc2021 credit_ps inv open
## 0 Europe & Central Asia 2023 53082.2 98.7 21.1 67.1
## 1 Europe & Central Asia 2024 53366.0 94.0 20.6 64.3
## 2 Latin America & Caribbean 2023 25249.5 65.3 19.7 40.3
## 3 Latin America & Caribbean 2024 25459.2 64.8 18.8 42.5
# Pivot (reshape): años como columnas
df3.pivot_table(index='region', columns='year', values='gdp_pc2021').reset_index().round(1)
## year region 2023 2024
## 0 Europe & Central Asia 53082.2 53366.0
## 1 Latin America & Caribbean 25249.5 25459.2
Este bloque muestra cómo generar un gráfico de líneas con
pyplot
de matplotlib
, con etiquetas, colores,
título y guardado de imagen a archivo.
import matplotlib.pyplot as plt
AR = df.loc[:, ('ccode', 'year', 'gdp_pc2021')]
AR = AR[AR.ccode == 'ARG']
plt.figure(figsize=(10, 4))
plt.plot(AR.year, AR.gdp_pc2021, color='red', linestyle='--', linewidth=2, marker='o', label='GDPpc')
plt.xlabel('')
plt.ylabel('GDP pc PPP (constant 2021 int. $)')
plt.title('Argentina')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
plt.savefig('arg_gdp.png')