Olá, Eduardo. Como vai?
Parabéns pela excelente resolução do desafio! O seu código demonstra que você assimilou muito bem os conceitos de manipulação, tratamento e filtragem de dados utilizando a biblioteca Pandas.
A escolha de preencher as notas nulas com 0 através do fillna(0) foi bem coerente, assim como a estratégia de capturar os índices com .index para depois aplicar o .drop(). A filtragem condicional e a exportação para CSV também ficaram impecáveis.
Para agregar ainda mais valor ao seu projeto e expandir o seu domínio sobre o Pandas, quero compartilhar duas dicas de boas práticas e otimização baseadas nas soluções que você construiu:
1. Otimizando a remoção de registros com o método .isin()
No exercício 2, você utilizou o método .query() combinando os nomes com o operador | (OU). Essa abordagem funciona perfeitamente para dois nomes, mas imagine se você precisasse remover uma lista com 20 alunos? O texto do .query() ficaria gigantesco.
Uma alternativa mais elegante e escalável no Pandas é utilizar o método .isin() combinado com o operador de negação do til (~). Esse operador diz ao Pandas: "selecione tudo, exceto o que estiver nesta lista".
Veja como o código fica mais limpo:
# Criamos uma lista com os nomes a remover
remover = ['Alice', 'Carlos']
# O ~ filtra a base mantendo apenas quem NÃO está na lista
dados = dados[~dados['Nome'].isin(remover)]
Dessa forma, você elimina a necessidade de buscar os índices e aplicar o .drop(), realizando a filtragem e a remoção em uma única linha de código direcionada.
2. Evitando o SettingWithCopyWarning no exercício extra
No desafio extra, ao executar a linha alunos_aprovados['Notas'] = alunos_aprovados['Notas'].replace(7.0, 8.0), é muito provável que o ambiente (como o Jupyter Notebook) tenha exibido um aviso em vermelho chamado SettingWithCopyWarning.
Esse aviso acontece porque a variável alunos_aprovados nasceu de uma filtragem direta (dados[dados['Notas'] >= 7]). Para o Pandas, ela não é um DataFrame totalmente independente, mas sim uma "visão" (uma cópia vinculada) do DataFrame original dados. Quando você tenta modificar uma coluna dessa cópia, o Pandas avisa que isso pode gerar comportamentos inesperados.
Para evitar esse aviso e garantir que você está trabalhando com um objeto isolado na memória, a boa prática é utilizar o método .copy() logo após fazer o filtro. Veja a diferença:
# O .copy() garante que alunos_aprovados é um novo DataFrame independente
alunos_aprovados = dados[dados['Notas'] >= 7].copy()
# Agora você pode alterar as notas sem receber avisos do Pandas
alunos_aprovados['Notas'] = alunos_aprovados['Notas'].replace(7.0, 8.0)
Garantir essa independência de DataFrames impede bugs sutis em projetos maiores de Ciência de Dados.
Você foi muito bem em todas as etapas, inclusive na busca pelo método .replace() no desafio extra. Continue com esse ótimo ritmo de estudos!
Espero que possa ter lhe ajudado!