# Series, DataFrames e índices

Construir tabelas, inspecionar tipos, ler CSV e compreender alinhamento por rótulos.

Página: https://resumos.rgo.pt/cadeiras/ct-iadp/series-dataframes/

Uma **Series** é uma sequência de valores com rótulos. Um **DataFrame** é uma tabela com colunas e um índice de linhas. Cada coluna pode ter um tipo diferente. O índice identifica as linhas nas operações de seleção e alinhamento.

## Construir uma tabela

```python
import pandas as pd
vendas = pd.DataFrame({
    "cidade": ["Porto", "Porto", "Braga", "Braga"],
    "produto": ["pão", "leite", "pão", "arroz"],
    "quantidade": [2, 3, 5, 4],
    "preco": [1.5, 1.2, 1.5, 2.0],
})
print(vendas.to_string(index=False))
print(vendas.shape)
print(vendas.dtypes)
```

Há quatro observações e quatro colunas, por isso `shape` é `(4, 4)`. Cada linha representa uma venda. Quantidade é uma contagem; preço é o preço por unidade em euros. Ainda não há uma coluna de total.

`vendas["quantidade"]` devolve uma Series. `vendas[["quantidade"]]` devolve um DataFrame com uma coluna. A diferença afeta a forma e as operações seguintes.

`pd.Series([10, 20], index=["a", "b"])` associa 10 a `a` e 20 a `b`. A série não é apenas um array com uma legenda: os rótulos participam nas operações.

## Inspecionar antes de transformar

| Operação | Pergunta |
| --- | --- |
| `df.head(3)` | Como são as primeiras três linhas? |
| `df.tail(3)` | Como termina a tabela? |
| `df.shape` | Quantas linhas e colunas tem? |
| `df.columns` | Que nomes têm as colunas? |
| `df.index` | Como estão identificadas as linhas? |
| `df.dtypes` | Que tipo tem cada coluna? |
| `df.info()` | Quantos valores não nulos existem e que tipos há? |

`info()` escreve informação e devolve `None`. Os tipos de strings podem aparecer como `object`, `string` ou `str`, consoante a versão e a construção. Confere operações e significado dos valores, não apenas o nome visual do tipo.

## Ler um CSV

```
vendas = pd.read_csv("vendas.csv", sep=";", decimal=",")
```

`sep` indica o separador dos campos. `decimal` indica o separador decimal dentro de valores numéricos. `dtype={"codigo": "string"}` evita que um código como `"0012"` perca os zeros iniciais. `usecols` lê apenas as colunas necessárias e `parse_dates` pode interpretar colunas de datas, mas confere o formato recebido.

No navegador, os exemplos usam `StringIO` e dados visíveis. Num notebook local, substitui esse objeto pelo caminho do ficheiro. Exporta uma tabela com `df.to_csv("resultado.csv", index=False)` se o índice não fizer parte dos dados; sem esse argumento, pandas escreve-o como mais uma coluna.

## O alinhamento usa rótulos

```python
import pandas as pd
a = pd.Series([10, 20], index=["Porto", "Braga"])
b = pd.Series([1, 2], index=["Braga", "Porto"])
print((a + b).sort_index().to_dict())
c = pd.Series([3], index=["Aveiro"])
print(a.add(c, fill_value=0).sort_index().to_dict())
```

O primeiro resultado é Braga 21 e Porto 12. Não somamos a primeira posição à primeira posição: somamos os valores da mesma cidade. Quando um rótulo só está num dos lados, a operação normal produz um valor em falta nesse rótulo. `fill_value=0` é adequado aqui apenas porque interpretamos a ausência como nenhuma quantidade para acrescentar.

## Tornar o índice explícito

`df.set_index("codigo")` devolve uma tabela com essa coluna como índice. `reset_index()` devolve o índice para uma coluna e cria posições de zero em diante. Um índice pode ter valores repetidos. Se o problema exige uma chave única, verifica `df.index.is_unique` ou `df["codigo"].is_unique` em vez de o assumir.

## Exercícios

Somar pelo rótulo

a tem Porto: 10 e Braga: 20. b tem Braga: 1 e Porto: 2. Quanto vale (a + b) no rótulo Porto?

Primeira pista

As Series alinham rótulos, não posições.

Mais uma pista

No rótulo Porto, os valores são 10 e 2.

Ver solução

O resultado é 12. Braga recebe 20 + 1 = 21, mesmo aparecendo noutra posição de b.

#### Erros frequentes

Somar 10 a 1 por ser a primeira posição.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/series-dataframes/#o-alinhamento-usa-r%C3%B3tulos)

Escolher Series ou DataFrame

Qual é a expressão que conserva a coluna x como um DataFrame de uma coluna?

Primeira pista

Uma lista de nomes pede uma tabela com essas colunas.

Mais uma pista

Mesmo com um único nome, os parênteses retos interiores criam a lista.

Ver solução

df\[\[“x”\]\] devolve um DataFrame. A sua forma é número de linhas por uma coluna.

*   **df\[\["x"\]\].** A lista de colunas conserva uma tabela.
*   **df\["x"\].** Seleciona uma Series.
*   **df.loc\["x"\].** Procura um rótulo de linha, não esta seleção de coluna.

#### Erros frequentes

Tratar as duas seleções como sempre equivalentes nas operações seguintes.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/series-dataframes/#construir-uma-tabela)
