Pular para o conteúdo

    Capítulo 6, Básico

    Inspecionar antes de analisar

    Nunca analise logo depois de carregar. Leia os dados primeiro. Cada função abaixo responde a uma pergunta específica, e juntas elas mostram os problemas que uma conta "que roda sem erro" esconderia.

    As perguntas e as funções

    FunçãoA pergunta que ela responde
    .head()Qual é a cara dos dados?
    .tail()Como são as últimas linhas?
    .sample()Como é uma fatia aleatória?
    .shapeQuantas linhas e colunas existem?
    .dtypesQue tipo de dado há em cada coluna?
    .info()Qual é a estrutura, e onde faltam valores?
    .describe()Como são as estatísticas básicas?
    basico/cap06_inspecionar.pylinhas 10 a 19
    import pandas as pd
    
    pd.set_option("display.width", 170)
    pd.set_option("display.max_columns", 20)
    df = pd.read_csv("dados/funcionarios_15.csv")
    
    print(df.shape)
    print(df.head(3))
    print(df.tail(2))
    print(df.sample(3, random_state=1))
    
    Saída
    (15, 10)
       id_funcionario          nome  departamento          cidade  idade  salario  experiencia  nota_desempenho  projetos_concluidos data_admissao
    0             101     Ana Souza    Engenharia       São Paulo   24.0     4500            1                4                    2    2021-01-10
    1             102    Bruno Lima     Marketing  Belo Horizonte   25.0  R$ 4650            2                5                    3    2022-02-11
    2             103  Carla Mendes   engenharia     Porto Alegre   26.0     4800            3                6                    4    2023-03-12
        id_funcionario            nome      departamento          cidade  idade salario  experiencia  nota_desempenho  projetos_concluidos data_admissao
    13             114  Nelson Ribeiro  Ciência de Dados  Rio de Janeiro   27.0    6450            6                5                    5    2022-05-23
    14             101       Ana Souza                RH        Curitiba   28.0    6600            7                6                    6    2023-06-24
       id_funcionario            nome      departamento          cidade  idade  salario  experiencia  nota_desempenho  projetos_concluidos data_admissao
    3             104     Diego Rocha  Ciência de Dados  Rio de Janeiro   27.0      NaN            4                7                    5    2024-04-13
    7             108  Henrique Costa          Finanças    Porto Alegre   31.0  R$ 5550            8                5                    9    2024-08-17
    6             107  Gabriela Nunes         Marketing  Belo Horizonte    NaN     5400            7                4                    8    2023-07-16
    

    O head() sempre parece limpo, porque os problemas costumam morar mais fundo no arquivo. O sample() dá uma primeira impressão mais honesta de dados bagunçados, e o random_state fixa o sorteio para o resultado ser reproduzível.

    `info()`: a estrutura e os ausentes

    O info() mostra, por coluna, quantos valores não ausentes existem e o tipo. Comparar com o número total de linhas revela onde há buracos:

    basico/cap06_inspecionar.pylinha 24
    df.info(memory_usage=False)
    
    Saída
    <class 'pandas.DataFrame'>
    RangeIndex: 15 entries, 0 to 14
    Data columns (total 10 columns):
     #   Column               Non-Null Count  Dtype  
    ---  ------               --------------  -----  
     0   id_funcionario       15 non-null     int64  
     1   nome                 15 non-null     str    
     2   departamento         14 non-null     str    
     3   cidade               13 non-null     str    
     4   idade                14 non-null     float64
     5   salario              13 non-null     str    
     6   experiencia          15 non-null     int64  
     7   nota_desempenho      15 non-null     int64  
     8   projetos_concluidos  15 non-null     int64  
     9   data_admissao        15 non-null     str    
    dtypes: float64(1), int64(4), str(5)
    

    Cada coluna deveria ter 15 valores. A departamento tem 14, a cidade 13, a idade 14 e o salario 13: os ausentes. E repare no tipo do salario, que é str, e não número.

    `describe()` e o salário que sumiu

    O describe() resume apenas as colunas numéricas, por padrão:

    basico/cap06_inspecionar.pylinha 29
    print(df.describe().round(2))
    
    Saída
           id_funcionario  idade  experiencia  nota_desempenho  projetos_concluidos
    count           15.00  14.00        15.00            15.00                15.00
    mean           107.07  27.50         4.27             6.20                 5.67
    std              4.37   2.88         2.25             1.74                 2.85
    min            101.00  24.00         1.00             4.00                 2.00
    25%            103.50  25.25         2.50             5.00                 3.50
    50%            107.00  27.00         4.00             6.00                 5.00
    75%            110.50  28.75         6.00             7.50                 7.50
    max            114.00  33.00         8.00             9.00                11.00
    

    Olhe as colunas da tabela: o salario não aparece. Não é um erro do Pandas, é um sintoma: a coluna é texto (por causa dos R$ 4650), e por isso não entra nas estatísticas. Quando uma coluna que você esperava ver some do describe(), o primeiro suspeito é o tipo dela.

    Outra observação: o id_funcionario aparece, porque é um número, mas um identificador não é uma quantidade, e a média dele não significa nada. O describe não sabe disso, e quem sabe é você.

    Para incluir as colunas de texto, peça:

    basico/cap06_inspecionar.pylinha 31
    print(df.describe(include="str"))
    
    Saída
                 nome departamento     cidade salario data_admissao
    count          15           14         13      13            15
    unique         14            7          5      13            15
    top     Ana Souza    Marketing  São Paulo    4500    2021-01-10
    freq            2            3          3       1             1
    

    Contar os valores

    Duas perguntas aparecem o tempo todo: quais valores existem, e quantas vezes cada um aparece. O value_counts responde às duas, e o dropna=False inclui os ausentes na contagem (que, por padrão, ficam de fora):

    basico/cap06_inspecionar.pylinhas 36 a 38
    print(df["departamento"].value_counts())
    print(df["departamento"].value_counts(dropna=False).head(3))
    print(df["cidade"].nunique(), df["cidade"].unique().tolist())
    
    Saída
    departamento
    Marketing           3
    RH                  3
    Engenharia          2
    Ciência de Dados    2
    Finanças            2
     engenharia         1
    MARKETING           1
    Name: count, dtype: int64
    departamento
    Marketing     3
    RH            3
    Engenharia    2
    Name: count, dtype: int64
    5 ['São Paulo', 'Belo Horizonte', 'Porto Alegre', 'Rio de Janeiro', nan, 'Curitiba']
    

    O departamento mostra sete grupos que deveriam ser cinco: engenharia (com espaços) e Engenharia são o mesmo departamento, e MARKETING e Marketing também. Nenhum erro foi levantado, mas qualquer agrupamento por departamento daria resultados errados. É por isso que se inspeciona antes de analisar.

    Um roteiro de inspeção

    Estas quatro linhas, nesta ordem, são o que eu rodo em todo conjunto de dados novo:

    basico/cap06_inspecionar.pylinhas 43 a 46
    print(df.shape)
    print(df.dtypes.value_counts())
    print(df.isna().sum()[lambda s: s > 0])
    print(df.duplicated().sum(), df.duplicated("id_funcionario").sum())
    
    Saída
    (15, 10)
    str        5
    int64      4
    float64    1
    Name: count, dtype: int64
    departamento    1
    cidade          2
    idade           1
    salario         2
    dtype: int64
    0 1
    

    O tamanho, os tipos, onde faltam valores, e se existem repetições (inteiras ou só pelo identificador). Os dois números do final já apontam um problema: nenhuma linha é inteiramente repetida, mas existe um identificador repetido.

    Performance e honestidade

    O head() e o describe() são rápidos e dão uma sensação de controle. O sample(), o value_counts() e o isna().sum() são os que acham os problemas. Se você só tem tempo de rodar três coisas, rode esses.

    Exercício 1

    Um resumo de qualidade

    Escreva resumo_qualidade(tabela), que devolva um dicionário com linhas, colunas, ausentes (o total de valores ausentes) e ids_repetidos (quantos ids aparecem repetidos), e confira para df.