90UBA XXI, cátedra Balbiano

Pensamiento Computacional · Guía Segundo Parcial

UBA XXI · Cátedra Balbiano · Guía intensiva del 2do parcial, con ejercicios de parciales reales 2023 a 2025
1 Estrategia general y sintaxis clave

Cómo es el 2° parcial de PC

El 2° parcial es 100% de opción múltiple (una sola respuesta correcta por pregunta, que se vuelca en una grilla). Tiene entre 9 y 13 preguntas, en Python 3 (entorno Colab). Nunca se escribe un programa desde cero: siempre elegís entre 4 opciones, en uno de estos tres formatos.

Formato de preguntaQué te piden
"¿Qué imprime / qué devuelve / cómo queda el archivo?"Trazar la ejecución mentalmente y elegir la salida correcta
"¿Cuál de estas 4 versiones es la correcta?"Comparar 4 fragmentos casi iguales y detectar el que funciona
"¿Cuál es la línea que falta?" / "¿Qué estructura produce esta salida?"Elegir la línea, el dato o el archivo que completa el programa para que dé la salida pedida

Esto te da una ventaja enorme: el examen ya es opción múltiple igual que estos quizzes. Practicar trazando código es exactamente lo que vas a hacer el día del parcial.

Los 6 temas que entran (unidades 4-6)

Si la pregunta tiene...Es...
{clave: valor}, d[k], .get(), .items()TIPO 1: Diccionarios
map(, filter(, list(map(...))TIPO 2: map / filter
open(, readlines(), write(), modos 'r'/'w'/'a'TIPO 3: Archivos
try: / except:, validar input()TIPO 4: Errores y validación
df[...], groupby, .mean(), DataFrameTIPO 5: Pandas
plt., ax.bar, subplots, gráficoTIPO 6: Matplotlib
El tema que MÁS se toma
map() y filter() aparecen en TODOS los parciales, normalmente 2 a 4 veces. Si dominás ese tema solo, ya tenés varios puntos asegurados. La segunda estrella son los diccionarios (sobre todo el patrón "clave → lista de datos").
Lo que NO entra (no pierdas tiempo)
En este parcial no hay recursión, ni algoritmos de ordenamiento/búsqueda para programar a mano. Las "matrices" que aparecen son en realidad listas dentro de un diccionario (clave → [dato1, dato2, ...]), no matrices NxM.

Cómo trazar código sin equivocarte

  1. Llevá el estado de cada variable en un papel: anotá qué vale cada una después de cada línea.
  2. Mirá la indentación: en Python define qué está adentro de un for, if o def. Un return mal indentado cambia todo.
  3. map/filter: aplicá la función mentalmente a cada elemento y anotá el resultado.
  4. Acordate: range(n) va de 0 a n−1; input() siempre devuelve str; 'w' borra el archivo.

Mini-recordatorio de sintaxis (lo que más se olvida)

Diccionario: d = {clave: valor}  ·  d[clave]  ·  clave in d  ·  d.items()
map/filter: list(map(func, secuencia))  ·  list(filter(func, secuencia))
Archivo: open(ruta, 'r'/'w'/'a')  ·  .readlines()  ·  .write(texto)  ·  .close()
Error: try: … except ValueError: …
Pandas: df[df['col'] > x]  ·  df.groupby('col')['otra'].mean()
Gráfico: fig, ax = plt.subplots()  ·  ax.bar(categorías, valores)
↑ Volver al índice
2 TIPO 1 · Diccionarios

¿Qué es un diccionario?

Un diccionario guarda pares clave → valor. A diferencia de una lista (que se accede por posición 0, 1, 2…), un diccionario se accede por su clave.

persona = {"nombre": "Ana", "edad": 25}
persona["nombre"]      # "Ana"
persona["edad"]        # 25
OperaciónSintaxisQué hace
Accederd[clave]Devuelve el valor. KeyError si la clave no existe
Acceder segurod.get(clave, default)Devuelve el valor, o default si no está (no falla)
¿Existe la clave?clave in dTrue/False
Agregar / modificard[clave] = valorCrea la clave o pisa su valor
Recorrer clavesfor k in d:Itera sobre las claves
Recorrer paresfor k, v in d.items():Itera clave y valor a la vez

El patrón estrella: clave → lista de datos

El 90% de los ejercicios de diccionarios del parcial usan un diccionario donde cada valor es una lista. Por ejemplo, DNI → [nombre, apellido, aula]:

datos = {41234225: ['Juan', 'Aguirre', 301]}
datos[41234225]        # ['Juan', 'Aguirre', 301]
datos[41234225][2]     # 301  (índice 2 del valor, que es una lista)
datos[41234225][0]     # 'Juan'

Para acceder a un dato puntual encadenás dos accesos: primero la clave (te da la lista), después el índice dentro de la lista.

Ejercicio real (Parcial 2024 · T7 · Ej 1007): Completá la línea faltante

Por cada botón la clave es el código y los datos son [color, precio unitario, tamaño]. Hay que incrementar 10% el precio. ¿Cuál es la línea faltante?

botones = {'MET 002': ['plata', 350.0, 1], 'MET 001': ['dorado', 475.6, 1.5],
           'ACR 102': ['rojo', 210, 0.8], 'PVC 233 ': ['azul', 650, 2]}
boton = input('Cgo botón: ').upper()
if boton in botones:
    ……………           # línea a completar
    precio = round(botones[boton][1], 2)
    print('El nuevo precio unitario de', boton,
          'con 10% de aumento es $', precio)

Para el ingreso met 002 la salida debe ser: El nuevo precio unitario de MET 002 con 10% de aumento es $ 385.0

La línea correcta es botones[boton][1] *= 1.1: botones[boton] es la lista ['plata', 350.0, 1]; el índice [1] es el precio (350.0); *= 1.1 lo modifica adentro de la lista → 385.0. Las otras opciones del parcial: botones[boton] = 1 pisa toda la lista por el número 1; boton = 350*10/100+350 no toca el diccionario; botones[boton][2] -= 1 cambia el tamaño, no el precio.

Ejercicio real (Camejo · 2023 · T8 · Ej 0208): Validar usuario con diccionario

El diccionario es mail → [password, usuario]: cada clave es un mail y su valor es la lista [password, usuario]. El programa insiste hasta que el login sea correcto:

users = {'[email protected]': ['X25fc230', 'joanaBur'],
         '[email protected]':   ['Irma1211', 'rojas_27'],
         '[email protected]':    ['joacoS01', 'infoGENERAL']}

while not ingreso(users):
    print('Datos de Acceso Erróneos')

La versión correcta de ingreso() (entre las 4 que daba el parcial) es:

def ingreso(usr):
    usuario = input('Usuario: ')
    passw = input('Password: ')
    resp = False
    for datos in usr:                 # datos = cada clave (un mail)
        if usr[datos] == [passw, usuario]:
            resp = True
    return resp

Por qué: al recorrer for datos in usr, datos es cada mail y usr[datos] es su lista [password, usuario]. Comparar usr[datos] == [passw, usuario] exige que coincidan los dos datos. Trampa: comparar solo usr[datos][0] validaría únicamente el password; y usuario in usr chequea si es una clave (un mail), no los datos de acceso. (Es la versión completa del ejercicio 2.3 del quiz.)

Errores típicos del bloque
• KeyError: acceder a una clave que no existe (usá .get() o in antes).
• Confundir clave int con str: {0: 'a'} se accede con d[0], no d['0'].
• Pisar toda la lista (d[k] = x) cuando querías modificar un solo dato (d[k][i] = x).
↑ Volver al índice
3 TIPO 2 · map() y filter()

El tema más tomado del parcial

Las dos son funciones de orden superior: reciben una función y una secuencia, y aplican esa función a cada elemento. La diferencia es qué hacen con el resultado.

map(func, secuencia) → TRANSFORMA cada elemento. Devuelve la MISMA cantidad de elementos.
filter(func, secuencia) → SELECCIONA. La func debe devolver True/False; deja solo los que dan True.

Importante: las dos devuelven un "iterador", no una lista. Para ver el resultado hay que envolverlas en list(...).

def doble(x):
    return x * 2

def es_par(x):
    return x % 2 == 0

list(map(doble, [1, 2, 3]))        # [2, 4, 6]   -> transforma cada uno
list(filter(es_par, [1, 2, 3, 4])) # [2, 4]      -> deja los pares
La diferencia, en una frase
map cambia los elementos (misma cantidad de salida). filter elige cuáles pasan (devuelve los originales que cumplen la condición). El orden de los argumentos es SIEMPRE (función, secuencia).

Se combinan: primero filtrar, después transformar

list(map(doble, filter(es_par, [1, 2, 3, 4])))
# filter deja [2, 4]; map los duplica -> [4, 8]
Ejercicio real (Parcial 2024 · T7 · Ej 0307): filter con condición
def mes31(m):
    de30dias = (4, 6, 9, 11)
    return m not in de30dias and m != 2

meses = [1, 5, 6, 2, 7]
print(list(filter(mes31, meses)))

Salida: [1, 5, 7]

Por qué: filter deja los m que dan True. El 6 está en de30dias → False (sale). El 2 lo excluye m != 2 → False (sale). Quedan 1, 5 y 7.

Ejercicio real (Camejo · 2023 · T8 · Ej 0108): Precedencia and/or dentro del filter
def organiza(n):
    if n % 3 == 0 and n % 2 == 0 or n < 100:
        return True
    return False

a = [15, -150, 150, 66]
print(list(filter(organiza, a)))

Salida: [15, -150, 150, 66] (todos)

Por qué: and tiene más prioridad que or, así que la condición es (n%3==0 and n%2==0) or (n<100). 15, −150 y 66 son < 100 → True. 150 es múltiplo de 3 y de 2 → True. Pasan todos.

Ejercicio real (Parcial 2025 · T1 · Ej 9): filter + map encadenados
def edita(txt):
    return txt.upper()
def patron(txt):
    return ('á' in txt) or ('ó' in txt)

ciudades = ['anillaco', 'san javier', 'la dársena', 'fiambalá']
mostrar = list(filter(patron, ciudades))
salida = list(map(edita, mostrar))
print(salida)

Salida: ['LA DÁRSENA', 'FIAMBALÁ']

Por qué: filter deja las que tienen á u ó ("la dársena", "fiambalá"); después map(edita) las pasa a mayúsculas. Primero seleccionar, después transformar.

Errores típicos (los distractores del parcial)
• Invertir los argumentos: map(secuencia, func) ❌. Va map(func, secuencia).
• Usar filter con una función que NO devuelve booleano.
• Olvidar el list(...) y creer que imprime la lista (imprime <map object>).
↑ Volver al índice
4 TIPO 3 · Archivos de texto

Abrir, leer, escribir, cerrar

Trabajar con un archivo es siempre: abrir (con un modo) → leer o escribir → cerrar.

ModoQué hace
'r' (read)Lee. Falla (FileNotFoundError) si el archivo no existe
'w' (write)Borra todo el contenido previo y escribe de cero (crea el archivo si no existe)
'a' (append)Conserva lo que hay y agrega al final
'r+'Lee y escribe
MétodoQué hace
.readlines()Devuelve una lista con todas las líneas (cada una termina en \n)
.readline()Lee una sola línea (útil para saltear un título)
.write(texto)Escribe un string. NO agrega \n solo: lo ponés vos
.writelines(lista)Escribe una lista de strings (cada uno debe traer su \n)

El patrón típico: leer, procesar línea por línea, escribir otro

arch = open('contactos.txt', 'r')
lineas = arch.readlines()
arch.close()

invitados = []
for amigo in lineas:
    amigo = amigo.strip('\n')        # saca el salto de línea
    campos = amigo.split(',')        # separa por coma -> lista
    if campos[3].lower() == 'trabajo':
        invitados.append(campos[0] + ',' + campos[2] + '\n')

arch2 = open('invitados.txt', 'w')
arch2.writelines(invitados)
arch2.close()

Las dos herramientas clave para procesar cada línea: .strip('\n') (quita el salto de línea) y .split(',') (parte la línea en una lista de campos).

Ejercicio real (Parcial 2024 · T7 · Ej 0407): Filtrar y escribir otro archivo

contactos.txt tiene una línea por contacto con el formato nombre,apellido,mail,categoría. Hay que dejar en invitados.txt solo los de categoría "trabajo", escribiendo nombre,mail:

arch = open('contactos.txt', 'r+')
lista = arch.readlines()
arch.close()

invitados = []
for amigo in lista:
    amigo = amigo.strip('\n')        # saca el salto de línea
    a = amigo.split(',')             # separa por coma -> lista de campos
    if a[3].lower() == 'trabajo':    # la categoría es el 4° campo
        invitados.append(a[0] + ',' + a[2] + '\n')

arch = open('invitados.txt', 'w')
arch.writelines(invitados)
arch.close()

invitados.txt queda con: AnaEmilia,[email protected], Rodo,[email protected] y Ema,[email protected] (los tres con "trabajo"; el .lower() hace que entre también "TRABAJO"). Claves: strip('\n') saca el salto, split(',') parte en campos, y al escribir se agrega el \n a mano.

Errores típicos del bloque
• Usar 'w' cuando querías conservar lo anterior (borra todo). Para agregar, usá 'a'.
• Olvidar el \n al hacer write (queda todo pegado en una línea).
• No hacer .strip('\n') y arrastrar el salto de línea en las comparaciones.
↑ Volver al índice
5 TIPO 4 · try/except y validación

Capturar errores en vez de que el programa explote

El bloque try/except intenta ejecutar algo; si surge un error, en vez de cortar el programa, salta al except y sigue.

try:
    n = int(input("Número: "))   # esto puede fallar si escriben texto
    print(10 / n)                 # esto puede fallar si n = 0
except ValueError:
    print("No es un número entero")
except ZeroDivisionError:
    print("No se puede dividir por cero")
ExcepciónCuándo salta
ValueErrorint("hola"): convertir algo que no es número
ZeroDivisionErrorDividir por 0
FileNotFoundErrorAbrir en modo 'r' un archivo inexistente
KeyErrorAcceder a una clave que no está en el diccionario
IndexErrorAcceder a un índice fuera de la lista

El esqueleto clásico: validar input con while + try

sigue = True
while sigue:
    try:
        n = int(input("Ingresá un par positivo: "))
        if n > 0 and n % 2 == 0:
            sigue = False            # dato válido -> corta el while
        else:
            print("No es par positivo")
    except ValueError:
        print("Debe ser un entero")
print(n)
Por qué esta versión es la correcta (y los distractores no)
• El while insiste hasta tener un dato válido.
• sigue = True al inicio → entra al ciclo (si fuera False, no entraría nunca).
• sigue = False solo cuando el dato cumple la condición.
• El try envuelve únicamente lo que puede fallar (el int()).
Errores típicos del bloque
• Inicializar la bandera al revés (sigue = False) → el while nunca arranca.
• Poner el try sin while → no insiste, falla un dato y sigue de largo.
• Inventar excepciones que no existen (IndexOutOfRangeError no es de Python; es IndexError).
↑ Volver al índice
6 TIPO 5 · Pandas (DataFrames)

El DataFrame: una tabla con filas y columnas

Pandas trabaja con DataFrames (tablas). Lo que se toma en el parcial es: filtrar filas, agrupar y promediar, y operaciones simples (.sum(), .mean()).

OperaciónSintaxisQué devuelve
Una columnadf['col']La columna (Series)
Varias columnasdf[['col1', 'col2']]Sub-tabla (corchete doble)
Filtrar filasdf[df['edad'] > 18]Las filas que cumplen
Filtro con 2 condicionesdf[(df['a'] > 1) & (df['b'] != 'C')]Usar & | y paréntesis
Sumar / promediardf['col'].sum() · .mean()Un número
Agrupar y resumirdf.groupby('mes')['mm'].mean() · .max() · .min()Un valor por grupo
Ordenardf.sort_values('apellido')La tabla ordenada por esa columna
Contar repetidosdf['col'].value_counts()Cuántas veces aparece cada valor
La trampa #1 de pandas: & no and
Para combinar condiciones de filtro se usa & (y), | (o), ~ (no), con paréntesis obligatorios en cada condición. and/or de Python NO funcionan acá.
Ejercicio real (Parcial 2024 · T7 · Ej 0707): Filtro con dos condiciones
vue1 = vue[(vue['fila'] < 20) & (vue['asiento'] != 'C')]

Deja las filas con fila < 20 Y asiento distinto de 'C'. Una fila con asiento C se descarta; una con fila 23 también. Clave: los paréntesis y el & son obligatorios.

Ejercicio real (Camejo · 2023 · T8 · Ej 0508): groupby + mean
lluvias.groupby('mes')['mm'].mean()

Agrupa las filas por mes y promedia la columna mm de cada grupo. Si abril tiene 65 y 96 → promedio 80.5; si marzo tiene 120 y 115 → 117.5; etc. Devuelve un valor por mes. Distractor típico: confundirlo con head(4) o con un filtro.

↑ Volver al índice
7 TIPO 6 · Matplotlib (gráficos)

Las DOS formas de graficar (las dos se toman)

Hay dos APIs y el parcial usa ambas. Conviene reconocer las dos.

Forma pyplot (la directa)

import matplotlib.pyplot as plt
plt.bar(['A', 'B', 'C'], [10, 20, 15])
plt.title("Ventas")
plt.show()

Forma orientada a objetos (la que toman los parciales nuevos)

fig, ax = plt.subplots()
ax.bar(jugadores, alturas)       # bar(categorías, valores)
ax.set_title("Plantel")
ax.set_xlabel("Jugador")
ax.set_ylabel("Altura (cm)")
ax.set_ylim(0, 200)
ax.grid(axis='y')
plt.show()
FunciónQué dibuja
ax.bar(cat, val)Barras verticales
ax.barh(cat, val)Barras horizontales
ax.plot(x, y)Línea
ax.scatter(x, y)Puntos (dispersión)
ax.set_title / set_xlabel / set_ylabelTítulos y etiquetas

Grilla de subgráficos

fig, ax = plt.subplots(nrows=3, ncols=3)
ax[1, 1].plot(x, y)    # fila 1, columna 1 = el del centro
ax[2, 2].bar(x, y)     # esquina inferior derecha

Con una grilla, ax se indexa ax[fila, columna] (ambas empiezan en 0).

Errores típicos del bloque
• Invertir los argumentos de bar: va ax.bar(categorías, valores), no al revés.
• Leer mal la posición en la grilla: ax[fila, columna] empezando en 0 (el del medio de una 3×3 es ax[1,1]).
↑ Volver al índice
8 Mix integrador · Ejercicios de repaso

Reconocer el tipo en menos de 15 segundos

Si la pregunta muestra...TipoQué hacer
list(map(...)) o list(filter(...))map/filterAplicar la función a cada elemento; filter deja los True
d[clave], d[k][i]DiccionariosEncadenar clave + índice de la lista
open(...), .readlines(), .write()ArchivosVer el modo; procesar con strip/split
try: / except:ValidaciónVer bandera del while y qué excepción captura
df[...], groupbyPandasFiltro con & y paréntesis; groupby agrupa
ax.bar, subplotsMatplotlibbar(categorías, valores); ax[fila, col]

Los 4 errores más caros del parcial

Error 1: Orden de argumentos en map/filter
Es map(función, secuencia), nunca map(secuencia, función). Y filter necesita una función que devuelva True/False.
Error 2: 'w' borra el archivo
Abrir en modo 'w' borra todo el contenido anterior. Si querías conservar y agregar, era 'a'.
Error 3: and en lugar de & en pandas
Los filtros de pandas usan &, |, ~ con paréntesis. and/or de Python tiran error ahí.
Error 4: Pisar la lista de un diccionario
d[k] = x reemplaza TODO el valor; para tocar un dato puntual era d[k][i] = x.

Práctica integradora: combinaciones típicas

Ejercicio real (Parcial 2024 · T7 · Ej 0607): try/except con diccionario

Devolver el titular de una patente sin que el programa aborte si la patente no está (ej. jdd003):

titulares = {'AG 234 FF': 'Andreoli, Julieta', 'AC 217 DR': 'Vargas, Ulises',
             'KDF 876': 'Fiquet, Paulo', 'LBD 976': 'Carassa, Emilia'}
pat = input('Patente: ')
print('El titular de', pat, quien(titulares, pat))

La versión correcta de quien() (entre las 4 del parcial) es:

def quien(titulares, pat):
    try:
        cartel = 'es ' + titulares[pat.upper()]
    except:
        cartel = ' es desconocido'
    return cartel

Por qué: el acceso titulares[pat.upper()] (que da KeyError si la patente no existe) va DENTRO del try; si falla, el except arma "es desconocido". Trampa: si el acceso al diccionario estuviera fuera del try, el programa se cortaría igual.

Ejercicio real (Parcial 2024 · T4 · Ej 1004): for + diccionario + replace

Traducir los caracteres del texto que están definidos en el diccionario traductor. ¿Cuál es la línea faltante?

txt = 'Me picaron MAS mosquitos QUE EN EL VERANO'
traductor = {'e':')', 'o':'%', 'a':'*', 'i':'/', 'u':'@', 'm':'&', ' ':'+-'}
for letra in txt:
    if letra in traductor:
        ……………           # línea a completar
print(txt)

La línea correcta es txt = txt.replace(letra, traductor[letra]): reemplaza en TODO el texto cada carácter por su traducción. Trampa: letra = traductor[letra] cambia una variable suelta pero no toca txt; y hay que reasignar txt = porque replace devuelve un texto nuevo (no modifica el original).

Estrategia final para el día del parcial
1. Leé la pregunta y clasificá el tema en 15 segundos.
2. En map/filter: aplicá la función a cada elemento en un papel.
3. En archivos: fijate el modo ('w' borra) y dónde se concatena cada dato.
4. En diccionarios: ¿la clave es int o str? ¿el valor es lista (encadenar índice)?
5. En pandas: filtros con & y paréntesis; groupby agrupa.
6. Si dudás entre dos opciones, descartá la que tenga un error de sintaxis evidente (argumentos invertidos, and en pandas, excepción inexistente).
↑ Volver al índice