Pular para o conteúdo

    Capítulo 14, Básico

    O fluxo completo, do começo ao fim

    Todos os passos que você aprendeu, encadeados como em uma análise de verdade, sobre o mesmo conjunto de dados. O curso original tem aqui uma armadilha no próprio roteiro, e eu a mostro primeiro.

    O roteiro que quebra

    O roteiro do curso original seleciona só algumas colunas (passo 3) e depois tenta preencher a idade e converter a data_admissao (passos 5 a 8), que ele acabou de descartar. Essa ordem falha, e é um bom exemplo de que a ordem dos passos faz parte da análise:

    basico/cap14_fluxo_completo.pylinhas 10 a 22
    import numpy as np
    import pandas as pd
    
    pd.set_option("display.width", 170)
    pd.set_option("display.max_columns", 20)
    
    bruto = pd.read_csv("dados/funcionarios_15.csv")
    so_essas = bruto[["id_funcionario", "nome", "departamento", "cidade", "salario",
                      "experiencia", "nota_desempenho", "projetos_concluidos"]]
    try:
        so_essas["idade"].fillna(so_essas["idade"].median())
    except KeyError as erro:
        print("KeyError:", erro)
    
    Saída
    KeyError: 'idade'
    

    A regra que eu sigo: selecione colunas só no final, ou depois de tratar tudo o que você vai precisar delas.

    O fluxo na ordem certa

    Eu escrevo o fluxo como uma função: ela recebe o caminho do arquivo e devolve a tabela tratada e o resumo. Assim, o fluxo é testável, repetível e pode ser chamado com outro arquivo:

    basico/cap14_fluxo_completo.pylinhas 27 a 73
    CANONICO = {"engenharia": "Engenharia", "marketing": "Marketing", "finanças": "Finanças",
                "ciência de dados": "Ciência de Dados", "rh": "RH"}
    
    
    def analisar(caminho: str) -> tuple[pd.DataFrame, pd.DataFrame]:
        # 1 e 2. Carregar e inspecionar
        df = pd.read_csv(caminho)
        assert df["id_funcionario"].notna().all()
    
        # 3. Corrigir os tipos (antes de qualquer conta)
        df["salario"] = pd.to_numeric(df["salario"].str.replace("R$ ", "", regex=False))
        df["data_admissao"] = pd.to_datetime(df["data_admissao"])
    
        # 4. Padronizar o texto
        df["departamento"] = df["departamento"].str.strip().str.lower().map(CANONICO)
        df["departamento"] = df["departamento"].fillna("Não informado")
        df["cidade"] = df["cidade"].fillna("Não informada")
    
        # 5. Remover repetições pelo identificador
        df = df.drop_duplicates(subset=["id_funcionario"], keep="first")
    
        # 6. Preencher os ausentes numéricos com a mediana
        df["salario"] = df["salario"].fillna(df["salario"].median())
        df["idade"] = df["idade"].fillna(df["idade"].median())
    
        # 7. Criar atributos
        df["nivel"] = pd.cut(df["experiencia"], bins=[0, 2, 5, np.inf], right=False,
                             labels=["Iniciante", "Pleno", "Sênior"]).astype(str)
    
        # 8. Selecionar e ordenar, só no final
        df = df[["id_funcionario", "nome", "departamento", "cidade", "idade", "salario", "experiencia",
                 "nota_desempenho", "projetos_concluidos", "nivel"]]
        df = df.sort_values(["nota_desempenho", "projetos_concluidos", "id_funcionario"],
                            ascending=[False, False, True]).reset_index(drop=True)
    
        # 9. Resumir por departamento
        resumo = df.groupby("departamento").agg(
            salario_medio=("salario", "mean"),
            nota_media=("nota_desempenho", "mean"),
            funcionarios=("id_funcionario", "count"),
        ).reset_index()
        return df, resumo
    
    
    tabela, resumo = analisar("dados/funcionarios_15.csv")
    print(tabela.shape, int(tabela.isna().sum().sum()))
    print(resumo.round(1))
    
    Saída
    (14, 10) 0
           departamento  salario_medio  nota_media  funcionarios
    0  Ciência de Dados         5962.5         6.0             2
    1        Engenharia         5100.0         6.0             3
    2          Finanças         5925.0         4.5             2
    3         Marketing         5362.5         6.8             4
    4     Não informado         5700.0         6.0             1
    5                RH         5287.5         7.5             2
    

    Todos os valores ausentes foram tratados (o 0 depois da forma), e o departamento "sem informação" ganhou um nome (Não informado) em vez de sumir do resumo. A soma da coluna funcionarios fecha com as 14 linhas.

    As perguntas de negócio

    A partir da tabela tratada, cada pergunta é uma ou duas linhas:

    basico/cap14_fluxo_completo.pylinhas 78 a 83
    melhor_salario = resumo.loc[resumo["salario_medio"].idxmax()]
    melhor_nota = resumo.loc[resumo["nota_media"].idxmax()]
    print(melhor_salario["departamento"], round(float(melhor_salario["salario_medio"]), 1))
    print(melhor_nota["departamento"], round(float(melhor_nota["nota_media"]), 2))
    
    print(tabela["departamento"].value_counts().to_dict())
    
    Saída
    Ciência de Dados 5962.5
    RH 7.5
    {'Marketing': 4, 'Engenharia': 3, 'RH': 2, 'Ciência de Dados': 2, 'Finanças': 2, 'Não informado': 1}
    

    Qual cidade tem mais funcionários? O jeito curto (value_counts().idxmax()) devolve uma cidade, e aqui isso esconde um empate:

    basico/cap14_fluxo_completo.pylinhas 85 a 88
    por_cidade = tabela["cidade"].value_counts()
    print(por_cidade.to_dict())
    empatadas = por_cidade[por_cidade == por_cidade.max()].index.tolist()
    print(sorted(empatadas), por_cidade.idxmax() in empatadas)
    
    Saída
    {'São Paulo': 3, 'Rio de Janeiro': 3, 'Porto Alegre': 3, 'Não informada': 2, 'Belo Horizonte': 2, 'Curitiba': 1}
    ['Porto Alegre', 'Rio de Janeiro', 'São Paulo'] True
    

    Três cidades empatam no topo, e o idxmax escolheu uma sem avisar. A resposta honesta é a lista das empatadas. A mesma lição do capítulo 12 vale para o "top 5":

    basico/cap14_fluxo_completo.pylinhas 90 a 91
    top5 = tabela.head(5)
    print(top5[["nome", "nota_desempenho", "projetos_concluidos"]])
    
    Saída
                  nome  nota_desempenho  projetos_concluidos
    0    Felipe Araújo                9                    7
    1    Lucas Barbosa                9                    3
    2  Elisa Fernandes                8                    6
    3     Karina Alves                8                    2
    4     João Pereira                7                   11
    

    E a pergunta "quem tem mais experiência completa mais projetos?" se responde com a correlação:

    basico/cap14_fluxo_completo.pylinha 93
    print(round(float(tabela["experiencia"].corr(tabela["projetos_concluidos"])), 3))
    
    Saída
    0.217
    

    O valor, 0,22, indica uma relação positiva, mas fraca: neste conjunto, quem tem mais experiência tende a concluir um pouco mais projetos, mas a experiência explica pouco. Com 14 pessoas, é uma indicação, e não uma conclusão.

    Exercício 1

    Mais uma pergunta de negócio

    Usando tabela, descubra quantos funcionários de cada nível (Iniciante, Pleno, Sênior) têm nota de desempenho 8 ou mais, e confira que o total é igual ao número de linhas com nota 8 ou mais.