Más Contenedores, Condiciones y Más Loops#

  • zip de 2 variables List

  • Variables de tipo Dict

  • if ... elif ... else

  • bool, and y or

  • la instrucción while

Resolvamos el problema propuesto en la Tarea 1.

zip de 2 List#

Consideremos las variables de tipo List donde están almacenados los flujos y los plazos de los flujos.

flujos = [1, 1, 1, 1, 1, 1, 1, 1, 1, 101]
plazos = [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]

Ahora, podemos juntar las List notas y nombres usando la función zip.

El zip de 2 List es Iterable y podemos, por lo tanto, recorrer sus elementos con una instrucción for.

for fp in zip(flujos, plazos):
    print(fp)
(1, 182)
(1, 365)
(1, 547)
(1, 730)
(1, 912)
(1, 1095)
(1, 1277)
(1, 1460)
(1, 1642)
(101, 1825)

Notar que los resultados de cada print aparecen entre (...). Esto indica que cada elemento del zip es una Tuple.

También se puede hacer de la siguiente forma:

for f, p in zip(flujos, plazos):
    print(f, p)
1 182
1 365
1 547
1 730
1 912
1 1095
1 1277
1 1460
1 1642
101 1825

De esta forma, en cada paso del for, el flujo y plazo correspondiente se almacenan separadamente (sin uasr una Tuple).

Ojo: al hacer zip de List tenemos que estar atentos al largo de las List. Si hacemos zip de 2 List de largo distinto, zip considerará el largo de la List más corta.
lista1 = [1, 2, 3]
lista2 = ['a', 'b', 'c', 'd']
for ll in zip(lista1, lista2):
    print(ll)
(1, 'a')
(2, 'b')
(3, 'c')

Gracias a la instrucción zip podemos calcular el valor presente de los flujos utilizando el for.

vp = 0.0     # <--- en esta variable se almacenará el resultado del cálculo
tasa = .02   # <--- este es el valor de la tasa de descuento
for f, p in zip(flujos, plazos):
    vp += f * (1 + tasa)**(-p / 365) # vp = vp + f * (1 + tasa)**(-p / 365)
print(f'El valor presente es: {vp:,.4f}')
El valor presente es: 100.0470

En el loop anterior hay dos elementos nuevos:

  • La expresión a += b es equivalente a escribir a = a + b que, a su vez, quiere decir que el nuevo valor de la variable a es el actual valor de a más el valor de la variable b. En el caso anterior, el nuevo valor de vp es el valor actual de vp más el valor presente del flujo f. Podemos apreciar como vp es una variable en donde vamos sumando (acumulando) los valores presentes de cada flujo.

  • ** representa elevar a una potencia, como ^ en Excel.

Pregunta#

La tasa de cupón del bono es 2.00% y la tasa de descuento es 2.00% ¿Porqué no está perfectamente a la par (vp = 100)?

Respuesta: por la diferencia en la convención de la tasa de cupón y de la tasa de descuento.

Variables de Tipo Dict#

En el ejemplo anterior tenemos los flujos de un único bono, sea éste bono_1. ¿Cómo podemos almacenar flujos de distintos bonos y posteriormente identificarlos con facilidad? Por ejemplo, lo más usual sería usar como identificador el nemotécnico del bono. Para eso sirven las variables de tipo Dict.

nombre_edad = {
    'alvaro': 52,
    'thomas': 26,
    'daniel': 36
}
nombre_edad_peso = {
    'alvaro': [52, 94],
    'thomas': [26, 80]
}
dict_bonos = {
    "bono_1": [
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 101],
        [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]
    ],
    "bono_2": [
        [2, 2, 2, 2, 2, 2, 2, 2, 2, 102],
        [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]
    ]
}

Un dict va entre {} y cada elemento del Dict tiene la forma <identificador>: <valor>. En este caso, el identificador de cada elemento es el nemotécnico del bono (variable de tipo str) y el valor de cada elemento es una List que, a su vez, contiene una List con los flujos y una List con los plazos de los bonos.

Cómo se Usa un Dict#

nombre_edad['alvaro']
52
dict_bonos['bono_1'] # los flujos y plazos del bono_1
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 101],
 [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]

Si queremos sólo los flujos del bono_1:

dict_bonos['bono_1'][0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 101]

Los datos del bono_2.

dict_bonos['bono_2'] # las notas de Física
[[2, 2, 2, 2, 2, 2, 2, 2, 2, 102],
 [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]

Sólo los plazos del bono_2.

dict_bonos['bono_2'][1]
[182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]

Un Dictes una estructura de datos mutable. Si aparece un nuevo bono, podemos agregarlo, por ejemplo:

dict_bonos['bono_3'] = [
    [3, 3, 3, 3, 3, 3, 3, 3, 3, 103],
    [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]
]
dict_bonos
{'bono_1': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 101],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]],
 'bono_2': [[2, 2, 2, 2, 2, 2, 2, 2, 2, 102],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]],
 'bono_3': [[3, 3, 3, 3, 3, 3, 3, 3, 3, 103],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]}
dict_bonos['bono_3'][0][9] = 104
dict_bonos
{'bono_1': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 101],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]],
 'bono_2': [[2, 2, 2, 2, 2, 2, 2, 2, 2, 102],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]],
 'bono_3': [[3, 3, 3, 3, 3, 3, 3, 3, 3, 104],
  [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]}

Un Dict también es Iterable, veamos que pasa cuando lo usamos en un for loop.

for x in dict_bonos:
    print(x)
bono_1
bono_2
bono_3
for x in dict_bonos:
    print(dict_bonos[x])
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 101], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
[[2, 2, 2, 2, 2, 2, 2, 2, 2, 102], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
[[3, 3, 3, 3, 3, 3, 3, 3, 3, 104], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]

Vemos que x es el valor de los identificadores del Dict, en este caso, los nemotécnicos de los bonos.

Para obtener los valores del Dict usamos lo siguiente:

for x in dict_bonos.values():
    print(x)
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 101], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
[[2, 2, 2, 2, 2, 2, 2, 2, 2, 102], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
[[3, 3, 3, 3, 3, 3, 3, 3, 3, 104], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]

Para obtener los identificadores (keys) y valores (values) usamos lo siguiente:

for x in dict_bonos.items():
    print(x)
('bono_1', [[1, 1, 1, 1, 1, 1, 1, 1, 1, 101], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]])
('bono_2', [[2, 2, 2, 2, 2, 2, 2, 2, 2, 102], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]])
('bono_3', [[3, 3, 3, 3, 3, 3, 3, 3, 3, 104], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]])
for k, v in dict_bonos.items():
    print(k, v)
bono_1 [[1, 1, 1, 1, 1, 1, 1, 1, 1, 101], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
bono_2 [[2, 2, 2, 2, 2, 2, 2, 2, 2, 102], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]
bono_3 [[3, 3, 3, 3, 3, 3, 3, 3, 3, 104], [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]]

O también:

for k, v in dict_bonos.items():
    print(f'nemotécnico: {k},\nflujos: {v[0]}\nplazos: {v[1]}\n') # <--- \n significa que lo que sigue empieza
                                                                  # en una nueva línea. Es como apretar Enter.
nemotécnico: bono_1,
flujos: [1, 1, 1, 1, 1, 1, 1, 1, 1, 101]
plazos: [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]

nemotécnico: bono_2,
flujos: [2, 2, 2, 2, 2, 2, 2, 2, 2, 102]
plazos: [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]

nemotécnico: bono_3,
flujos: [3, 3, 3, 3, 3, 3, 3, 3, 3, 104]
plazos: [182, 365, 547, 730, 912, 1095, 1277, 1460, 1642, 1825]

Ejercicio#

Escribir una función que tenga como argumento una variable como dict_bonos y una tasa de descuento y devuelva un Dict donde el identicador es el nemotécnico del bono y el valor es el valor presente del bono.

Primero vamos a definir una función que calcula el valor presente de sólo 1 bono. Aprovechamos de introducir la docstring de una función.

def vp_bono(flujos, plazos, tasa):
    """
    Calcula el valor presente de un bono dada una tasa de descuento.
    
    Parameters
    ----------
    
    flujos: List[float]
            Contiene los flujos del bono.
            
    plazos: List[float]
            Contiene los plazos residuales de los flujos expresados en días.
    
    tasa: float
          Tasa de descuento del bono. Debe ser en convención Com Act/365.
          
    Returns
    -------
    
    float
         El valor presente del bono.
    """
    result = 0.0
    for f, p in zip(flujos, plazos):
        result += f * (1 + tasa)**(-p / 365.0)
    return result

El docstring de la función anterior está en formato numpy. Hay un par más (ver) y se tiene libertad para escoger el que más te gusta. Es importante, eso sí, que una vez que se ha escogido un estilo, mantener consistencia en su uso.

El uso del docstring me permite investigar una función antes de usarla. Esto es muy útil para funciones de paquetes y librerías externas e incluso para nuestras propias funciones.

print(vp_bono.__doc__)
    Calcula el valor presente de un bono dada una tasa de descuento.
    
    Parameters
    ----------
    
    flujos: List[float]
            Contiene los flujos del bono.
            
    plazos: List[float]
            Contiene los plazos residuales de los flujos expresados en días.
    
    tasa: float
          Tasa de descuento del bono. Debe ser en convención Com Act/365.
          
    Returns
    -------
    
    float
         El valor presente del bono.
    

Probemos la función usando las variables definidas al inicio de este notebook.

vp = vp_bono(flujos, plazos, tasa)
print(f'Valor presente: {vp:,.04f}')
Valor presente: 100.0470

Ahora, con esta función, podemos definir una función que valorice muchos bonos.

def vp_bonos(bonos, tasa):
    """
    Calcula el valor presente de un conjunto de bonos con una tasa de descuento dada.
      
    Parameters
    ----------
    
    bonos: Dict[str, List[List[float], List[float]]]
          Las `keys` del `Dict` son los nemotécnicos del bono o cualquier otro identificador único, como el CUSIP.
          Los `values` son una `List` que, a su vez, contiene dos `List`, una con los flujos del bono en el índice
          0 y otra con los plazos residuales de los flujos en días en el índice 1.
          
    tasa: float
          Tasa de descuento, debe estar en convención Com Act/365.
          
    Returns
    -------
    
    Dict[str, float]
        Los `keys` son los mismos del parámetro `bono` y los `values` son los valores presente de los bonos.
    """
    result = {} # Dict vacío
    for k, v in bonos.items():
        result[k] = vp_bono(v[0], v[1], tasa)
    return result

Probemos con el Dict dict_bonos y la variable tasa definida al prinicpio del notebook.

vps = vp_bonos(dict_bonos, tasa)
for k, v in vps.items():
    print(f'El valor presente de {k} es: {v:,.04f}')
El valor presente de bono_1 es: 100.0470
El valor presente de bono_2 es: 109.5210
El valor presente de bono_3 es: 119.9007

Tarea#

Hacer todos los cambios que estimen necesarios para poder valorizar dict_bonos usando una tasa de descuento distinta para cada bono.

La Instrucción if ... elif ... else#

Muchas veces, la función que estamos programando requiere hacer una distinción, o tomar un decisión en función de una o más variables. Veamos un ejemplo muy sencillo. La función

\[abs: \mathbb{R} \rightarrow \mathbb{R}^+\]

se define de la siguiente forma \(abs(x)=x\) si \(x\geq0\) y \(abs(x)=-x\) si \(x<0\). Para programar esta función, necesitamos una forma de distinguir si si \(x\) es positivo (o cero) o negativo.

La instrucción if ... else#

Veamos como hacerlo, poner mucha atención en la indentación que viene debajo de los if y else.

def abs(x):
    if x < 0:
        return -x
    else:
        return x

Probemos …

print(f"El valor absoluto de {2} es: {abs(2)}")
print(f"El valor absoluto de {-2} es: {abs(-2)}")
El valor absoluto de 2 es: 2
El valor absoluto de -2 es: 2

¡Bien! El resultado es el esperado. Existe también una forma breve de escribir esta instrucción. Esta forma breve se puede utilizar en casos sencillos como el anterior.

def abs2(x):
    return x if x >= 0 else -x

Probemos esta también … (para recordar, aquí usamos print con format)

print("El valor absoluto de {} es: {}".format(2, abs2(2)))
print("El valor absoluto de {} es: {}".format(-2, abs2(-2)))
El valor absoluto de 2 es: 2
El valor absoluto de -2 es: 2

La instrucción if ... elif ... else#

En ocasiones, debemos distinguir entre más de dos alternativas. Por ejemplo, supongamos que queremos clasificar los bonos de una cartera en las categorías ‘SUBPAR’ si el bono se transa por debajo de 98, ‘PAR’ si el bono se transa entre 98 y 102 y ‘SOBREPAR’ si el bono se transa por sobre 102.

Definimos una función que acepta un número que representa el precio del bono y retorna un str con la categoría del bono. Este ejemplo nos permite introducir, además, el operador lógico and, con el cual se puede verificar si 2 condiciones son verdaderas al mismo tiempo.

def categoria(precio):
    if precio < 98:
        return "SUBPAR"
    elif precio >= 98 and precio < 102: # <--- Notar el conector lógico and. Más detalle en lo que sigue.
        return "PAR"
    else:
        return "SOBREPAR"

Probemos …

precios = [96, 97, 98, 99, 100, 101, 102, 103]
for precio in precios:
    print(f"El bono de precio {precio:.2f} está en la categoría: {categoria(precio)}")
El bono de precio 96.00 está en la categoría: SUBPAR
El bono de precio 97.00 está en la categoría: SUBPAR
El bono de precio 98.00 está en la categoría: PAR
El bono de precio 99.00 está en la categoría: PAR
El bono de precio 100.00 está en la categoría: PAR
El bono de precio 101.00 está en la categoría: PAR
El bono de precio 102.00 está en la categoría: SOBREPAR
El bono de precio 103.00 está en la categoría: SOBREPAR

Se puede utilizar más de un elif, por ejemplo, supongamos que necesitamos establecer otra categoría más y dejar las cosas de esta manera:

  • Menor a 98: “SUBPAR”

  • Mayor o igual 98 y menor que 101: “PAR”

  • Mayor o igual a 101 y menor a 103: “SOBREPAR”

  • Mayor o igual a 103: “MUYSOBREPAR”

Esta categoría se podría implementar de la siguiente forma:

def categoria2(preco):
    if precio < 98:
        return "SUBPAR"
    elif precio >= 98 and precio < 101:
        return "PAR"
    elif precio >= 101 and precio < 103:
        return "SOBREPAR"
    else:
        return "MUYSOBREPAR"
for precio in precios:
    print(f"El bono de precio {precio:.2f} está en la categoría: {categoria2(precio)}")
El bono de precio 96.00 está en la categoría: SUBPAR
El bono de precio 97.00 está en la categoría: SUBPAR
El bono de precio 98.00 está en la categoría: PAR
El bono de precio 99.00 está en la categoría: PAR
El bono de precio 100.00 está en la categoría: PAR
El bono de precio 101.00 está en la categoría: SOBREPAR
El bono de precio 102.00 está en la categoría: SOBREPAR
El bono de precio 103.00 está en la categoría: MUYSOBREPAR

Valores bool y los Operadores Lógicos and y or#

El Tipo bool#

Hasta ahora hemos visto los tipos de variable int, float y str. Existe otro tipo de variable primitiva, las variables de tipo bool. Estas variables sólo pueden asumir dos valores True o False y aparecen al momento de verificar una condición. De hecho, al verificar una condición, Python retorna una variable de tipo bool. Veamos un par de ejemplos:

2 > 1
True
2 < 1
False

Cuando queremos verificar si dos valores son iguales, se utiliza el símbolo ==, el símbolo = sólo se utiliza para asignar un valor a una variable. Por ejemplo:

a = 3  # asignación
b = 4  # asignación
a == b # comparación
False

El resultado de una comparación, puede almacenarse en una variable.

c = (a == b) # se asigna a c el resultado (bool) de a == b
print("El valor de c es {}".format(c))
El valor de c es False

Operador and#

Al ejecutar condicion1 and condicion2 se obtendrá True si y sólo si ambas condiciones son True.

precio = 101
print("precio: {}".format(precio))
print("¿precio >= 100: {}".format(precio >= 100))
print("¿precio < 102?: {}".format(precio < 102))
print("¿precio >= 100 and precio < 102?: {}".format(precio >= 100 and precio < 102))
precio: 101
¿precio >= 100: True
¿precio < 102?: True
¿precio >= 100 and precio < 102?: True
print("precio: {}".format(precio))
print("¿precio >= 100: {}".format(precio >= 100))
print("¿precio < 101?: {}".format(precio < 101))
print("¿precio >= 100 and precio < 101?: {}".format(precio >= 100 and precio < 101))
precio: 101
¿precio >= 100: True
¿precio < 101?: False
¿precio >= 100 and precio < 101?: False

Operador or#

Al ejecutar condicion1 or condicion2 se obtendrá False si y sólo si ambas condiciones son False.

print("precio: {}".format(precio))
print("¿precio >= 100: {}".format(precio >= 100))
print("¿precio < 101?: {}".format(precio < 101))
print("¿precio >= 100 or precio < 101?: {}".format(precio >= 100 or precio < 101))
precio: 101
¿precio >= 100: True
¿precio < 101?: False
¿precio >= 100 or precio < 101?: True
print("precio: {}".format(precio))
print("¿precio < 101: {}".format(precio < 101))
print("¿precio >= 102?: {}".format(precio >= 102))
print("¿precio < 101 or precio >= 102?: {}".format(precio < 101 or precio >= 102))
precio: 101
¿precio < 101: False
¿precio >= 102?: False
¿precio < 101 or precio >= 102?: False

Ejercicio#

Una universidad ofrece una beca si:

  • El NEM del alumno es superior a 5.5 y tu ingreso familiar es menor a 1.2 millones de CLP (pesos chilenos) o

  • El NEM del alumno es superior a 6.0 y tu ingreso familiar es menor a 1.5 millones de CLP (pesos chilenos) o

  • El NEM del alumno es superior a 6.5

Escribe una función que tenga el NEM y el ingreso familiar como variables y retorne un bool que indique si es eligible para la beca (True eres eligible, False no eres elegible).

def es_elegible(nem: float, ingreso: float) -> bool: # type anotations
    """
    Indica si un alumno es elegible para beca.
    
    Parameters
    ----------
    
    nem: float
        Promedio de Notas Enseñanza Media
        
    ingreso: float
        Ingreso familiar mensual en pesos.
        
    Returns
    -------
    
    `True` si es elegible `False` en caso contrario.
    """
    if nem >= 5.5 and ingreso < 1200000:
        return True
    elif nem >= 6.0 and ingreso < 1500000:
        return True
    elif nem >= 6.5:
        return True
    else:
        return False
nem = 6.0
ingreso = 1300000
es_elegible(nem, ingreso)
True
if (nem >= 5.5 and ingreso < 1200000) or (nem >= 6.0 and ingreso < 1500000) or (nem >= 6.5):
    print(True)
else:
    print(False)
True

La Instrucción while#

El uso de while permite ejecutar un grupo de instrucciones hasta que una condición sea verdadera.

Ejemplo#

En este caso, se mostrarán por pantalla los números del 0 al 9.

  • Se inicializa el valor de la variable i.

  • Comienza while, se evalúa si i < 10, las instrucciones dentro del while se ejecutarán sólo si i < 10 = True.

  • Se muestra por pantalla el valor de i

  • Se incrementa el valor de i en 1 (la instrucción i += 1 es equivalente a i = i + 1)

  • Se vuelve a ejecutar …

i = 0
while i < 10:
    print(f'El valor de i es {i}')
    i += 1
El valor de i es 0
El valor de i es 1
El valor de i es 2
El valor de i es 3
El valor de i es 4
El valor de i es 5
El valor de i es 6
El valor de i es 7
El valor de i es 8
El valor de i es 9

La Instrucción continue#

La instrucción continue permite volver anticipadamente al inicio del while. En este caso, cuando i == 3, no se muestra el valor de i por pantalla y se vuelve al inicio.

i = 0
while i < 10:
    if i == 3:
        i += 1
        continue
    print(f'El valor de i es {i}')
    i += 1
El valor de i es 0
El valor de i es 1
El valor de i es 2
El valor de i es 4
El valor de i es 5
El valor de i es 6
El valor de i es 7
El valor de i es 8
El valor de i es 9

Continuar la Ejecución Hasta que el Usuario la Interrumpa#

En este ejemplo, el while se ejecuta siempre y sólo se interrumpe cuando el usuario ingresa la letra 'q'. Para este ejemplo utilizamos una nueva función: input. Esta función permite capturar input del usuario y procesarlo.

while True: # dado que True == True siempre, el while siempre se ejecuta
    letra = input('Elige una letra ...')
    if letra == 'q':
        print('Esa sí.')
        break
    else:
        print('\tNop, esa no ...')
        continue
print('\nFuera del while.')
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[52], line 2
      1 while True: # dado que True == True siempre, el while siempre se ejecuta
----> 2     letra = input('Elige una letra ...')
      3     if letra == 'q':
      4         print('Esa sí.')

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Ejercicio#

Para este ejercicio, vas a necesitar la siguiente función: randint

  • Primero se debe importar el módulo random. Un módulo puede pensarse como programas que otros han escrito y que podemos agregar a nuestros propios programas.

  • random es un módulo de la librería estándar de Python, eso quiere decir que siempre está disponible.

  • Para agregarlo a nuestros programas, se ejecuta la siguiente instrucción

import random as rnd

Del módulo random vamos a utilizar la función randint(i, j) que genera un número entero aleatorio entre i y j (ambos inclusive).

Cada vez que se ejecuta esta celda, se obtiene un número entero nuevo de forma aleatoria.

rnd.randint(1, 10)
10

Enunciado del Ejercicio#

Vas a programar un juego muy sencillo utilizando while y la función randint.

  • El computador elige un número entero aleatorio, que llamaremos num_secreto, en un rango predeterminado.

  • Tú debes tratar de adivinarlo

  • Si lo adivinas ganas y tu puntaje es el número de veces que tuviste que jugar para adivinar num_secreto

  • Si no lo adivinas, el computador te responderá si el número que dijiste es más alto o más bajo que num_secreto y sigues jugando

  • Mientras más bajo tu puntaje, mejor

Tip: el resultado de input es un str

x = input()
x
print(type(x))
<class 'str'>

Para convertirlo a int hacer lo siguiente:

x = int(x)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-66-6ed577bb1378> in <module>
----> 1 x = int(x)

ValueError: invalid literal for int() with base 10: 'x'
type(x)

Solución#

def jugar():
    """
    """
    num_secreto = rnd.randint(1, 51)
    while True:
        guess = input("Adivina qué número elegí (entre 1 y 50 inclusive):")
        try:
            int_guess = int(guess)
            if num_secreto == int(guess):
                return "Ganaste."
            else:
                if int(guess) > num_secreto:
                    print("Tú número es mayor ...")
                else:
                    print("Tu número es menor ...")
        except NameError:
            print("Tienes que ingresar un número entero.")
    return "Game Over"

Hacer otra versión con isdecimal().

jugar()
Adivina qué número elegí (entre 1 y 50 inclusive):r
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-84-e61132de9759> in <module>
----> 1 jugar()

<ipython-input-83-e889f314c381> in jugar()
      6         guess = input("Adivina qué número elegí (entre 1 y 50 inclusive):")
      7         try:
----> 8             int_guess = int(guess)
      9             if num_secreto == int(guess):
     10                 return "Ganaste."

ValueError: invalid literal for int() with base 10: 'r'

Encuentra la TIR de un Bono#

Supongamos que tenemos un bono que está transando a un valor presente de 101 y queremos calcular cuál es su TIR de mercado. O sea, queremos determinar qué tasa de descuento hace que el valor presente del bono sea 101. Veamos como hacerlo utilizando los flujos y plazos definidos al principio del notebook.

flujos
plazos

Tenemos que resolver la ecuación:

\[VP\left(TIR\right)-P=0\]

Para esto vamos a programar un mini BuscarObjetivo totalmente similar al que usaríamos si resolviéramos este problema con Excel. Utilizaremos el método de Newton-Raphson que nos dice que podemos encontrar la solución a la ecuación anterior si iterativamente calculamos:

\[TIR_{n+1}=TIR_n - \frac{VP\left(TIR_n\right)-P}{VP'\left(TIR_n\right)}\]

Aquí \(V'\left(TIR_n\right)\) es la derivada del valor presente respecto a \(TIR_n\).

La derivada del valor presente la calcularemos de forma numérica con la siguiente aproximación:

\[VP'\left(TIR_n\right)\approx\frac{VP\left(TIR_n+h\right)-VP\left(TIR_n-h\right)}{2h}\]
def der_vp_bono(flujos, plazos, tasa):
    """
    Calcula la derivada del valor presente del bono respecto a la tasa usando la aproximación
    por diferencia central.
    
    VP'(tasa) = (VP(tasa + h) - VP(tasa - h)) / 2h
    
    Se utiliza un valor de h = .0001
    
    Parameters
    ----------
    
    flujos: List[float]
            Contiene los flujos del bono.
            
    plazos: List[float]
            Contiene los plazos residuales de los flujos expresados en días.
    
    tasa: float
          Tasa de descuento del bono. Debe ser en convención Com Act/365.
          
    Returns
    -------
    
    float
         La derivada del valor presente del bono.
    """
    h = .0001
    return (vp_bono(flujos, plazos, tasa + h) - vp_bono(flujos, plazos, tasa - h)) / (2 * h)

Realizamos un check:

h = .0001
vp = vp_bono(flujos, plazos, .02)
vp_mas = vp_bono(flujos, plazos, .02 + h)
vp_menos = vp_bono(flujos, plazos, .02 - h)
(vp_mas - vp_menos)/(2 * h)
der_vp_bono(flujos, plazos, .02) # f(x + h) = f(x) + f'(x)*h + error(h), error(h)->0 , h->0

Ahora definimos la función que encuentre la TIR para un cierto precio de mercado.

def encuentra_tir(flujos, plazos, vp, tasa):
    """
    Calcula la tasa de descuento o TIR que hace que el valor presente del bono (en base 100)
    sea igual a un valor dado.
    
    Parameters
    ----------
    
    flujos: List[float]
            Contiene los flujos del bono.
            
    plazos: List[float]
            Contiene los plazos residuales de los flujos expresados en días.
    
    vp: float
        Valor presente del bono.
          
    tasa: float
          Estimación inicial del valor del resultado.
          
    Returns
    -------
    
    float
         Tasa de descuento buscada.
    """
    epsilon = .000001
    diff = 1000
    while diff > epsilon:
        q = (vp_bono(flujos, plazos, tasa) - vp) / der_vp_bono(flujos, plazos, tasa)
        nueva_tasa = tasa - q
        diff = abs(nueva_tasa - tasa)
        tasa = nueva_tasa
    return tasa
tir = encuentra_tir(flujos, plazos, 104, .02)
print(f'El valor de la TIR es: {tir:.8%}')

Hagamos el check:

print(f'El valor presente con la TIR es: {vp_bono(flujos, plazos, tir):,.8f}')