Capítulo 3, Ambiente
Como o Pandas guarda os dados
Cada coluna é um array com o seu próprio tipo. Entender isso explica por que uma coluna de inteiros vira decimal quando falta um valor, por que o texto vira `object` ou `str`, e quanta memória o seu `DataFrame` usa.
Uma coluna, um tipo
No NumPy, o array inteiro tem um dtype. No Pandas, cada coluna tem o seu. É isso que permite uma tabela misturar números, textos e datas:
import numpy as np
import pandas as pd
pd.set_option("display.width", 170)
df = pd.read_csv("dados/funcionarios_15.csv")
print(df.dtypes)
id_funcionario int64
nome str
departamento str
cidade str
idade float64
salario str
experiencia int64
nota_desempenho int64
projetos_concluidos int64
data_admissao str
dtype: object
O dtypes é a primeira coisa que eu olho em qualquer conjunto de dados. Três colunas mereciam atenção: o salario é str (texto), e não número, o que explica por que ele não aparece em um describe(); a idade é float64, embora idades sejam inteiras; e a data_admissao é str, e não uma data.
Por que a idade virou decimal
Os inteiros do NumPy não têm representação para "ausente". Quando uma coluna de inteiros ganha um valor ausente, o Pandas a converte para decimal, porque o NaN é um valor decimal. Dá para ver a regra acontecendo:
print(pd.Series([24, 25, 26]).dtype)
print(pd.Series([24, 25, None]).dtype)
print(pd.Series([24, 25, None], dtype="Int64").dtype)
int64
float64
Int64
O terceiro caso usa o tipo anulável Int64 (com I maiúsculo), que guarda inteiros e aceita ausentes sem converter. O capítulo 16 trata dele em profundidade.
Texto: `str` no Pandas 3, `object` antes
Até o Pandas 2, colunas de texto eram do tipo object, que guarda qualquer objeto Python e é lento e pesado. O Pandas 3 tem um tipo próprio, str, para texto. Um object só aparece quando a coluna mistura tipos de verdade:
print(pd.Series(["a", "b", "c"]).dtype)
print(pd.Series(["a", 1, 2.5]).dtype)
print(df["nome"].dtype, df["departamento"].dtype)
str
object
str str
Se você ler material mais antigo que diz "colunas de texto são object", saiba que isso era verdade nas versões anteriores. Neste curso, o que importa é a mesma ideia: uma coluna que deveria ser número e aparece como texto (ou object) é um problema de limpeza, não um erro do Pandas.
O índice também é um objeto
Toda Series e todo DataFrame têm um índice, que rotula as linhas. Por padrão, é uma sequência de 0 até n-1, mas pode ser qualquer coisa: ids, datas, nomes. As operações alinham por rótulo, e não por posição, e é isso que mais diferencia o Pandas do NumPy:
a = pd.Series([10, 20, 30], index=["x", "y", "z"])
b = pd.Series([1, 2, 3], index=["z", "y", "x"])
print((a + b).to_dict())
print((a.to_numpy() + b.to_numpy()).tolist())
{'x': 13, 'y': 22, 'z': 31}
[11, 22, 33]
O Pandas somou x com x (10 + 3), y com y (20 + 2) e z com z (30 + 1), apesar da ordem diferente. O NumPy somou posição com posição. Nenhum dos dois está errado, mas misturar os dois modelos sem perceber é uma fonte clássica de resultados trocados.
Quanto custa
memory_usage(deep=True) mostra os bytes de cada coluna, inclusive o conteúdo dos textos. Sem o deep=True, ele só conta os ponteiros, e subestima o custo dos textos:
memoria = df.memory_usage(deep=True)
print(memoria.index.tolist())
print(bool(memoria["nome"] > memoria["idade"]))
['Index', 'id_funcionario', 'nome', 'departamento', 'cidade', 'idade', 'salario', 'experiencia', 'nota_desempenho', 'projetos_concluidos', 'data_admissao']
True
Uma coluna de texto custa mais do que uma numérica do mesmo tamanho, e é por isso que, em conjuntos grandes, o tipo certo de cada coluna decide se os dados cabem na memória. O capítulo 29 mostra como reduzir esse custo.
| Conceito | O que significa |
|---|---|
dtype por coluna | Cada coluna é um array com o seu próprio tipo |
NaN | Um valor decimal especial: coluna de inteiros com ausentes vira float64 |
Int64, boolean, string | Tipos anuláveis, que guardam ausentes sem converter |
str | O tipo de texto do Pandas 3 (antes, object) |
| Índice | Rótulos das linhas. As operações alinham por rótulo |
Exercício 1
Descobrir o tipo certo
Dado df = pd.read_csv("dados/funcionarios_15.csv"), escreva colunas_com_tipo_errado(df), que devolva a lista dos nomes das colunas que deveriam ser numéricas (idade e salario) mas que o Pandas não leu como inteiras. Use pd.api.types.is_integer_dtype.