Criar uma tabela e um gráfico mensais a partir de registros de trabalho
Valide as datas e as horas de trabalho do CSV e salve a quantidade de registros e o total de horas de cada mês em um CSV e um gráfico de barras.
Conteúdo verificado 2026.09.19Inclui arquivos de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. 한국어
Para quem éProfissionais administrativos que querem processar resumos mensais recorrentes com código simples em Python
Preparação
Prepare o Python 3.12 e um terminal. Confira a versão com python --version.
Extraia todo o ZIP baixado e abra o terminal na pasta que contém example.py.
Execute primeiro com os dados de prática incluídos e compare o resultado com o original.
São usados work_log.csv e Matplotlib. O CSV deve ter codificação UTF-8 ou UTF-8 BOM.
01Começar definindo o que uma linha representa
Cada linha é considerada um registro de trabalho, e hours é somado ao mês de date. record_count conta linhas, não pessoas nem dias trabalhados. hours representa horas decimais: 1.50 significa 1 hora e 30 minutos, não 1 hora e 50 minutos.
Os dados incluídos são 9 registros de trabalho fictícios dos meses 1~3 de 2026. Não é uma ferramenta para medir o tempo trabalhado nem avaliar o desempenho, mas uma prática para somar por mês números já informados.
02Conferir as colunas e os valores de entrada
Coluna
Formato
Critério de conferência
record_id
Texto como R001
Deve ser único no arquivo
date
2026-01-06
YYYY-MM-DD, uma data real
task
자료 정리
Não pode estar vazio
hours
2.50
Número finito maior ou igual a 0, com no máximo duas casas decimais
Não inclua linhas de título, linhas de totais nem números com vírgulas. Se um ID duplicado for detectado, o programa para para evitar uma contagem dupla. Mesmo que a tarefa seja igual, IDs diferentes são somados como registros distintos.
03Preparar os arquivos e executar
Extraia o ZIP e confira se o arquivo de entrada indicado em README.txt está no mesmo local que example.py. Não execute diretamente de dentro do ZIP.
Execute os comandos abaixo, um por vez, na pasta que contém example.py. Se no Windows o comando python não estiver disponível, mas py estiver, substitua python por py -3.12 em cada comando.
Quando aparecer a mensagem de conclusão, abra a nova pasta outputs. Se outputs já existir, guarde-a primeiro com outro nome e execute novamente.
A instalação exige conexão com a internet. BASE define os caminhos de entrada e saída a partir da localização de example.py, não da localização atual do terminal.
04Código completo e método de agregação
As datas são validadas com date.fromisoformat, e é criada uma chave mensal no formato YYYY-MM. As horas são somadas com Decimal e registradas na tabela com duas casas decimais. A conversão para números de ponto flutuante ocorre somente ao desenhar o gráfico. É usado o modo Agg, portanto um PNG é salvo em vez de abrir uma janela de gráfico.
example.py
"""Summarize fictional work-log records by calendar month."""
import csv
import math
from collections import defaultdict
from datetime import date
from decimal import Decimal, InvalidOperation
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
BASE = Path(__file__).resolve().parent
HEADERS = ["record_id", "date", "task", "hours"]
def main():
destination = BASE / "outputs"
if destination.exists():
raise ValueError("outputs already exists; rename it before running again.")
totals = defaultdict(lambda: {"records": 0, "hours": Decimal("0")})
seen = set()
with (BASE / "work_log.csv").open(encoding="utf-8-sig", newline="") as file:
reader = csv.DictReader(file)
if reader.fieldnames != HEADERS:
raise ValueError(f"Expected headers: {HEADERS}")
for line, row in enumerate(reader, start=2):
if None in row or any(value is None or not value.strip() for value in row.values()):
raise ValueError(f"Row {line}: missing or extra field.")
if row["record_id"] in seen:
raise ValueError(f"Row {line}: duplicate record_id.")
seen.add(row["record_id"])
parsed_date = date.fromisoformat(row["date"])
if parsed_date.isoformat() != row["date"]:
raise ValueError(f"Row {line}: use YYYY-MM-DD dates.")
try:
hours = Decimal(row["hours"])
except InvalidOperation as error:
raise ValueError(f"Row {line}: hours must be a number.") from error
if not hours.is_finite() or hours < 0 or hours.as_tuple().exponent < -2:
raise ValueError(f"Row {line}: hours must be finite, nonnegative, with at most 2 decimals.")
month = parsed_date.strftime("%Y-%m")
totals[month]["records"] += 1
totals[month]["hours"] += hours
if not totals:
raise ValueError("No work records were found.")
months = sorted(totals)
figure, axis = plt.subplots(figsize=(8, 4.6), layout="constrained")
values = [float(totals[month]["hours"]) for month in months]
if not all(math.isfinite(value) and math.isfinite(value * 1.2) for value in values):
plt.close(figure)
raise ValueError("Monthly total is too large to plot as a finite number.")
bars = axis.bar(months, values, color="#2B6578", width=0.55)
axis.bar_label(bars, labels=[f"{value:.2f}" for value in values], padding=4)
axis.set(title="Monthly work hours | Fictional data", xlabel="Calendar month", ylabel="Hours")
axis.set_ylim(0, max(values) * 1.2 if max(values) > 0 else 1)
axis.spines[["top", "right"]].set_visible(False)
axis.set_axisbelow(True)
axis.grid(axis="y", alpha=0.2)
destination.mkdir(exist_ok=False)
with (destination / "monthly_summary.csv").open("x", encoding="utf-8-sig", newline="") as file:
writer = csv.writer(file)
writer.writerow(["month", "record_count", "total_hours"])
for month in months:
writer.writerow([month, totals[month]["records"], f"{totals[month]['hours']:.2f}"])
figure.savefig(destination / "monthly_hours.png", dpi=160)
plt.close(figure)
total_hours = sum((values["hours"] for values in totals.values()), Decimal("0"))
print(f"Records: {len(seen)}; months: {len(months)}; hours: {total_hours:.2f}")
print("Created outputs/monthly_summary.csv and outputs/monthly_hours.png.")
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, csv.Error) as error:
raise SystemExit(f"Stopped: {error}") from error
05Ler a tabela e o gráfico de resumo
São criados outputs/monthly_summary.csv e outputs/monthly_hours.png. O gráfico mostra apenas os meses com dados de entrada, ordenados por ano e mês. Os eixos em inglês foram escolhidos para permitir a execução sem instalar fontes coreanas adicionais.
month
record_count
total_hours
2026-01
3
6.50
2026-02
3
7.50
2026-03
3
9.50
Total geral de conferência
9
23.50
Gráfico de horas mensais de registros de trabalho fictícios. Mês 1: 6.5 horas; mês 2: 7.5 horas; mês 3: 9.5 horas.
06Conferir os totais e a mudança de mês
Confira no CSV de resumo se o mês 1 soma 2.50 + 1.00 + 3.00 = 6.50 horas.
Confira se a soma mensal de record_count corresponde aos 9 registros de entrada.
Confira se a soma de total_hours é 23.50 e compare com os valores das barras do gráfico.
Copie a pasta de prática, altere a data do último registro para 2026-04-01 e execute novamente. Confira se o resultado fica dividido em 5.50 horas para o mês 3 e 4.00 horas para o mês 4.
07Quando o programa para por causa de datas ou números
Problema
Como corrigir
duplicate record_id
Confira se o mesmo registro foi inserido duas vezes.
day is out of range / Invalid isoformat
Confira se há datas impossíveis, como 2026-02-30, ou formatos de data incorretos.
hours must be a number
Informe apenas números para as horas, como 2.5. Remova as unidades de tempo e as vírgulas de separação de milhares.
hours must be finite…
Verifique valores negativos, NaN, Infinity ou valores com três ou mais casas decimais.
Os caracteres coreanos aparecem incorretamente
Salve novamente o CSV original em UTF-8.
outputs already exists
Preserve os resultados anteriores e altere o nome da pasta de resultados.
08O que saber antes de incluir em um relatório
Registros que contêm apenas a data são agrupados por mês do calendário. Não são processados horários de início ou término, turnos noturnos, fusos horários nem tarefas que passam da meia-noite.
Meses sem dados não são convertidos em linhas de 0 horas, pois é necessário distinguir registros ausentes de um valor real de 0 horas.
Apenas IDs duplicados são bloqueados. Se a mesma tarefa foi inserida duas vezes com IDs diferentes, é necessário conferir os dados originais.
O CSV de resumo e o PNG refletem o momento da execução. Se você alterar o CSV, deverá executar novamente para obter novos resultados.
Registro de execução e verificação
Windows 11 (10.0.26200), CPython 3.12.14 (64 bits). openpyxl 3.1.5, Matplotlib 3.10.8. As versões das bibliotecas usadas em cada exemplo estão fixadas em requirements.txt.
Conferidas 3 linhas mensais, 23.50 horas e 9 registros
Verificadas a criação do PNG e os resultados ao mudar um registro de mês
Verificados erros de datas incorretas, IDs duplicados, horas negativas ou NaN e cabeçalhos
Conferido que o hash do original permanece igual e que os resultados existentes são preservados
Limites da verificação
Não é verificada a correspondência com as regras de sistemas reais de recursos humanos ou controle de ponto.
O código de exemplo, os nomes de arquivos e as chaves de entrada são mantidos como no original. Consulte também os comandos e os procedimentos de conferência do texto traduzido.
Material de prática original · Guarde os arquivos originais separadamente antes de executar.
Fontes de referência
As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.
Busque linhas com as quatro colunas idênticas e reúna-as em um novo arquivo do Excel com os números das linhas originais. Confira cada grupo de duplicatas incluindo sua primeira ocorrência.