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ção | A pergunta que ela responde |
|---|---|
.head() | Qual é a cara dos dados? |
.tail() | Como são as últimas linhas? |
.sample() | Como é uma fatia aleatória? |
.shape | Quantas linhas e colunas existem? |
.dtypes | Que tipo de dado há em cada coluna? |
.info() | Qual é a estrutura, e onde faltam valores? |
.describe() | Como são as estatísticas básicas? |
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))
(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:
df.info(memory_usage=False)
<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:
print(df.describe().round(2))
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:
print(df.describe(include="str"))
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):
print(df["departamento"].value_counts())
print(df["departamento"].value_counts(dropna=False).head(3))
print(df["cidade"].nunique(), df["cidade"].unique().tolist())
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:
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())
(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 odescribe()são rápidos e dão uma sensação de controle. Osample(), ovalue_counts()e oisna().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.