Manipulação de dados com Pandas
Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.
1. Resumo
Vamos ver uma pequena introdução à biblioteca de manipulação e análise de dados Pandas
. Com ela, poderemos manejar e processar dados tabulares, o que nos ajudará a operar com eles e obter informações de uma maneira muito valiosa.
2. O que é Pandas?
Pandas é uma biblioteca de Python que está desenhada para que o trabalho com dados relacionais ou etiquetados seja fácil e intuitivo Pandas é projetado para muitos tipos diferentes de dados:
- Dados tabulares com colunas de tipos heterogêneos, como em uma tabela SQL ou uma planilha do Excel* Dados de séries temporais ordenados e desordenados (não necessariamente de frequência fixa).* Dados matriciais arbitrários (homogêneos ou heterogêneos) com rótulos de linha e coluna* Qualquer outra forma de conjuntos de dados observacionais/estatísticos. Não é necessário etiquetar os dados de forma alguma para colocá-los em uma estrutura de dados do pandas.
As duas principais estruturas de dados do Pandas são as Series
(unidimensional) e os DataFrames
(bidimensional). O Pandas é construído sobre o NumPy e destina-se a integrar-se bem dentro de um ambiente científico computacional com muitas outras bibliotecas de terceiros.
Para os cientistas de dados, o trabalho com dados geralmente se divide em várias etapas: coletar e limpar dados, analisá-los/modelá-los e, em seguida, organizar os resultados da análise em uma forma adequada para traçá-los ou mostrá-los em forma de tabela. pandas é a ferramenta ideal para todas essas tarefas.
Outra característica é que pandas é rápido, muitos dos algoritmos de baixo nível foram construídos em C
.
2.1. Pandas como pd
Geralmente, ao importar pandas, costuma-se importar com o alias de pd
import pandas as pdprint(pd.__version__)
1.0.1
3. Estruturas de dados do Pandas
Em Pandas, existem dois tipos de estruturas de dados: as Series
e os DataFrame
s
3.1. Séries
O tipo de dado Series
é uma matriz rotulada unidimensional capaz de conter qualquer tipo de dados (inteiros, cadeias, números de ponto flutuante, objetos Python, etc.). Está dividida em índices.
Para criar um tipo de dado Serie
a forma mais comum é
serie = pd.Series(data, index=index)
Onde data
pode ser* Um dicionário* Uma lista ou tupla* Um ndarray do Numpy* Um valor escalar
Como um dos tipos de dados pode ser um ndarray do NumPy, importamos o NumPy para poder usá-lo
import numpy as np
3.1.1. Series a partir de um dicionário
import numpy as npdiccionario = {"b": 1, "a": 0, "c": 2}serie = pd.Series(diccionario)serie
b 1a 0c 2dtype: int64
Se um índice for passado, os valores dos dados correspondentes aos rótulos do índice serão extraídos. Se não existirem, eles serão criados como NaN
(not a number)
diccionario = {"b": 1, "a": 0, "c": 2}serie = pd.Series(diccionario, index=["b", "c", "d", "a"])serie
b 1.0c 2.0d NaNa 0.0dtype: float64
3.1.2. Séries de uma lista ou tupla
Se os dados vierem de uma lista ou tupla e nenhum índice for passado, será criado um com valores [0, ..., len(data)-1]
serie = pd.Series([1, 2, 3, 4])serie
0 11 22 33 4dtype: int64
Se for passado um índice, ele deve ter o mesmo comprimento que os dados
serie = pd.Series([1, 2, 3, 4], index=["a", "b", "c", "d"])serie
a 1b 2c 3d 4dtype: int64
3.1.3. Séries de um ndarray
Se os dados vierem de um ndarray e nenhum índice for passado, será criado um com valores [0, ..., len(data)-1]
serie = pd.Series(np.random.randn(5))serie
0 1.2678651 -0.8778572 -0.1385563 -0.1329874 -0.827295dtype: float64
Se for passado um índice, ele deve ter o mesmo comprimento que os dados.
serie = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])serie
a -1.091828b -0.584243c 0.220398d 1.248923e 1.652351dtype: float64
3.1.4. Séries a partir de um escalar
Se a série for criada a partir de um escalar, ela será criada com um único item
serie = pd.Series(5.0)serie
0 5.0dtype: float64
Se se quiser criar mais itens na série, é necessário passar o índice com o número de itens desejado, dessa forma todos os itens terão o valor do escalar
serie = pd.Series(5.0, index=["a", "b", "c", "d", "e"])serie
a 5.0b 5.0c 5.0d 5.0e 5.0dtype: float64
3.1.5. Operações com Séries
Assim como com Numpy, podemos realizar operações com todos os elementos de uma série, sem precisar fazer uma iteração por cada um deles.
serie = pd.Series(5.0, index=["a", "b", "c", "d", "e"])print(f"serie:\n{serie}")print(f"\nserie + serie =\n{serie + serie}")
serie:a 5.0b 5.0c 5.0d 5.0e 5.0dtype: float64serie + serie =a 10.0b 10.0c 10.0d 10.0e 10.0dtype: float64
serie = pd.Series(5.0, index=["a", "b", "c", "d", "e"])print(f"serie:\n{serie}")print(f"\nexp(serie) =\n{np.exp(serie)}")
serie:a 5.0b 5.0c 5.0d 5.0e 5.0dtype: float64exp(serie) =a 148.413159b 148.413159c 148.413159d 148.413159e 148.413159dtype: float64
Uma diferença entre Series
e ndarrays é que as operações entre Series
alinham automaticamente os dados de acordo com suas etiquetas. Portanto, é possível escrever cálculos sem se preocupar se as Series
envolvidas têm as mesmas etiquetas. Se uma etiqueta não for encontrada em uma Series
ou outra, o resultado será marcado como ausente (NaN).
serie = pd.Series(5.0, index=["a", "b", "c", "d", "e"])print(f"serie:\n{serie}")print(f"\nserie[1:] + serie[:-1] =\n{serie[1:] + serie[:-1]}")
serie:a 5.0b 5.0c 5.0d 5.0e 5.0dtype: float64serie[1:] + serie[:-1] =a NaNb 10.0c 10.0d 10.0e NaNdtype: float64
3.1.6. Atributo nome das Series
Um dos atributos das Serie
s é name
, o qual corresponde ao nome que terão quando forem adicionadas a um DataFrame. Pelo caminho contrário, quando se obtém uma série de um DataFrame, essa série terá como nome aquele que tinha no DataFrame.
serie = pd.Series(np.random.randn(5), name="aleatorio")serie
0 -0.1910091 -0.7931512 -0.9077473 -1.4405084 -0.676419Name: aleatorio, dtype: float64
Pode-se mudar o nome de uma série utilizando o método rename()
serie = serie.rename("random")serie
0 -0.1910091 -0.7931512 -0.9077473 -1.4405084 -0.676419Name: random, dtype: float64
3.2. DataFrames
Um DataFrame
é uma estrutura de dados etiquetada e bidimensional, com colunas de tipos potencialmente diferentes, ou seja, em uma coluna pode haver dados de tipo inteiro, em outra coluna dados de tipo string, etc. Pode pensar nisso como uma planilha ou uma tabela SQL, ou um dicionário de objetos Series
.
É o objeto pandas mais utilizado. Assim como as Series
, os DataFrame
s aceitam muitos tipos diferentes de entrada:
Junto com os dados, opcionalmente você pode passar argumentos de índice (rótulos de linha) e colunas (rótulos de coluna). Se passar um índice e/ou colunas, estará garantindo o índice e/ou colunas do DataFrame
resultante. Portanto, um dicionário de Series
mais um índice específico descartará todos os dados que não coincidam com o índice passado.
Se as etiquetas dos eixos não forem passadas, elas serão construídas a partir dos dados de entrada com base em regras de bom senso.
3.2.1. DataFrames a partir de um dicionário de Séries
Se um dicionário contendo Series
for passado, o DataFrame
será criado com tantas colunas quantas Series
houver no dicionário.
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0]),
"dos": pd.Series([4.0, 5.0, 6.0, 7.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe
Se cada uma das Series
tiver índices definidos, o DataFrame
resultante será a união desses índices
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
"dos": pd.Series([4.0, 5.0, 6.0, 7.0], index=["a", "b", "c", "d"])
}
dataframe = pd.DataFrame(diccionario)
dataframe
dataframe = pd.DataFrame(diccionario, index=["d", "b", "a"])
dataframe
Se as colunas forem passadas, elas aparecerão na ordem passada
dataframe = pd.DataFrame(diccionario, columns=["dos", "tres"])
dataframe
3.2.2. DataFrames a partir de um dicionário de ndarrays ou listas
Todos os ndarrays ou listas devem ter o mesmo comprimento. Se for passado um índice, ele também deve ter o mesmo comprimento que os ndarrays ou listas.
diccionario = {
"uno": [1.0, 2.0, 3.0, 4.0],
"dos": [4.0, 3.0, 2.0, 1.0]
}
dataframe = pd.DataFrame(diccionario)
dataframe
3.2.3. DataFrames a partir de uma matriz
Se for passado um índice, ele deve ter o mesmo comprimento que o número de linhas da matriz e, se forem passadas as colunas, elas devem ter o mesmo comprimento que as colunas da matriz
matriz = np.array([[1, 3], [2, 2], [3, 1]])
dataframe = pd.DataFrame(matriz, index=["a", "b", "c"], columns=["columna1", "columna2"])
dataframe
3.2.4. DataFrames a partir de uma lista de dicionários
lista = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
dataframe = pd.DataFrame(lista)
dataframe
3.2.5. DataFrames de um dicionário de tuplas
diccionario = {
("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
}
dataframe = pd.DataFrame(diccionario)
dataframe
3.2.6. DataFrames a partir de uma Série
O resultado será um DataFrame
com o mesmo índice que a Série de entrada, e com uma coluna cujo nome é o nome original da Série (apenas se outro nome de coluna não for fornecido).
diccionario = {"b": 1, "a": 0, "c": 2}
serie = pd.Series(diccionario)
dataframe = pd.DataFrame(serie)
dataframe
4. Exploração de um DataFrame
Quando um DataFrame
é muito grande não pode ser representado inteiro
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")
california_housing_train
Portanto, é muito útil ter métodos para explorá-lo e obter informações de maneira rápida.
4.1. Cabeçalho do DataFrame
Para ver as primeiras linhas e ter uma ideia de como é o DataFrame
existe o método head()
, que por padrão mostra as primeiras 5 linhas do DataFrame
. Se você quiser ver um número diferente de linhas, insira-o através do atributo n
california_housing_train.head(n=10)
4.2. Cauda do DataFrame
Se o que você quer é ver as últimas linhas, pode usar o método tail()
, através do atributo n
você escolhe quantas linhas mostrar.
california_housing_train.tail()
4.3. Informações do DataFrame
Outro método muito útil é info()
que nos dá informações sobre o DataFrame
diccionario = {"uno": pd.Series([1.0, 2.0, 3.0]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0])}dataframe = pd.DataFrame(diccionario)dataframediccionario = {"uno": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0], index=["a", "b", "c", "d"])}dataframe = pd.DataFrame(diccionario)dataframedataframe = pd.DataFrame(diccionario, index=["d", "b", "a"])dataframedataframe = pd.DataFrame(diccionario, columns=["dos", "tres"])dataframediccionario = {"uno": [1.0, 2.0, 3.0, 4.0],"dos": [4.0, 3.0, 2.0, 1.0]}dataframe = pd.DataFrame(diccionario)dataframematriz = np.array([[1, 3], [2, 2], [3, 1]])dataframe = pd.DataFrame(matriz, index=["a", "b", "c"], columns=["columna1", "columna2"])dataframelista = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]dataframe = pd.DataFrame(lista)dataframediccionario = {("a", "b"): {("A", "B"): 1, ("A", "C"): 2},("a", "a"): {("A", "C"): 3, ("A", "B"): 4},("a", "c"): {("A", "B"): 5, ("A", "C"): 6},("b", "a"): {("A", "C"): 7, ("A", "B"): 8},("b", "b"): {("A", "D"): 9, ("A", "B"): 10},}dataframe = pd.DataFrame(diccionario)dataframediccionario = {"b": 1, "a": 0, "c": 2}serie = pd.Series(diccionario)dataframe = pd.DataFrame(serie)dataframecalifornia_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")california_housing_traincalifornia_housing_train.head(n=10)california_housing_train.tail()california_housing_train.info()
<class 'pandas.core.frame.DataFrame'>RangeIndex: 17000 entries, 0 to 16999Data columns (total 9 columns):# Column Non-Null Count Dtype--- ------ -------------- -----0 longitude 17000 non-null float641 latitude 17000 non-null float642 housing_median_age 17000 non-null float643 total_rooms 17000 non-null float644 total_bedrooms 17000 non-null float645 population 17000 non-null float646 households 17000 non-null float647 median_income 17000 non-null float648 median_house_value 17000 non-null float64dtypes: float64(9)memory usage: 1.2 MB
4.4. Linhas e colunas DataFrame
Podem-se obter os índices e as colunas de um DataFrame
através dos métodos index
e columns
diccionario = {"uno": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0], index=["a", "b", "c", "d"])}dataframe = pd.DataFrame(diccionario)indices = dataframe.indexcolumnas = dataframe.columnsprint(f"El DataFrame tiene los índices {indices}\n")print(f"El DataFrame tiene las columnas {columnas}")
El DataFrame tiene los índicesIndex(['a', 'b', 'c', 'd'], dtype='object')El DataFrame tiene las columnasIndex(['uno', 'dos'], dtype='object')
4.5. Descrição do DataFrame
O método describe()
mostra um resumo estatístico rápido dos dados do DataFrame
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")
california_housing_train.describe()
4.6. Ordenação do DataFrame
Podem-se ordenar alfabeticamente as linhas de um DataFrame
através do método sort_index()
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")
california_housing_train.sort_index().head()
Como neste caso as linhas já estavam ordenadas, estabelecemos ascending=False
para que a ordem seja inversa
california_housing_train.sort_index(ascending=False).head()
Se o que se quer é ordenar as colunas é necessário introduzir axis=1
já que por padrão é 0
california_housing_train.sort_index(axis=1).head()
Se o que queremos é ordenar o DataFrame
através de uma coluna determinada, temos que usar o método sort_values()
e indicar a etiqueta da coluna sobre a qual queremos ordenar
california_housing_train.sort_values('median_house_value')
4.7. Estatísticas do DataFrame
Pode-se obter estatísticas do DataFrame
, como a média, a moda, o desvio padrão
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")california_housing_train.describe()california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")california_housing_train.sort_index().head()california_housing_train.sort_index(ascending=False).head()california_housing_train.sort_index(axis=1).head()california_housing_train.sort_values('median_house_value')california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")print(f"media:\n{california_housing_train.mean()}")print(f" desviación estandar:\n{california_housing_train.std()}")
media:longitude -119.562108latitude 35.625225housing_median_age 28.589353total_rooms 2643.664412total_bedrooms 539.410824population 1429.573941households 501.221941median_income 3.883578median_house_value 207300.912353dtype: float64desviación estandar:longitude 2.005166latitude 2.137340housing_median_age 12.586937total_rooms 2179.947071total_bedrooms 421.499452population 1147.852959households 384.520841median_income 1.908157median_house_value 115983.764387dtype: float64
Se se querem obter as estatísticas sobre as linhas e não sobre as colunas, é necessário indicá-lo através de axis=1
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")print(f"media:\n{california_housing_train.mean(axis=1)}")print(f" desviación estandar:\n{california_housing_train.std(axis=1)}")
media:0 8357.5970671 10131.5277782 9664.6423223 8435.0290784 7567.436111...16995 12806.40856716996 9276.77087816997 12049.50792216998 10082.05330016999 10863.022744Length: 17000, dtype: float64desviación estandar:0 22026.6124451 26352.9392722 28514.3165883 24366.7547474 21730.014569...16995 36979.67689916996 26158.00677116997 34342.87679216998 28408.15232916999 31407.119788Length: 17000, dtype: float64
Outra coisa útil que se pode obter dos DataFrame
s é por exemplo o número de vezes que se repete cada item de uma coluna
california_housing_train["total_rooms"].value_counts()
1582.0 161527.0 151717.0 141471.0 141703.0 14..157.0 12760.0 1458.0 110239.0 14068.0 1Name: total_rooms, Length: 5533, dtype: int64
Por exemplo, podemos ver que há um total de 16 casas com 1582 quartos.
4.8. Memória usada
Podemos ver a memória que usa o dataframe
california_housing_train.memory_usage(deep=True)
Index 128longitude 136000latitude 136000housing_median_age 136000total_rooms 136000total_bedrooms 136000population 136000households 136000median_income 136000median_house_value 136000dtype: int64
5. Adição de dados
5.1. Adição de colunas
É possível adicionar colunas facilmente como operações de outras colunas
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0]),
"dos": pd.Series([4.0, 5.0, 6.0, 7.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe["tres"] = dataframe["uno"] + dataframe["dos"]
dataframe["flag"] = dataframe["tres"] > 7.0
dataframe
Também podem ser adicionadas colunas indicando qual valor terão todos os seus itens.
dataframe["constante"] = 8.0
dataframe
Se uma Serie
que não tem o mesmo número de índices que o DataFrame
for adicionada, ela será ajustada ao número de índices do DataFrame
.
dataframe["Menos indices"] = dataframe["uno"][:2]
dataframe
Com os métodos anteriores a coluna era adicionada ao final, mas se você quiser adicionar a coluna em uma posição determinada, pode usar o método insert()
.
Por exemplo, se você quiser adicionar uma coluna na posição 3 (levando em conta que a contagem começa na posição 0), cujo nome da coluna seja coluna inserida e cujo valor seja o dobro do valor da coluna três, seria feito da seguinte maneira
dataframe.insert(loc=3, column="columna insertada", value=dataframe["tres"]*2)
dataframe
Se se quiser adicionar mais de uma coluna por comando, pode-se usar o método assign()
dataframe = dataframe.assign(
columna_asignada1 = dataframe["uno"] * dataframe["tres"],
columna_asignada2 = dataframe["dos"] * dataframe["tres"],
)
dataframe
5.2. Adição de linhas
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0]),
"dos": pd.Series([4.0, 5.0, 6.0, 7.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe.head()
Podemos adicionar uma linha ao final com o método concat
(que veremos mais em detalhe depois)
diccionario = {
"uno": [10.0],
"dos": [20.0]
}
dataframe = pd.concat([dataframe, pd.DataFrame(diccionario)])
dataframe
Vemos que a coluna foi adicionada ao final, mas que tem o índice zero, então reordenamos os índices através do método reset_index(drop=True)
dataframe = dataframe.reset_index(drop=True)
dataframe
6. Eliminação de dados
6.1. Remoção de colunas
Pode-se eliminar uma coluna determinada usando o método pop()
dataframe.pop("constante")
dataframe
O através de del
del dataframe["flag"]
dataframe
6.1. Eliminação de linhas
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
"dos": pd.Series([11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]),
"tres": pd.Series([21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe.head()
Se queremos eliminar uma linha, podemos usar o método drop
, especificando sua posição. Por exemplo, se queremos eliminar a linha da posição 1
dataframe = dataframe.drop(1)
dataframe
Se queremos eliminar a última linha
dataframe = dataframe.drop(len(dataframe)-1)
dataframe
Se o que queremos é eliminar um intervalo de linhas
dataframe = dataframe.drop(range(2, 5))
dataframe
Se o que queremos é eliminar um conjunto de linhas determinado
dataframe = dataframe.drop([5, 7, 9])
dataframe
Assim como quando adicionamos linhas, vemos que alguns índices foram removidos, então reordenamos os índices usando o método reset_index(drop=True)
dataframe = dataframe.reset_index(drop=True)
dataframe
7. Operações em DataFrames
Pode-se realizar operações sobre DataFrame
s assim como se podia fazer com Numpy
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
"dos": pd.Series([11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]),
"tres": pd.Series([21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe.head()
dataframe[ ["uno", "dos", "tres"] ] * 2
np.exp(dataframe[ ["uno", "dos", "tres"] ])
Se se quiser realizar operações mais complexas, pode-se utilizar o método apply()
diccionario = {"uno": pd.Series([1.0, 2.0, 3.0]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0])}dataframe = pd.DataFrame(diccionario)dataframe["tres"] = dataframe["uno"] + dataframe["dos"]dataframe["flag"] = dataframe["tres"] > 7.0dataframedataframe["constante"] = 8.0dataframedataframe["Menos indices"] = dataframe["uno"][:2]dataframedataframe.insert(loc=3, column="columna insertada", value=dataframe["tres"]*2)dataframedataframe = dataframe.assign(columna_asignada1 = dataframe["uno"] * dataframe["tres"],columna_asignada2 = dataframe["dos"] * dataframe["tres"],)dataframediccionario = {"uno": pd.Series([1.0, 2.0, 3.0]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0])}dataframe = pd.DataFrame(diccionario)dataframe.head()diccionario = {"uno": [10.0],"dos": [20.0]}dataframe = pd.concat([dataframe, pd.DataFrame(diccionario)])dataframedataframe = dataframe.reset_index(drop=True)dataframedataframe.pop("constante")dataframedel dataframe["flag"]dataframediccionario = {"uno": pd.Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),"dos": pd.Series([11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]),"tres": pd.Series([21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0])}dataframe = pd.DataFrame(diccionario)dataframe.head()dataframe = dataframe.drop(1)dataframedataframe = dataframe.drop(len(dataframe)-1)dataframedataframe = dataframe.drop(range(2, 5))dataframedataframe = dataframe.drop([5, 7, 9])dataframedataframe = dataframe.reset_index(drop=True)dataframediccionario = {"uno": pd.Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),"dos": pd.Series([11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]),"tres": pd.Series([21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0])}dataframe = pd.DataFrame(diccionario)dataframe.head()dataframe[ ["uno", "dos", "tres"] ] * 2np.exp(dataframe[ ["uno", "dos", "tres"] ])dataframe = dataframe.apply(lambda x: x.max() - x.min())dataframe
uno 9.0dos 9.0tres 9.0dtype: float64
Foi aplicada uma função lambda
porque é uma função simples, mas caso se queira aplicar funções mais complexas, podemos defini-las e aplicá-las
def funcion(x):if x < 10:return np.exp(x) - np.log(5*x) + np.sqrt(x)elif x < 20:return np.sin(x) + np.cos(x) + np.tan(x)else:return np.log(x) + np.log10(x) + np.log2(x)dataframe = dataframe.apply(funcion)dataframe
uno 8102.277265dos 8102.277265tres 8102.277265dtype: float64
Utilizar o método apply
em um dataframe é muito mais rápido do que fazer um for
por cada uma das linhas e realizar a operação
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")
california_housing_train.head()
Vamos calcular a porcentagem de dormitórios do total de quartos
california_housing_train["percent_bedrooms"] = None
%time california_housing_train["percent_bedrooms"] = california_housing_train.apply(lambda x: x["total_bedrooms"] / x["total_rooms"], axis=1)
california_housing_train.head()
california_housing_train["percent_bedrooms"] = None
%time for i in range(len(california_housing_train)): california_housing_train["percent_bedrooms"][i] = california_housing_train["total_bedrooms"][i] / california_housing_train["total_rooms"][i]
california_housing_train.head()
Com a função lambda
demorou cerca de 300 ms, enquanto que com o laço for
demorou mais de 1 segundo
8. Transposta
Pode-se fazer a transposta de um DataFrame
através do método T
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0], index=["fila a", "fila b", "fila c"]),
"dos": pd.Series([4.0, 5.0, 6.0], index=["fila a", "fila b", "fila c"])
}
dataframe = pd.DataFrame(diccionario)
dataframe["tres"] = dataframe["uno"] + dataframe["dos"]
dataframe["flag"] = dataframe["tres"] > 7.0
dataframe.T
9. Conversão para Numpy
Se se deseja converter uma Serie
ou DataFrame
para NumPy pode-se usar o método to_numpy()
ou usar a função np.asarray()
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0], index=["fila a", "fila b", "fila c"]),
"dos": pd.Series([4.0, 5.0, 6.0], index=["fila a", "fila b", "fila c"])
}
dataframe = pd.DataFrame(diccionario)
dataframe["tres"] = dataframe["uno"] + dataframe["dos"]
dataframe["flag"] = dataframe["tres"] > 7.0
dataframe
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")california_housing_train.head()california_housing_train["percent_bedrooms"] = None%time california_housing_train["percent_bedrooms"] = california_housing_train.apply(lambda x: x["total_bedrooms"] / x["total_rooms"], axis=1)california_housing_train.head()california_housing_train["percent_bedrooms"] = None%time for i in range(len(california_housing_train)): california_housing_train["percent_bedrooms"][i] = california_housing_train["total_bedrooms"][i] / california_housing_train["total_rooms"][i]california_housing_train.head()diccionario = {"uno": pd.Series([1.0, 2.0, 3.0], index=["fila a", "fila b", "fila c"]),"dos": pd.Series([4.0, 5.0, 6.0], index=["fila a", "fila b", "fila c"])}dataframe = pd.DataFrame(diccionario)dataframe["tres"] = dataframe["uno"] + dataframe["dos"]dataframe["flag"] = dataframe["tres"] > 7.0dataframe.Tdiccionario = {"uno": pd.Series([1.0, 2.0, 3.0], index=["fila a", "fila b", "fila c"]),"dos": pd.Series([4.0, 5.0, 6.0], index=["fila a", "fila b", "fila c"])}dataframe = pd.DataFrame(diccionario)dataframe["tres"] = dataframe["uno"] + dataframe["dos"]dataframe["flag"] = dataframe["tres"] > 7.0dataframematriz_np = dataframe.to_numpy()matriz_np
CPU times: user 309 ms, sys: 86 µs, total: 309 msWall time: 309 ms/home/wallabot/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a DataFrameSee the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy"""Entry point for launching an IPython kernel.array([[1.0, 4.0, 5.0, False],[2.0, 5.0, 7.0, False],[3.0, 6.0, 9.0, True]], dtype=object)
matriz_np = np.asarray(dataframe)matriz_np
array([[1.0, 4.0, 5.0, False],[2.0, 5.0, 7.0, False],[3.0, 6.0, 9.0, True]], dtype=object)
Este exemplo não é o mais indicado, pois mistura números com booleanos, e como já explicamos no post anterior Cálculo matricial com NumPy, todos os elementos de um ndarray
têm que ser do mesmo tipo.
Neste caso, estamos misturando números com booleanos, então para resolver isso, o NumPy os converte todos em objetos.
Para solucionar isso ficamos apenas com os números e os convertimos em um ndarray
matriz_np = dataframe[ ["uno", "dos", "tres"] ].to_numpy()matriz_np, matriz_np.dtype
(array([[1., 4., 5.],[2., 5., 7.],[3., 6., 9.]]), dtype('float64'))
Agora pode-se ver que foi criado um ndarray
onde todos os dados são do tipo float
10. Leitura de dados de fontes externas
Uma das maiores forças do Pandas é poder ler dados de arquivos, assim não é necessário criar um DataFrame
com os dados que se deseja processar, mas sim lê-los de um arquivo.
Da mesma forma que se pode criar DataFrame
s a partir de arquivos externos, também se pode salvar DataFrame
s em arquivos, para assim criar seu próprio conjunto de dados, configurá-lo da maneira que desejar e salvá-lo em um arquivo para poder usá-lo mais tarde.
Na tabela a seguir são mostradas as funções para ler e escrever arquivos de diferentes formatos:
Formato | Leitura | Escrita |
---|---|---|
CSV | pd.read_csv | df.to_csv |
Excel | pd.read_excel | df.to_excel |
JSON | pd.read_json | df.to_json |
Parquet | pd.read_parquet | df.to_parquet |
SQL | pd.read_sql | df.to_sql |
HDF5 | pd.read_hdf | df.to_hdf |
Feather | pd.read_feather | df.to_feather |
Stata | pd.read_stata | df.to_stata |
SAS | pd.read_sas | N/A |
SPSS | pd.read_spss | N/A |
|Formato|Tipo de arquivo|Função de leitura|Função de escrita||---|---|---|---||texto|CSV|read_csv|to_csv||texto|Arquivo de Texto de Largura Fixa|read_fwf|| |texto|JSON|read_json|to_json||texto|HTML|read_html|to_html||texto|Área de transferência local|read_clipboard|to_clipboard||binary|MS Excel|read_excel|to_excel||binário|OpenDocument|read_excel||binário|Formato HDF5|read_hdf|to_hdf||binary|Formato Feather|read_feather|to_feather||binário|Formato Parquet|read_parquet|to_parquet||binário|Formato ORC|read_orc||binary|Msgpack|read_msgpack|to_msgpack||binary|Stata|read_stata|to_stata||binário|SAS|read_sas||binary|SPSS|read_spss||binário|Formato de Pickle do Python|read_pickle|to_pickle||SQL|SQL|read_sql|to_sql||SQL|Google BigQuery|read_gbq|to_gbq|
11. Indexação em DataFrames
Há muitas maneiras de indexar nos DataFrame
s,
fechas = pd.date_range('1/1/2000', periods=8)
dataframe = pd.DataFrame(np.random.randn(8, 4), index=fechas, columns=['A', 'B', 'C', 'D'])
dataframe
11.1. Indexação de colunas
Para selecionar colunas dentro de um DataFrame
podemos fazê-lo selecionando a coluna entre colchetes []
, ou indicando a coluna como se fosse um método do DataFrame
fechas = pd.date_range('1/1/2000', periods=8)dataframe = pd.DataFrame(np.random.randn(8, 4), index=fechas, columns=['A', 'B', 'C', 'D'])dataframedataframe['A']
2000-01-01 0.8131532000-01-02 -0.2445842000-01-03 0.1257292000-01-04 0.3522752000-01-05 -2.0509762000-01-06 -0.3122962000-01-07 0.8978372000-01-08 0.271403Freq: D, Name: A, dtype: float64
dataframe.A
2000-01-01 0.8131532000-01-02 -0.2445842000-01-03 0.1257292000-01-04 0.3522752000-01-05 -2.0509762000-01-06 -0.3122962000-01-07 0.8978372000-01-08 0.271403Freq: D, Name: A, dtype: float64
Se se quiser determinadas linhas, elas podem ser passadas através de uma lista
dataframe[ ['A', 'B'] ]
11.2. Indexação de linhas por posições
Pode-se selecionar um intervalo de linhas de um DataFrame
da seguinte maneira
dataframe[0:3]
Se você quiser selecionar apenas uma única linha, deve-se indicar um intervalo de linhas que inclua apenas essa, por exemplo, se você quiser selecionar a linha número 1
dataframe[1:2]
Outro método para selecionar uma linha por sua posição é o método iloc[]
dataframe.iloc[0:3]
Se se quiserem algumas linhas específicas, passa-se uma lista com suas posições
dataframe.iloc[ [0, 2, 4] ]
11.3. Indexação de linhas por etiquetas
Para selecionar uma linha por suas etiquetas podemos usar o método loc[]
dataframe[ ['A', 'B'] ]dataframe[0:3]dataframe[1:2]dataframe.iloc[0:3]dataframe.iloc[ [0, 2, 4] ]dataframe.loc['2000-01-01']
A 0.813153B -0.869356C 0.934293D 0.338644Name: 2000-01-01 00:00:00, dtype: float64
Se quiser selecionar um intervalo de linhas, podemos indexá-las usando dois pontos :
dataframe.loc['2000-01-01':'2000-01-03']
Se se quiserem determinadas linhas, elas são passadas através de uma lista
dataframe.loc[ ['2000-01-01', '2000-01-03', '2000-01-05'] ]
11.4. Seleção de uma porção do DataFrame por meio de posições
dataframe.iloc[0:3, 0:2]
Se você quiser determinadas linhas e colunas, passe listas com as posições desejadas
dataframe.iloc[ [0, 2, 4], [0, 2] ]
11.5. Seleção de uma porção do DataFrame através de etiquetas
dataframe.loc['2000-01-01':'2000-01-03', 'A':'B']
Se quiser linhas e colunas específicas, passe listas com as etiquetas desejadas
dataframe.loc[ ['2000-01-01', '2000-01-03', '2000-01-05'], ['A', 'C'] ]
11.6. Indexação por função lambda
Se podem selecionar dados de um DataFrame
que cumpram uma condição dada por uma função lambda
dataframe.loc[lambda dataframe:2*dataframe['A']+5*np.exp(dataframe['B'])>0.2]
Como pode ser visto, esta forma de indexação é muito poderosa
11.7. Indexação condicional
Se não precisarmos de funções complexas para indexar, mas apenas condicionais, podemos fazer
dataframe[dataframe['A']>0.2]
Podemos fazer múltiplas condições
dataframe[(dataframe['A']>0.2) & (dataframe['B']>0.2)]
11.8. Indexação Aleatória
Mediante o método sample()
obteremos uma linha aleatória do DataFrame
dataframe.sample()
Se queremos mais de uma amostra, indicamos isso com o atributo n
dataframe.sample(n=3)
Se o que se quer são colunas aleatórias, é preciso indicá-lo através de axis=1
dataframe.sample(axis=1)
Se se quer um único item do DataFrame
é necessário chamar duas vezes o método sample()
dataframe.sample(axis=1).sample()
12. União de DataFrames
12.1. Concatenação de DataFrames
Para concatenar vários DataFrame
s usamos o método concat()
, onde será passada uma lista com os DataFrame
s que se quer unir
dataframe.loc['2000-01-01':'2000-01-03']dataframe.loc[ ['2000-01-01', '2000-01-03', '2000-01-05'] ]dataframe.iloc[0:3, 0:2]dataframe.iloc[ [0, 2, 4], [0, 2] ]dataframe.loc['2000-01-01':'2000-01-03', 'A':'B']dataframe.loc[ ['2000-01-01', '2000-01-03', '2000-01-05'], ['A', 'C'] ]dataframe.loc[lambda dataframe:2*dataframe['A']+5*np.exp(dataframe['B'])>0.2]dataframe[dataframe['A']>0.2]dataframe[(dataframe['A']>0.2) & (dataframe['B']>0.2)]dataframe.sample()dataframe.sample(n=3)dataframe.sample(axis=1)dataframe.sample(axis=1).sample()dataframe1 = pd.DataFrame({"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],})dataframe2 = pd.DataFrame({"A": ["A4", "A5", "A6", "A7"],"B": ["B4", "B5", "B6", "B7"],"C": ["C4", "C5", "C6", "C7"],"D": ["D4", "D5", "D6", "D7"],})dataframe3 = pd.DataFrame({"A": ["A8", "A9", "A10", "A11"],"B": ["B8", "B9", "B10", "B11"],"C": ["C8", "C9", "C10", "C11"],"D": ["D8", "D9", "D10", "D11"],})dataframe = pd.concat([dataframe1, dataframe2, dataframe3])print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"dataframe3:\n{dataframe3}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D3dataframe2:A B C D0 A4 B4 C4 D41 A5 B5 C5 D52 A6 B6 C6 D63 A7 B7 C7 D7dataframe3:A B C D0 A8 B8 C8 D81 A9 B9 C9 D92 A10 B10 C10 D103 A11 B11 C11 D11dataframe:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D30 A4 B4 C4 D41 A5 B5 C5 D52 A6 B6 C6 D63 A7 B7 C7 D70 A8 B8 C8 D81 A9 B9 C9 D92 A10 B10 C10 D103 A11 B11 C11 D11
Como pode ser visto, os índices 0
, 1
, 2
e 3
se repetem, porque cada dataframe tem esses índices. Para que isso não aconteça, é necessário usar o parâmetro ignore_index=True
dataframe = pd.concat([dataframe1, dataframe2, dataframe3], ignore_index=True)print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"dataframe3:\n{dataframe3}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D3dataframe2:A B C D0 A4 B4 C4 D41 A5 B5 C5 D52 A6 B6 C6 D63 A7 B7 C7 D7dataframe3:A B C D0 A8 B8 C8 D81 A9 B9 C9 D92 A10 B10 C10 D103 A11 B11 C11 D11dataframe:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D34 A4 B4 C4 D45 A5 B5 C5 D56 A6 B6 C6 D67 A7 B7 C7 D78 A8 B8 C8 D89 A9 B9 C9 D910 A10 B10 C10 D1011 A11 B11 C11 D11
Se se quisesse fazer a concatenação ao longo das colunas, seria necessário ter introduzido a variável axis=1
dataframe = pd.concat([dataframe1, dataframe2, dataframe3], axis=1)print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"dataframe3:\n{dataframe3}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D3dataframe2:A B C D0 A4 B4 C4 D41 A5 B5 C5 D52 A6 B6 C6 D63 A7 B7 C7 D7dataframe3:A B C D0 A8 B8 C8 D81 A9 B9 C9 D92 A10 B10 C10 D103 A11 B11 C11 D11dataframe:A B C D A B C D A B C D0 A0 B0 C0 D0 A4 B4 C4 D4 A8 B8 C8 D81 A1 B1 C1 D1 A5 B5 C5 D5 A9 B9 C9 D92 A2 B2 C2 D2 A6 B6 C6 D6 A10 B10 C10 D103 A3 B3 C3 D3 A7 B7 C7 D7 A11 B11 C11 D11
12.1.1. Interseção de concatenação
Existem duas maneiras de fazer a concatenação, pegando todos os índices dos DataFrame
s ou pegando apenas os que coincidem, isso é determinado pela variável join
, que aceita os valores 'outer'
(por padrão) (pega todos os índices) ou 'inner'
(apenas os que coincidem)
Vamos ver um exemplo de 'outer'
dataframe1 = pd.DataFrame({"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],},index=[0, 1, 2, 3])dataframe4 = pd.DataFrame({"B": ["B2", "B3", "B6", "B7"],"D": ["D2", "D3", "D6", "D7"],"F": ["F2", "F3", "F6", "F7"],},index=[2, 3, 6, 7])dataframe = pd.concat([dataframe1, dataframe4], axis=1)print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe4}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D3dataframe2:B D F2 B2 D2 F23 B3 D3 F36 B6 D6 F67 B7 D7 F7dataframe:A B C D B D F0 A0 B0 C0 D0 NaN NaN NaN1 A1 B1 C1 D1 NaN NaN NaN2 A2 B2 C2 D2 B2 D2 F23 A3 B3 C3 D3 B3 D3 F36 NaN NaN NaN NaN B6 D6 F67 NaN NaN NaN NaN B7 D7 F7
Vejamos um exemplo de 'inner'
dataframe = pd.concat([dataframe1, dataframe4], axis=1, join="inner")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe4}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A B C D0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D3dataframe2:B D F2 B2 D2 F23 B3 D3 F36 B6 D6 F67 B7 D7 F7dataframe:A B C D B D F2 A2 B2 C2 D2 B2 D2 F23 A3 B3 C3 D3 B3 D3 F3
12.2. Merge
de DataFrames
Antes criamos um novo dataframe com a união de vários dataframes, agora podemos completar um dataframe com outro, para isso usamos merge
, passando o parâmetro on
, sobre qual coluna queremos que seja feito o merge
.
dataframe1 = pd.DataFrame({"Key": ["K0", "K1", "K2", "K3"],"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],})dataframe2 = pd.DataFrame({"Key": ["K0", "K1", "K2", "K3"],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],})dataframe = dataframe1.merge(dataframe2, on="Key")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe:\n{dataframe}")
dataframe1:Key A B0 K0 A0 B01 K1 A1 B12 K2 A2 B23 K3 A3 B3dataframe2:Key C D0 K0 C0 D01 K1 C1 D12 K2 C2 D23 K3 C3 D3dataframe:Key A B C D0 K0 A0 B0 C0 D01 K1 A1 B1 C1 D12 K2 A2 B2 C2 D23 K3 A3 B3 C3 D3
Neste caso, os dois dataframes tinham uma chave que se chamava igual (Key
), mas no caso de ter dataframes, nos quais sua chave tenha outro nome, podemos usar os parâmetros left_on
e right_on
.
dataframe1 = pd.DataFrame({"Key1": ["K0", "K1", "K2", "K3"],"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],})dataframe2 = pd.DataFrame({"Key2": ["K0", "K1", "K2", "K3"],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],})dataframe = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe:\n{dataframe}")
dataframe1:Key1 A B0 K0 A0 B01 K1 A1 B12 K2 A2 B23 K3 A3 B3dataframe2:Key2 C D0 K0 C0 D01 K1 C1 D12 K2 C2 D23 K3 C3 D3dataframe:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D23 K3 A3 B3 K3 C3 D3
No caso em que uma das chaves não coincida, não será feito o merge
sobre essa chave
dataframe1 = pd.DataFrame({"Key1": ["K0", "K1", "K2", "K3"],"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],})dataframe2 = pd.DataFrame({"Key2": ["K0", "K1", "K2", np.nan],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],})dataframe = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe:\n{dataframe}")
dataframe1:Key1 A B0 K0 A0 B01 K1 A1 B12 K2 A2 B23 K3 A3 B3dataframe2:Key2 C D0 K0 C0 D01 K1 C1 D12 K2 C2 D23 NaN C3 D3dataframe:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D2
Para mudar esse comportamento podemos usar o parâmetro how
, que por padrão tem o valor inner
, mas podemos passar o valor left
, right
e outer
.
dataframe1 = pd.DataFrame({"Key1": ["K0", "K1", "K2", "K3"],"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],})dataframe2 = pd.DataFrame({"Key2": ["K0", "K1", "K2", np.nan],"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],})dataframe_inner = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2", how="inner")dataframe_left = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2", how="left")dataframe_right = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2", how="right")dataframe_outer = dataframe1.merge(dataframe2, left_on="Key1", right_on="Key2", how="outer")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe inner:\n{dataframe_inner}")print(f"\ndataframe left:\n{dataframe_left}")print(f"\ndataframe right:\n{dataframe_right}")print(f"\ndataframe outer:\n{dataframe_outer}")
dataframe1:Key1 A B0 K0 A0 B01 K1 A1 B12 K2 A2 B23 K3 A3 B3dataframe2:Key2 C D0 K0 C0 D01 K1 C1 D12 K2 C2 D23 NaN C3 D3dataframe inner:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D2dataframe left:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D23 K3 A3 B3 NaN NaN NaNdataframe right:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D23 NaN NaN NaN NaN C3 D3dataframe outer:Key1 A B Key2 C D0 K0 A0 B0 K0 C0 D01 K1 A1 B1 K1 C1 D12 K2 A2 B2 K2 C2 D23 K3 A3 B3 NaN NaN NaN4 NaN NaN NaN NaN C3 D3
Como se pode ver, quando se escolhe left
apenas são adicionados os valores do dataframe da esquerda e quando se escolhe right
, os valores do dataframe da direita
12.3. Join
de dataframes
A última ferramenta de união de dataframes é join
. É similar a merge
, só que em vez de procurar semelhanças com base em colunas especificadas, procura com base nos índices.
dataframe1 = pd.DataFrame({"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],},index=["K0", "K1", "K2", "K3"])dataframe2 = pd.DataFrame({"C": ["C0", "C1", "C2", "C3"],"D": ["D0", "D1", "D2", "D3"],},index=["K0", "K1", "K2", "K3"])dataframe = dataframe1.join(dataframe2)print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe:\n{dataframe}")
dataframe1:A BK0 A0 B0K1 A1 B1K2 A2 B2K3 A3 B3dataframe2:C DK0 C0 D0K1 C1 D1K2 C2 D2K3 C3 D3dataframe:A B C DK0 A0 B0 C0 D0K1 A1 B1 C1 D1K2 A2 B2 C2 D2K3 A3 B3 C3 D3
Neste caso, os índices são iguais, mas quando são diferentes podemos especificar a maneira de unir os dataframes através do parâmetro how
, que por padrão tem o valor inner
, mas pode ter o valor left
, right
e outer
dataframe1 = pd.DataFrame({"A": ["A0", "A1", "A2", "A3"],"B": ["B0", "B1", "B2", "B3"],},index=["K0", "K1", "K2", "K3"])dataframe2 = pd.DataFrame({"C": ["C0", "C2", "C3", "C4"],"D": ["D0", "D2", "D3", "D4"],},index=["K0", "K2", "K3", "K4"])dataframe_inner = dataframe1.join(dataframe2, how="inner")dataframe_left = dataframe1.join(dataframe2, how="left")dataframe_right = dataframe1.join(dataframe2, how="right")dataframe_outer = dataframe1.join(dataframe2, how="outer")print(f"dataframe1:\n{dataframe1}")print(f"dataframe2:\n{dataframe2}")print(f"\ndataframe inner:\n{dataframe_inner}")print(f"\ndataframe left:\n{dataframe_left}")print(f"\ndataframe rigth:\n{dataframe_right}")print(f"\ndataframe outer:\n{dataframe_outer}")
dataframe1:A BK0 A0 B0K1 A1 B1K2 A2 B2K3 A3 B3dataframe2:C DK0 C0 D0K2 C2 D2K3 C3 D3K4 C4 D4dataframe:A B C DK0 A0 B0 C0 D0K2 A2 B2 C2 D2K3 A3 B3 C3 D3dataframe:A B C DK0 A0 B0 C0 D0K1 A1 B1 NaN NaNK2 A2 B2 C2 D2K3 A3 B3 C3 D3dataframe:A B C DK0 A0 B0 C0 D0K2 A2 B2 C2 D2K3 A3 B3 C3 D3K4 NaN NaN C4 D4dataframe:A B C DK0 A0 B0 C0 D0K1 A1 B1 NaN NaNK2 A2 B2 C2 D2K3 A3 B3 C3 D3K4 NaN NaN C4 D4
13. Dados ausentes (NaN
)
Em um DataFrame
pode haver alguns dados faltantes, Pandas os representa como np.nan
diccionario = {
"uno": pd.Series([1.0, 2.0, 3.0]),
"dos": pd.Series([4.0, 5.0, 6.0, 7.0])
}
dataframe = pd.DataFrame(diccionario)
dataframe
13.1. Eliminação das linhas com dados faltantes
Para não ter linhas com dados faltantes, elas podem ser eliminadas
dataframe.dropna(how="any")
13.2. Eliminação das colunas com dados faltantes
dataframe.dropna(axis=1, how='any')
13.3. Máscara booleana com as posições faltantes
pd.isna(dataframe)
13.4. Preenchimento de dados ausentes
dataframe.fillna(value=5.5, inplace=True)
dataframe
Dica: Colocando a variável
inplace=True
, oDataFrame
que está sendo operado é modificado, assim não é necessário escreverdataframe = dataframe.fillna(value=5.5)
14. Séries temporais
Pandas oferece a possibilidade de trabalhar com séries temporais. Por exemplo, criamos uma Série
de 100 dados aleatórios a cada segundo desde 01/01/2021
diccionario = {"uno": pd.Series([1.0, 2.0, 3.0]),"dos": pd.Series([4.0, 5.0, 6.0, 7.0])}dataframe = pd.DataFrame(diccionario)dataframedataframe.dropna(how="any")dataframe.dropna(axis=1, how='any')pd.isna(dataframe)dataframe.fillna(value=5.5, inplace=True)dataframeindices = pd.date_range("1/1/2021", periods=100, freq="S")datos = np.random.randint(0, 500, len(indices))serie_temporal = pd.Series(datos, index=indices)serie_temporal
2021-01-01 00:00:00 2412021-01-01 00:00:01 142021-01-01 00:00:02 1902021-01-01 00:00:03 4072021-01-01 00:00:04 94...2021-01-01 00:01:35 2752021-01-01 00:01:36 562021-01-01 00:01:37 4482021-01-01 00:01:38 1512021-01-01 00:01:39 316Freq: S, Length: 100, dtype: int64
Esta funcionalidade do Pandas é muito potente, por exemplo, podemos ter um conjunto de dados em algumas horas determinadas de um fuso horário e mudá-las para outro fuso
horas = pd.date_range("3/6/2021 00:00", periods=10, freq="H")datos = np.random.randn(len(horas))serie_horaria = pd.Series(datos, horas)serie_horaria
2021-03-06 00:00:00 -0.8535242021-03-06 01:00:00 -1.3553722021-03-06 02:00:00 -1.2675032021-03-06 03:00:00 -1.1557872021-03-06 04:00:00 0.7309352021-03-06 05:00:00 1.4359572021-03-06 06:00:00 0.4609122021-03-06 07:00:00 0.7234512021-03-06 08:00:00 -0.8533372021-03-06 09:00:00 0.456359Freq: H, dtype: float64
Localizamos os dados em um fuso horário
serie_horaria_utc = serie_horaria.tz_localize("UTC")serie_horaria_utc
2021-03-06 00:00:00+00:00 -0.8535242021-03-06 01:00:00+00:00 -1.3553722021-03-06 02:00:00+00:00 -1.2675032021-03-06 03:00:00+00:00 -1.1557872021-03-06 04:00:00+00:00 0.7309352021-03-06 05:00:00+00:00 1.4359572021-03-06 06:00:00+00:00 0.4609122021-03-06 07:00:00+00:00 0.7234512021-03-06 08:00:00+00:00 -0.8533372021-03-06 09:00:00+00:00 0.456359Freq: H, dtype: float64
E agora podemos mudá-las para outro uso
serie_horaria_US = serie_horaria_utc.tz_convert("US/Eastern")serie_horaria_US
2021-03-05 19:00:00-05:00 -0.8535242021-03-05 20:00:00-05:00 -1.3553722021-03-05 21:00:00-05:00 -1.2675032021-03-05 22:00:00-05:00 -1.1557872021-03-05 23:00:00-05:00 0.7309352021-03-06 00:00:00-05:00 1.4359572021-03-06 01:00:00-05:00 0.4609122021-03-06 02:00:00-05:00 0.7234512021-03-06 03:00:00-05:00 -0.8533372021-03-06 04:00:00-05:00 0.456359Freq: H, dtype: float64
15. Dados categóricos
Pandas oferece a possibilidade de adicionar dados categóricos em um DataFrame
. Suponha o seguinte DataFrame
dataframe = pd.DataFrame(
{"id": [1, 2, 3, 4, 5, 6], "raw_grade": ["a", "b", "b", "a", "a", "e"]}
)
dataframe
Podemos converter os dados da coluna raw_grade
para dados categóricos através do método astype()
dataframe['grade'] = dataframe["raw_grade"].astype("category")
dataframe
As colunas raw_grade
e grade
parecem iguais, mas se olharmos as informações do DataFrame
podemos ver que não é assim
dataframe = pd.DataFrame({"id": [1, 2, 3, 4, 5, 6], "raw_grade": ["a", "b", "b", "a", "a", "e"]})dataframedataframe['grade'] = dataframe["raw_grade"].astype("category")dataframedataframe.info()
<class 'pandas.core.frame.DataFrame'>RangeIndex: 6 entries, 0 to 5Data columns (total 3 columns):# Column Non-Null Count Dtype--- ------ -------------- -----0 id 6 non-null int641 raw_grade 6 non-null object2 grade 6 non-null categorydtypes: category(1), int64(1), object(1)memory usage: 334.0+ bytes
Pode-se ver que a coluna grade
é do tipo categórico
Podemos ver as categorias dos tipos de dados categóricos através do método cat.categories()
dataframe["grade"].cat.categories
Index(['a', 'b', 'e'], dtype='object')
Podemos também renomear as categorias com o mesmo método, mas introduzindo uma lista com as novas categorias.
dataframe["grade"].cat.categories = ["very good", "good", "very bad"]
dataframe
Pandas nos dá a possibilidade de codificar numericamente os dados categóricos através do método get_dummies
pd.get_dummies(dataframe["grade"])
16. Groupby
Podemos agrupar os dataframes por valores de alguma das colunas. Vamos recarregar o dataframe com o valor das casas da Califórnia.
california_housing_train = pd.read_csv("https://raw.githubusercontent.com/maximofn/portafolio/main/posts/california_housing_train.csv")
california_housing_train.head()
Agora podemos agrupar os dados por algumas das colunas, por exemplo, agrupemos as casas em função do número de anos e vejamos quantas casas existem de cada idade com count
.
california_housing_train.groupby("housing_median_age").count().head()
Como vemos em todas as colunas, obtemos o mesmo valor, que é o número de casas que há com uma determinada idade, mas podemos saber a média do valor de cada coluna com mean
california_housing_train.groupby("housing_median_age").mean().head()
Podemos obter várias medidas de cada idade mediante o comando agg
(aggregation), passando as medidas que queremos mediante uma lista, por exemplo vejamos o mínimo, o máximo e a média de cada coluna para cada idade de casa
california_housing_train.groupby("housing_median_age").agg(['min', 'max', 'mean']).head()
Podemos especificar sobre quais colunas queremos realizar certos cálculos através da passagem de um dicionário, onde as chaves serão as colunas sobre as quais queremos realizar cálculos e os valores serão listas com os cálculos
california_housing_train.groupby("housing_median_age").agg({'total_rooms': ['min', 'max', 'mean'], 'total_bedrooms': ['min', 'max', 'mean', 'median']}).head()
Podemos agrupar por mais de uma coluna, para isso, devemos passar as colunas em uma lista
california_housing_train.groupby(["housing_median_age", "total_bedrooms"]).mean()
17. Gráficos
Pandas oferece a possibilidade de representar os dados dos nossos DataFrame
s em gráficos para poder obter uma melhor representação dos mesmos. Para isso, utiliza a biblioteca matplotlib
que veremos no próximo post.
17.1. Gráfico básico
Para representar os dados em um gráfico, a maneira mais fácil é usar o método plot()
serie = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
serie = serie.cumsum()
serie.plot()
No caso de ter um DataFrame
, o método plot()
representará cada uma das colunas do DataFrame
dataframe = pd.DataFrame(
np.random.randn(1000, 4), index=ts.index, columns=["A", "B", "C", "D"]
)
dataframe = dataframe.cumsum()
dataframe.plot()
17.2. Diagrama de barras verticais
Há mais métodos para criar gráficos, como o diagrama de barras verticais através de plot.bar()
dataframe = pd.DataFrame(np.random.rand(10, 4), columns=["a", "b", "c", "d"])
dataframe.plot.bar()
Se queremos empilhar as barras, indicamos isso através da variável stacked=True
dataframe.plot.bar(stacked=True)
17.3. Diagrama de barras horizontal
Para criar um diagrama de barras horizontal usamos plot.barh()
dataframe.plot.barh()
Se queremos empilhar as barras, indicamos isso através da variável stacked=True
dataframe.plot.barh(stacked=True)
17.4. Histograma
Para criar um histograma usamos plot.hist()
dataframe = pd.DataFrame(
{
"a": np.random.randn(1000) + 1,
"b": np.random.randn(1000),
"c": np.random.randn(1000) - 1,
}
)
dataframe.plot.hist(alpha=0.5)
Se queremos empilhar as barras, indicamos através da variável stacked=True
dataframe.plot.hist(alpha=0.5, stacked=True)
Se quisermos adicionar mais colunas, ou seja, se quisermos que o histograma seja mais informativo ou preciso, indicamos isso através da variável bins
.
dataframe.plot.hist(alpha=0.5, stacked=True, bins=20)
17.5. Diagramas de velas
Para criar um gráfico de velas usamos plot.box()
dataframe = pd.DataFrame(np.random.rand(10, 5), columns=["A", "B", "C", "D", "E"])
dataframe.plot.box()
17.6. Gráficos de áreas
Para criar um gráfico de áreas usamos plot.area()
dataframe.plot.area()
17.7. Diagrama de dispersão
Para criar um diagrama de dispersão usamos plot.scatter()
, onde é necessário indicar as variáveis x
e y
do diagrama
dataframe.plot.scatter(x='A', y='B')
17.8. Gráfico de contêiner hexagonal
Para criar um gráfico de contêiner hexagonal usamos plot.hexbin()
, onde é necessário indicar as variáveis x
e y
do diagrama e o tamanho da malha mediante gridsize
dataframe = pd.DataFrame(np.random.randn(1000, 2), columns=["a", "b"])
dataframe["b"] = dataframe["b"] + np.arange(1000)
dataframe.plot.hexbin(x="a", y="b", gridsize=25)