De una hoja de ventas a un informe verificable
De un CSV a un informe de ventas con Python
En unos 20 minutos agruparás seis ventas por producto, comprobarás un total de 111,80 € y obtendrás un CSV nuevo que puedes abrir en Excel. Sin registro y con datos sintéticos.
Si utilizas Excel y quieres empezar a automatizar informes, prueba este caso pequeño antes de decidir si el curso encaja contigo.
Unos 20 minutos · Sin registro, sin tarjeta y sin instalar Python. Necesitas leer instrucciones y completar dos líneas con ayuda. En móvil puedes leer la muestra; para programar recomendamos un ordenador.
- 3lecciones guiadas
- 5comprobaciones
- 1informe de ventas
- 0 €muestra libre

Esto es lo que vas a conseguir
Leer seis ventas, sumar por producto y comprobar que el total cuadra sin modificar el origen.
El CSV puede abrirse con Excel. No creamos un archivo .xlsx en esta muestra: generamos un resumen .csv y trabajamos el método que luego podrás ampliar.
Terminar significa comprobar
- El resumen contiene Cuaderno 24,50 €, Ratón 37,50 € y Teclado 49,80 €, en ese orden.
- La suma del resumen es exactamente 111,80 € y las pruebas detectan entradas inválidas.
- El código conserva las filas originales y crea un CSV nuevo, sin sobrescribir archivos.
Es una muestra guiada de un solo caso; no equivale a dominar Python ni a completar el curso.
Tres lecciones para hacer el trabajo
Lee el ejemplo, completa la operación y contrasta el resultado. Puedes abrir cada paso cuando lo necesites.
01Entender el archivo y decidir qué debe salir
Tu tarea no es copiar otra hoja: es repetir un informe con reglas explícitas. El archivo contiene seis ventas y tres columnas. Cada fila es una venta; producto identifica qué se vendió, unidades es la cantidad y precio es el precio unitario en euros. No hay clientes ni datos personales.
1. Lee como Excel, pero recuerda que el CSV guarda texto
El separador de este CSV es el punto y coma. csv.DictReader convierte cada fila en un diccionario y usa la primera línea como nombres de columna. Al leerlo, '2' y '4.90' siguen siendo cadenas, no números. Hay que convertirlas antes de multiplicar; concatenar texto daría un resultado incorrecto.
import csv, io
ventas = list(csv.DictReader(io.StringIO(VENTAS_CSV), delimiter=';'))
print(ventas[0])
# {'producto': 'Cuaderno', 'unidades': '2', 'precio': '4.90'} 2. Define la salida antes de programar
Queremos una fila por producto, ordenada alfabéticamente, y su importe total. Cuaderno aparece dos veces: 2 × 4,90 + 3 × 4,90 = 24,50 €. Ratón suma 37,50 € y Teclado 49,80 €. La suma de esos tres resultados debe ser 111,80 €. Estos números son una referencia calculada, no una promesa de que tu código ya haya pasado las pruebas.
producto;total_eur
Cuaderno;24,50
Ratón;37,50
Teclado;49,80 3. Conserva el origen
El ejercicio crea una estructura nueva para el resumen y un texto CSV nuevo para la salida. No cambia las filas de entrada ni escribe encima del archivo original. Si después llega un archivo con errores, podrás localizar el problema y repetir el informe sin perder evidencia.
Compruébalo tú
Antes de seguir: comprueba a mano Cuaderno (24,50 €). ¿Cuántas filas tendrá el resumen? Tres, aunque el origen tenga seis ventas.
02Validar importes y completar la agrupación
Una automatización útil no acepta cualquier cosa en silencio. Aquí se valida cada fila antes de sumarla y se interrumpe con un mensaje si falta un producto, las unidades no son un entero positivo o el precio no es válido. Para concentrarte en la agrupación, esa validación ya está incluida en el laboratorio.
1. Una regla concreta para el dinero
Decimal representa cantidades decimales a partir de texto. Usamos Decimal('4.90'), no Decimal(4.90): la segunda forma parte de un float binario. En esta muestra multiplicamos precio por unidades y redondeamos cada línea a dos decimales con ROUND_HALF_UP; después sumamos las líneas. Así 1,005 € se convierte en 1,01 €. En un proyecto real hay que confirmar y documentar la política de redondeo que corresponda.
from decimal import Decimal, ROUND_HALF_UP
importe = (Decimal('4.90') * 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(importe) # 9.80 2. Un diccionario guarda el acumulado
resumen empieza vacío. Por cada fila, validar_fila devuelve el nombre limpio y el importe de esa venta. get(producto, Decimal('0.00')) lee lo acumulado, o cero si el producto aún no existe. Añade el importe actual y guarda la nueva suma con el mismo nombre de producto. No reinicies el acumulado dentro del bucle.
anterior = resumen.get(producto, Decimal('0.00'))
resumen[producto] = anterior + importe 3. Tu parte del ejercicio
En el editor, busca COMPLETAR_AGRUPACION al principio de resumir_ventas. Sustituye pass por las dos líneas anteriores, manteniendo la indentación dentro del bucle for. El resto del código está preparado. Ejecuta y valida: primero verás el informe y después el resultado de las comprobaciones. Puedes usar las pistas o consultar la solución completa, pero intenta explicar por qué get necesita un valor inicial.
Compruébalo tú
¿Por qué no sirve resumen[producto] = importe? Porque una venta posterior del mismo producto sobrescribiría lo anterior en vez de sumarlo.
03Comprobar, exportar y repetir sin sorpresas
Ver una tabla con buen aspecto no demuestra que los números sean correctos. Validaremos un caso conocido, el total, los productos repetidos, los importes problemáticos y la conservación del original. Después generaremos una salida compatible con Excel.
1. Usa pruebas que puedan fallar de verdad
El laboratorio comprueba varios importes y entradas incorrectas. Una lista vacía debe producir {}; None no es una lista y debe rechazarse. Las unidades 0 o 1,5 no cumplen la regla de este ejemplo. NaN e infinito tampoco son precios válidos. Además, comparar una copia de las filas antes y después detecta modificaciones accidentales.
assert resumen['Cuaderno'] == Decimal('24.50')
assert sum(resumen.values(), Decimal('0.00')) == Decimal('111.80')
assert resumir_ventas([]) == {} 2. Crea un CSV nuevo para Excel
crear_csv_resumen convierte el resultado en texto con punto y coma y coma decimal. Copia la salida de consola en un archivo NUEVO llamado resumen_ventas.csv. Ábrelo en Excel con codificación UTF-8 y separador punto y coma si aparece el asistente. Los ZIP de inicio y solución incluyen exactamente el mismo código y dataset; requieren Python 3.10 o posterior y no instalan paquetes.
texto_salida = crear_csv_resumen(resumen)
print(texto_salida)
# El original VENTAS_CSV no cambia. 3. Distingue una muestra de una herramienta profesional
Aquí no procesamos datos reales, no trabajamos con archivos Excel .xlsx ni programamos ejecuciones automáticas. Esos son pasos posteriores. Los límites de entrada y el rechazo de nombres que comienzan por =, +, - o @ reducen riesgos de exportar fórmulas desde el campo producto, pero no convierten esta muestra en un sistema de producción. Entender estas fronteras también forma parte de automatizar bien.
Compruébalo tú
Finaliza con dos evidencias: el total 111,80 € y el mensaje de comprobaciones superadas. Después responde las cinco decisiones del diagnóstico.
Hazlo en el laboratorio
Busca COMPLETAR_AGRUPACION dentro de resumir_ventas y sustituye pass por las dos líneas de la segunda lección. El resto del programa ya está preparado.
El código se ejecuta en tu navegador, no se envía a KINTAVOR. El editor recuerda tu trabajo en este navegador. Usa solo el ejemplo ficticio: no pegues información personal, contraseñas ni claves.
Listo. Completa la agrupación y pulsa «Ejecutar y validar».
Consola y comprobaciones
La primera ejecución descarga el motor de Python y puede tardar. Si tu navegador no lo permite, descarga el ejercicio para ejecutarlo con Python 3 en tu ordenador.
Necesito una pista
- Pista 1: el importe de la fila ya está validado y redondeado. Tu tarea es sumarlo al acumulado de su producto.
- Pista 2: lee el total anterior con resumen.get(producto, Decimal('0.00')). No hagas resumen = {} dentro del bucle.
- Pista 3: guarda anterior + importe en resumen[producto]. Mantén las dos líneas con la misma indentación que la llamada a validar_fila.
Ver la solución explicada
Inténtalo primero. Después compara la acumulación, las validaciones y el total. Puedes copiar el código completo al editor y volver a ejecutarlo para comprobarlo.
"""Muestra didáctica KINTAVOR. Datos sintéticos, biblioteca estándar."""
import csv
import io
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
def resumir_ventas(filas):
"""Agrupa importes por producto, ordenados, sin modificar el original."""
if not isinstance(filas, list):
raise TypeError("filas debe ser una lista de diccionarios.")
if len(filas) > 10000:
raise ValueError("Esta muestra admite un máximo de 10000 filas.")
resumen = {}
for numero, fila in enumerate(filas, start=1):
producto, importe = validar_fila(fila, numero)
# COMPLETAR_AGRUPACION: solución
anterior = resumen.get(producto, Decimal('0.00'))
resumen[producto] = anterior + importe
return dict(sorted(resumen.items()))
VENTAS_CSV = """producto;unidades;precio
Cuaderno;2;4.90
Teclado;1;24.90
Cuaderno;3;4.90
Ratón;2;12.50
Teclado;1;24.90
Ratón;1;12.50
"""
def leer_ventas(texto):
"""Lee un CSV con punto y coma; los valores siguen siendo texto."""
if not isinstance(texto, str):
raise TypeError("El CSV debe ser texto.")
lector = csv.DictReader(io.StringIO(texto), delimiter=";")
if lector.fieldnames != ["producto", "unidades", "precio"]:
raise ValueError("Cabecera esperada: producto;unidades;precio")
return list(lector)
def validar_fila(fila, numero):
"""Devuelve producto e importe de línea; rechaza datos ambiguos."""
if not isinstance(fila, dict) or set(fila) != {"producto", "unidades", "precio"}:
raise ValueError(f"Fila {numero}: deben existir producto, unidades y precio.")
producto = fila["producto"]
if not isinstance(producto, str) or not producto.strip():
raise ValueError(f"Fila {numero}: falta el producto.")
producto = producto.strip()
if len(producto) > 80 or producto.startswith(("=", "+", "-", "@")):
raise ValueError(f"Fila {numero}: nombre de producto no permitido para este CSV.")
unidades = fila["unidades"]
if isinstance(unidades, bool) or not isinstance(unidades, (int, str)):
raise ValueError(f"Fila {numero}: unidades debe ser un entero positivo.")
texto_unidades = str(unidades).strip()
if len(texto_unidades) > 6 or not texto_unidades.isascii() or not texto_unidades.isdecimal():
raise ValueError(f"Fila {numero}: unidades debe ser un entero positivo.")
cantidad = int(texto_unidades)
if not 1 <= cantidad <= 100000:
raise ValueError(f"Fila {numero}: unidades debe estar entre 1 y 100000.")
valor = fila["precio"]
if isinstance(valor, bool) or not isinstance(valor, (str, int, float, Decimal)):
raise ValueError(f"Fila {numero}: el precio no es válido.")
texto_precio = str(valor).strip()
if not texto_precio or len(texto_precio) > 64:
raise ValueError(f"Fila {numero}: el precio no es válido.")
if isinstance(valor, str):
partes = texto_precio.replace(",", ".").split(".")
if len(partes) > 2 or any(not p.isascii() or not p.isdecimal() for p in partes):
raise ValueError(f"Fila {numero}: usa un precio sin separadores de miles.")
try:
precio = Decimal(texto_precio.replace(",", "."))
except (InvalidOperation, ValueError):
raise ValueError(f"Fila {numero}: el precio no es numérico.") from None
if not precio.is_finite() or not Decimal("0") <= precio <= Decimal("1000000"):
raise ValueError(f"Fila {numero}: el precio debe estar entre 0 y 1000000.")
# Regla explícita: redondear CADA LÍNEA a céntimos antes de agrupar.
importe = (precio * cantidad).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return producto, importe
def crear_csv_resumen(resumen):
"""Crea una salida NUEVA, con coma decimal, sin sobrescribir el CSV original."""
salida = io.StringIO(newline="")
escritor = csv.writer(salida, delimiter=";", lineterminator="\n")
escritor.writerow(["producto", "total_eur"])
for producto, importe in resumen.items():
escritor.writerow([producto, format(importe, ".2f").replace(".", ",")])
return salida.getvalue()
ventas = leer_ventas(VENTAS_CSV)
resumen = resumir_ventas(ventas)
print(crear_csv_resumen(resumen))
print("Total:", format(sum(resumen.values(), Decimal("0.00")), ".2f"), "EUR")
Descargar solución y pruebasCompara tu salida con este resultado
Resultado de referencia de las seis ventas sintéticas. Compáralo con tu consola; esta tabla no se actualiza al editar el código.
No es un gráfico que cambie con tu código. La consola del laboratorio muestra lo que realmente ha calculado tu programa.
| Producto | Importe |
|---|---|
| Cuaderno | 24,50 € |
| Ratón | 37,50 € |
| Teclado | 49,80 € |
| Total | 111,80 € |
Importes del ejemplo, no resultados económicos de alumnos.
Cinco comprobaciones prácticas
Haz pequeños cambios y observa el efecto. Entender un error forma parte del ejercicio.
- 01
Comprueba la lectura del CSV
Ejecuta print(ventas[0]) en una copia del editor. Identifica por qué unidades y precio llegan como texto y qué separador utiliza DictReader.
- 02
Completa el acumulado por producto
Completa las dos líneas marcadas, ejecuta las comprobaciones y localiza Cuaderno: dos filas deben producir 24,50 €, no 9,80 ni 14,70 €.
- 03
Verifica un caso de redondeo
Prueba dos filas del mismo producto, una unidad y precio '0.005' cada una. El total será 0,02 € porque la regla redondea cada línea; redondear al final daría 0,01 €.
- 04
Provoca y reconoce un error de entrada
En una copia de las ventas cambia unidades a '1.5'. Debe aparecer ValueError, no un informe silenciosamente corregido. Reinicia después para recuperar el ejemplo limpio.
- 05
Explica el resultado y conserva el origen
Anota las tres filas y el total 111,80 €. Explica por qué se usa un resumen nuevo y cómo comprobarías que las filas de origen siguen intactas.
Comprueba lo que has aprendido
Cinco preguntas distintas sobre el ejercicio. Recibirás la explicación de cada respuesta; no es un examen de certificación ni determina tu nivel general.
¿Quieres aplicar este método a más tareas?
Esta muestra enseña una tarea pequeña y verificable. El curso completo amplía el trabajo con automatización, Excel y calidad de datos, SQL, integraciones y proyectos de portfolio mediante autoestudio. Revisa el programa, la modalidad, las condiciones y el precio vigente antes de decidir. El diagnóstico orienta tu repaso de esta muestra; no acredita un nivel profesional ni indica saltarse módulos.
La muestra enseña un caso guiado con la biblioteca estándar de Python. El curso completo amplía la práctica con archivos, Excel, análisis e integraciones; revisa el temario, los requisitos y las condiciones antes de decidir.
Formación privada de autoestudio, sin clases en directo. Certificado propio no oficial. No se garantiza empleo ni ingresos. Vendedor: MYSERVEAI TECHNOLOGIES SL · CIF B23836711. KINTAVOR es la marca comercial.