Pular para o conteúdo

    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:

    ambiente/cap03_modelo_de_dados.pylinhas 10 a 15
    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)
    
    Saída
    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:

    ambiente/cap03_modelo_de_dados.pylinhas 20 a 22
    print(pd.Series([24, 25, 26]).dtype)
    print(pd.Series([24, 25, None]).dtype)
    print(pd.Series([24, 25, None], dtype="Int64").dtype)
    
    Saída
    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:

    ambiente/cap03_modelo_de_dados.pylinhas 27 a 29
    print(pd.Series(["a", "b", "c"]).dtype)
    print(pd.Series(["a", 1, 2.5]).dtype)
    print(df["nome"].dtype, df["departamento"].dtype)
    
    Saída
    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:

    ambiente/cap03_modelo_de_dados.pylinhas 34 a 37
    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())
    
    Saída
    {'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:

    ambiente/cap03_modelo_de_dados.pylinhas 42 a 44
    memoria = df.memory_usage(deep=True)
    print(memoria.index.tolist())
    print(bool(memoria["nome"] > memoria["idade"]))
    
    Saída
    ['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.

    ConceitoO que significa
    dtype por colunaCada coluna é um array com o seu próprio tipo
    NaNUm valor decimal especial: coluna de inteiros com ausentes vira float64
    Int64, boolean, stringTipos anuláveis, que guardam ausentes sem converter
    strO tipo de texto do Pandas 3 (antes, object)
    ÍndiceRó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.