title: “Atividade Prática: Analista de Dados Educacionais”
date: 2026-10-26
draft: false
tags:
- pandas
- sqlite
- sql
- python
- analise-de-dados
- atividade-pratica
description: “Atividade prática: cruze alunos, cursos e matrículas com Pandas e SQLite para calcular matrículas ativas, faturamento e perfil dos alunos.”
Objetivo
Manipular e cruzar múltiplas tabelas com Pandas e com SQL (SQLite) para gerar um relatório consolidado de alunos, cursos e faturamento da escola.
🎓 Contexto
Você foi contratado como Analista de Dados por uma escola de tecnologia. Os cadastros estão em três tabelas separadas e a diretoria precisa de respostas sobre matrículas, faturamento e perfil dos alunos.
Cada pergunta será resolvida duas vezes: primeiro com Pandas, depois com SQL. Ao final, compare os resultados.
Antes de programar
Os dados são pequenos. Para cada pergunta, calcule a resposta à mão e só depois confira com o código.
📊 1. Os três datasets
Tabela alunos
id_aluno,nome_aluno,idade,cidade
1,Ana Silva,21,São Paulo
2,Bruno Costa,24,Rio de Janeiro
3,Carlos Souza,19,Belo Horizonte
4,Diana Martins,22,São Paulo
5,Eduardo Lima,20,FortalezaTabela cursos
id_curso,nome_curso,preco_mensalidade
101,Python para Dados,150
102,Web Design Moderno,120
103,Banco de Dados SQL,130Tabela matriculas
id_matricula,id_aluno,id_curso,status
1001,1,101,Ativo
1002,1,103,Ativo
1003,2,101,Ativo
1004,3,102,Cancelado
1005,4,101,Ativo
1006,4,103,TrancadoAtenção aos casos de borda
Os dados foram montados para exigir cuidado:
- Eduardo não tem nenhuma matrícula.
- Ana tem duas matrículas ativas.
- Diana tem uma matrícula
Trancado(que não conta como ativa).- Web Design Moderno só tem uma matrícula
Cancelado, ou seja, 0 ativas.
🛠️ 2. Passo a passo
- Carregar os três arquivos CSV (
alunos.csv,cursos.csv,matriculas.csv) no Pandas. Useencoding='utf-8'para preservar os acentos. - Combinar
matriculascomalunospela colunaid_aluno. - Combinar o resultado com
cursospela colunaid_curso. - Conferir que nenhuma matrícula foi perdida no cruzamento (o número de linhas deve continuar sendo 6).
Resultado esperado
Um único DataFrame com nome do aluno, curso e preço da mensalidade de cada matrícula.
📈 3. Análise e relatórios
Como responder
Para cada pergunta: (1) calcule à mão, (2) resolva com Pandas, (3) resolva com SQL, (4) confira se os dois resultados são iguais.
Pergunta 1: Alunos ativos
Quais alunos têm pelo menos uma matrícula com status Ativo? Cada aluno deve aparecer uma única vez.
Resposta esperada
Ana Silva, Bruno Costa e Diana Martins.
Ana tem duas matrículas ativas, mas aparece uma vez só.
Pergunta 2: Matrículas ativas por curso
Qual é o número total de matrículas ativas por curso? Cursos sem matrículas ativas devem aparecer com 0.
Resposta esperada
- Python para Dados: 3
- Banco de Dados SQL: 1
- Web Design Moderno: 0
Armadilha
Se você filtrar o status
Ativoantes de agrupar, o curso Web Design desaparece do resultado. Pense em qual tabela deve ser o ponto de partida do cruzamento.
Pergunta 3: Faturamento mensal
Qual é o faturamento total mensal da escola? Some o preço da mensalidade de cada matrícula ativa.
Resposta esperada
R$ 580,00 (150 + 130 + 150 + 150). As matrículas
TrancadoeCanceladonão entram.
Pergunta 4: Perfil dos alunos
Qual é a idade média dos alunos com matrícula ativa no curso “Python para Dados”? Conte cada aluno uma vez.
Resposta esperada
22,3 anos ((21 + 24 + 22) ÷ 3). Exiba com uma casa decimal: arredondar para 22 esconde informação.
Pergunta 5: Alunos sem matrícula
Quais alunos não têm nenhuma matrícula?
Resposta esperada
Eduardo Lima.
Dica
Um cruzamento que só mantém linhas com par (
inner) nunca mostraria Eduardo. Qual tipo de cruzamento mantém todos os alunos?
Pergunta 6 (bônus): Maior faturamento
Qual curso gera o maior faturamento considerando apenas matrículas ativas?
Resposta esperada
Python para Dados, com R$ 450.
🗄️ 4. Versão com SQLite no Google Colab
O módulo sqlite3 já vem com o Python. Crie o banco com as três tabelas, insira os mesmos dados e responda às mesmas perguntas com SQL.
| Pandas | SQL |
|---|---|
pd.merge(a, b, on='x') | JOIN ... ON |
how='left' | LEFT JOIN |
df[df.col == x] | WHERE col = x |
.drop_duplicates() | SELECT DISTINCT |
.groupby('col').count() | GROUP BY col + COUNT(...) |
.sum() / .mean() | SUM(...) / AVG(...) |
.sort_values() | ORDER BY |
.head(1) | LIMIT 1 |
Regras do banco
Ative
PRAGMA foreign_keys = ON(o SQLite não valida chaves estrangeiras por padrão) e use umCHECKpara limitar o status aAtivo,CanceladoeTrancado.
ON×WHEREnoLEFT JOINNa pergunta 2, o filtro
status = 'Ativo'deve ficar noON. NoWHERE, a linha do curso sem matrículas é descartada e oLEFT JOINperde o efeito. Na pergunta 5, oIS NULLfica noWHERE. Entenda por que a regra muda.
🚀 5. Desafios extras
- Matricule Eduardo em um curso. Quais respostas mudam? O faturamento muda?
- Troque o status da matrícula 1006 de
TrancadoparaAtivo. Preveja quais valores mudam antes de rodar. - Tente inserir uma matrícula com
id_curso = 999. O SQLite aceita? E o Pandas? - Tente inserir o status
Pendente. Qual regra do banco impede? - Calcule o faturamento por cidade do aluno, em Pandas e em SQL.
- Gere um gráfico de barras com as matrículas ativas por curso (o curso com 0 deve aparecer) e uma pizza com a proporção do faturamento.
✅ 6. Entregável
- Notebook do Google Colab com as perguntas resolvidas em Pandas e SQL, lado a lado.
- Uma célula de conferência após cada pergunta, mostrando que os dois resultados coincidem.
- Os gráficos do desafio 6, usando apenas matrículas ativas.
🎓 Atividade Prática: Analista de Dados Educacionais
Você foi contratado como Analista de Dados por uma escola de tecnologia. Sua missão é importar os cadastros separados e responder às perguntas da diretoria.
Objetivo da atividade
Manipular e cruzar múltiplas tabelas de dados usando o Pandas para gerar um relatório consolidado de alunos, cursos e faturamento da escola.
Passo a passo do desafio
- Passo 1: Carregar os três arquivos CSV no Pandas.
- Passo 2: Combinar a tabela de matriculas com a tabela de alunos (usando a coluna em comum id_aluno).
- Passo 3: Em seguida, combinar o resultado anterior com a tabela de cursos (usando a coluna id_curso).
- Resultado esperado: Um único DataFrame contendo o nome do aluno, o curso que ele faz e o preço da mensalidade.
Análise e Relatórios (As Perguntas)
- Filtro: Quem são os alunos com matrículas com o status “Ativo”?
- Agrupamento (groupby): Qual é o número total de matrículas ativas por curso?
- Cálculo Financeiro: Qual é o faturamento total mensal da escola considerando apenas as matrículas ativas?
- Análise de Perfil: Qual é a idade média dos alunos que se matricularam no curso de “Python para Dados”?
📊 1. Preparação (Os Três Datasets) com SQLite
O módulo sqlite3 já vem com o Python, então não precisa instalar nada. Cada bloco abaixo é uma célula.
Passo 1: criar o banco e as tabelas
import sqlite3
import pandas as pd
conn = sqlite3.connect('escola.db') # use ':memory:' para um banco temporário
conn.execute('PRAGMA foreign_keys = ON') # SQLite não valida FK por padrão
cur = conn.cursor()
cur.executescript("""
DROP TABLE IF EXISTS matriculas;
DROP TABLE IF EXISTS cursos;
DROP TABLE IF EXISTS alunos;
CREATE TABLE alunos (
id_aluno INTEGER PRIMARY KEY,
nome_aluno TEXT NOT NULL,
idade INTEGER,
cidade TEXT
);
CREATE TABLE cursos (
id_curso INTEGER PRIMARY KEY,
nome_curso TEXT NOT NULL,
preco_mensalidade REAL NOT NULL
);
CREATE TABLE matriculas (
id_matricula INTEGER PRIMARY KEY,
id_aluno INTEGER NOT NULL REFERENCES alunos(id_aluno),
id_curso INTEGER NOT NULL REFERENCES cursos(id_curso),
status TEXT NOT NULL CHECK (status IN ('Ativo', 'Cancelado', 'Trancado'))
);
""")Passo 2: inserir os dados
cur.executemany("INSERT INTO alunos VALUES (?,?,?,?)", [
(1, 'Ana Silva', 21, 'São Paulo'),
(2, 'Bruno Costa', 24, 'Rio de Janeiro'),
(3, 'Carlos Souza', 19, 'Belo Horizonte'),
(4, 'Diana Martins', 22, 'São Paulo'),
])
cur.executemany("INSERT INTO cursos VALUES (?,?,?)", [
(101, 'Python para Dados', 150),
(102, 'Web Design Moderno', 120),
(103, 'Banco de Dados SQL', 130),
])
cur.executemany("INSERT INTO matriculas VALUES (?,?,?,?)", [
(1001, 1, 101, 'Ativo'),
(1002, 1, 103, 'Ativo'),
(1003, 2, 101, 'Ativo'),
(1004, 3, 102, 'Cancelado'),
(1005, 4, 101, 'Ativo'),
])
conn.commit()Alternativa que mantém o espírito do “carregar CSV”: criar os CSVs como no original e usar
pd.read_csv(...).to_sql('alunos', conn, index=False, if_exists='replace'). A desvantagem é que isso não cria chaves nemCHECK.
Passo 3: consolidar com JOIN (e guardar numa VIEW)
cur.executescript("""
DROP VIEW IF EXISTS vw_consolidado;
CREATE VIEW vw_consolidado AS
SELECT m.id_matricula, a.nome_aluno, a.idade, c.nome_curso,
c.preco_mensalidade, m.status
FROM matriculas m
JOIN alunos a ON a.id_aluno = m.id_aluno
JOIN cursos c ON c.id_curso = m.id_curso;
""")
pd.read_sql_query("SELECT * FROM vw_consolidado", conn)A VIEW evita repetir os dois JOINs em cada pergunta.
Perguntas
1. Quem são os alunos com matrícula ativa?
pd.read_sql_query("""
SELECT DISTINCT nome_aluno
FROM vw_consolidado
WHERE status = 'Ativo'
ORDER BY nome_aluno
""", conn)Esperado: Ana, Bruno e Diana.
2. Matrículas ativas por curso (incluindo cursos com 0)
pd.read_sql_query("""
SELECT c.nome_curso, COUNT(m.id_matricula) AS matriculas_ativas
FROM cursos c
LEFT JOIN matriculas m
ON m.id_curso = c.id_curso AND m.status = 'Ativo'
GROUP BY c.id_curso, c.nome_curso
ORDER BY matriculas_ativas DESC
""", conn)Esperado: Python 3, SQL 1, Web Design 0. O filtro status = 'Ativo' está no ON, e não no WHERE, justamente para o curso sem ativos não sumir. Vale mostrar isso aos alunos.
3. Faturamento mensal (só ativas)
pd.read_sql_query("""
SELECT SUM(preco_mensalidade) AS faturamento_mensal
FROM vw_consolidado
WHERE status = 'Ativo'
""", conn)Esperado: R$ 580.
4. Idade média em “Python para Dados”
pd.read_sql_query("""
SELECT ROUND(AVG(idade), 1) AS idade_media
FROM vw_consolidado
WHERE nome_curso = 'Python para Dados'
""", conn)Esperado: 22,3.
Gráficos
import matplotlib.pyplot as plt
df_alunos = pd.read_sql_query("""
SELECT c.nome_curso, COUNT(m.id_matricula) AS total
FROM cursos c
LEFT JOIN matriculas m ON m.id_curso = c.id_curso AND m.status = 'Ativo'
GROUP BY c.id_curso
""", conn)
df_fat = pd.read_sql_query("""
SELECT nome_curso, SUM(preco_mensalidade) AS faturamento
FROM vw_consolidado
WHERE status = 'Ativo'
GROUP BY nome_curso
""", conn)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
df_alunos.plot(kind='bar', x='nome_curso', y='total', legend=False,
color='#4F81BD', edgecolor='black', ax=axes[0])
axes[0].set_title('Matrículas Ativas por Curso', fontweight='bold')
axes[0].set_xlabel('Curso'); axes[0].set_ylabel('Total')
axes[0].tick_params(axis='x', rotation=15)
axes[0].grid(axis='y', linestyle='--', alpha=0.7)
df_fat.set_index('nome_curso')['faturamento'].plot(
kind='pie', autopct='%1.1f%%', startangle=90, ax=axes[1],
wedgeprops={'edgecolor': 'black'})
axes[1].set_title('Faturamento por Curso (Ativos)', fontweight='bold')
axes[1].set_ylabel('')
plt.tight_layout()
plt.show()Fechar e (opcional) baixar o banco
conn.close()
# from google.colab import files
# files.download('escola.db') # abre no DB Browser for SQLite, por exemploDica didática
Uma sequência boa é resolver cada pergunta primeiro em Pandas e depois em SQL, comparando os resultados lado a lado. Observe que merge = JOIN, df[df.col == x] = WHERE e groupby = GROUP BY. Se quiser, posso montar essa versão comparativa em duas colunas ou transformar tudo em um notebook .ipynb pronto para o Colab.