Capítulo 4, Básico
Series: a coluna rotulada
Uma `Series` é a peça mais simples do Pandas: valores mais um rótulo para cada um. Entendê-la bem evita metade dos tropeços com o `DataFrame`, que é feito delas.
Criar uma Series
Uma Series pode nascer de uma lista, de um dicionário ou de um array do NumPy. Sem índice explícito, os rótulos são 0, 1, 2... Com um índice, os rótulos são o que você quiser, e as chaves de um dicionário viram o índice sozinhas:
import numpy as np
import pandas as pd
salarios = pd.Series([4500, 6500, 5000])
print(salarios)
com_nome = pd.Series([4500, 6500, 5000], index=["Ana", "Bruno", "Carla"], name="salario")
print(com_nome)
print(pd.Series({"Ana": 4500, "Bruno": 6500}))
0 4500
1 6500
2 5000
dtype: int64
Ana 4500
Bruno 6500
Carla 5000
Name: salario, dtype: int64
Ana 4500
Bruno 6500
dtype: int64
Uma Series tem valores (os dados), índice (os rótulos) e, opcionalmente, um nome, que vira o nome da coluna quando ela entra em um DataFrame:
print(com_nome.name, com_nome.index.tolist(), com_nome.to_numpy())
salario ['Ana', 'Bruno', 'Carla'] [4500 6500 5000]
Por rótulo ou por posição
O índice dá duas formas de acessar um valor: pelo rótulo (.loc) e pela posição (.iloc). As duas só coincidem quando o índice é 0, 1, 2... e nunca se deve confiar nisso:
print(com_nome.loc["Bruno"], com_nome.iloc[1])
print(com_nome.iloc[-1], com_nome.loc["Ana":"Bruno"].tolist())
6500 6500
5000 [4500, 6500]
Repare que a fatia por rótulo ("Ana":"Bruno") inclui os dois extremos, ao contrário das fatias do Python. No Pandas 3, o acesso com_nome[1] com um inteiro em um índice de texto não cai mais para a posição, como acontecia nas versões antigas:
try:
com_nome[1]
except KeyError as erro:
print("KeyError:", erro)
KeyError: 1
Use sempre .loc ou .iloc, que dizem o que você quer.
Operações vetorizadas, e o alinhamento por rótulo
As operações valem para a Series inteira, como no NumPy. O que muda é que a soma de duas Series alinha pelos rótulos, e rótulos que só existem de um lado viram NaN:
print((com_nome * 1.1).round(0).tolist())
print((com_nome > 4800).tolist())
print(com_nome.mean())
bonus = pd.Series({"Ana": 500, "Carla": 300, "Diego": 100})
print(com_nome + bonus)
print(com_nome.add(bonus, fill_value=0))
[4950.0, 7150.0, 5500.0]
[False, True, True]
5333.333333333333
Ana 5000.0
Bruno NaN
Carla 5300.0
Diego NaN
dtype: float64
Ana 5000.0
Bruno 6500.0
Carla 5300.0
Diego 100.0
dtype: float64
O Bruno não tem bônus, e o Diego não tem salário, então a soma comum dá NaN nos dois. O método add com fill_value=0 trata o lado que falta como zero, e aí ninguém some. Essa é a diferença que mais pega quem vem do NumPy: um array soma posição com posição, e a Series soma rótulo com rótulo.
Texto em uma Series
O acessor .str aplica as operações de texto a todos os valores de uma só vez, sem laço, e deixa os ausentes passarem:
nomes = pd.Series([" ana souza ", "BRUNO LIMA", None])
print(nomes.str.strip().str.title().tolist())
['Ana Souza', 'Bruno Lima', nan]
Um acessor é um conjunto de ferramentas
O
.strserve para texto, o.dtpara datas (capítulo 18) e o.catpara categorias (capítulo 19). Cada um só funciona no tipo certo de coluna, e é isso que mantém a API organizada.
Exercício 1
Salário líquido por funcionário
Dada a Series com_nome, crie liquido, que desconte 10% de cada salário, e confira o valor da Carla. Depois some um bônus de 200 só para quem estiver em bonus (e deixe os outros como estavam).