Pular para o conteúdo principal

Groupby, split-apply-combine e pandas

Neste tutorial, você vai aprender como usar a operação groupby do pandas, que se baseia na conhecida estratégia split-apply-combine, em dados de filmes da Netflix.
· 8 min leer

Explore com IA

A Netflix divulgou recentemente alguns dados de avaliações de usuários. Eu quis responder a uma pergunta direta: os assinantes da Netflix preferem filmes mais antigos ou mais novos?

De forma intuitiva, você quer dividir o conjunto de dados em grupos, um para cada ano, calcular uma estatística de resumo, como média ou mediana, e então ver se essa estatística aumenta ao longo dos anos (depois disso, talvez você queira fazer um teste estatístico).

O melhor é que existe um arcabouço conceitual para fazer e pensar sobre isso, com implementações tanto em Python quanto em R. Esse arcabouço é conhecido como "split-apply-combine" porque nós...

  • Etapa 1: dividimos os dados em grupos criando um objeto groupby a partir do DataFrame original;
  • Etapa 2: aplicamos uma função, neste caso, uma função de agregação que calcula uma estatística de resumo (nesta etapa você também pode transformar ou filtrar seus dados);
  • Etapa 3: combinamos os resultados em um novo DataFrame.

Esse é o arcabouço conceitual para a análise que temos em mãos. Neste post, você vai aprender como fazer isso para responder à pergunta sobre as avaliações da Netflix usando o pacote pandas em Python. Você poderia fazer o mesmo em R usando, por exemplo, o pacote dplyr. Também vou entrar necessariamente nos objetos groupby, que não são os mais intuitivos. O processo de split-apply-combine com objetos groupby é um padrão que todos nós executamos de forma intuitiva, como veremos, mas foi Hadley Wickham quem formalizou o procedimento em 2011 com seu artigo The Split-Apply-Combine Strategy for Data Analysis.

Se você achar essa técnica útil, pode aprender mais sobre ela (entre muitas outras coisas) e praticá-la no nosso curso Manipulating DataFrames with pandas.

Exploração de dados com pandas

Importe seus dados

Aqui você vai usar pandas, objetos groupby e os princípios de split-apply-combine para investigar como as avaliações de filmes da Netflix variam em função do ano de lançamento. Eu encontrei os dados originalmente no data.world neste link. Você encontra todo o código deste post aqui caso queira reproduzir.

Primeiro, importe os pacotes necessários e os dados e visualize as cinco primeiras linhas:

# Import packages and set visualization style
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
%matplotlib inline
# Import data and check out head of DataFrame
df = pd.read_csv('data/chasewillden-netflix-shows/data/netflix.csv')
df.head()
 titleratingratinglevelratingdescriptionrelease_yearuser_rating_scoreuser_rating_size
0White ChicksPG-13crude and sexual humor, language and some drug...80200482.080
1Lucky Number SlevinRstrong violence, sexual content and adult lang...1002006NaN82
2Grey's AnatomyTV-14Parents strongly cautioned. May be unsuitable ...90201698.080
3Prison BreakTV-14Parents strongly cautioned. May be unsuitable ...90200898.080
4How I Met Your MotherTV-PGParental guidance suggested. May not be suitab...70201494.080

Parece bem legal: você tem títulos, classificação indicativa, ano de lançamento e a nota dos usuários, entre várias outras colunas. Antes de executar o groupby e o procedimento split-apply-combine, vamos olhar os dados com um pouco mais de atenção para garantir que são o que pensamos e para tratar valores ausentes. Note que há um valor ausente NaN em user_rating_score da segunda linha (linha 1).

Resumindo seus dados com gráficos e estatísticas

O método .info() do DataFrame do pandas é valioso. Aplicando-o abaixo, vemos que há 1000 linhas e 7 colunas, mas também que a coluna de interesse, user_rating_score, tem apenas 605 valores não nulos. Isso quer dizer que existem 395 valores ausentes:

# Check out info of DataFrame
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 7 columns):
title                1000 non-null object
rating               1000 non-null object
ratinglevel          941 non-null object
ratingdescription    1000 non-null int64
release_year         1000 non-null int64
user_rating_score    605 non-null float64
user_rating_size     1000 non-null int64
dtypes: float64(1), int64(3), object(3)
memory usage: 54.8+ KB

Você pode remover as linhas que tenham qualquer valor ausente, eliminar linhas duplicadas e construir um pairplot do DataFrame usando o seaborn para ter uma visão geral visual dos dados. Vamos colorir pelos valores da coluna 'rating'. Confira os gráficos e veja que insights consegue tirar.

# Drop rows with missing values and drop duplicate
df.dropna(inplace=True)
df.drop_duplicates(inplace=True)

# Visualize pairplot of df
sns.pairplot(df, hue='rating');

pairplot seaborn

Observe, por exemplo, user_rating_score em função de release_year. Não há uma tendência visual clara, mas talvez uma análise mais aprofundada revele algum padrão. Se quiser ver várias estatísticas descritivas do DataFrame, use o método .describe():

# Get summary stats of df
df.describe()
 ratingdescriptionrelease_yearuser_rating_scoreuser_rating_size
count246.000000246.000000246.000000246.0
mean73.5569112010.27235881.39024480.0
std26.6161458.88721912.6778830.0
min10.0000001940.00000055.00000080.0
25%60.0000002007.00000071.00000080.0
50%80.0000002015.00000083.50000080.0
75%90.0000002016.00000092.75000080.0
max124.0000002017.00000099.00000080.0

Groupbys e split-apply-combine para responder à pergunta

Etapa 1. Split

Agora que você já deu uma olhada nos dados, é hora da parte divertida. Primeiro, vamos usar groupby para dividir os dados em grupos, em que cada grupo é o conjunto de filmes lançados em um determinado ano. Esse é o split do split-apply-combine:

# Group by year
df_by_year = df.groupby('release_year')

Isso cria um objeto groupby:

# Check type of GroupBy object
type(df_by_year)
pandas.core.groupby.DataFrameGroupBy

Etapa 2. Apply

Objetos groupby são muito úteis. Lembra que o método .describe() de um DataFrame retorna estatísticas descritivas para colunas numéricas? Pois bem, o .describe() em objetos DataFrameGroupBy retorna estatísticas para cada coluna numérica, mas calculadas para cada grupo do split. No seu caso, para cada release_year. Esse é um exemplo do apply no split-apply-combine: você está aplicando o .describe() a cada grupo do groupby. Faça isso e imprima as 5 primeiras linhas do resultado:

# Summary stats over years
df_by_year.describe().head()
 ratingdescriptionuser_rating_scoreuser_rating_size
 countmeanstdmin25%50%75%maxcountmean...75%maxcountmeanstdmin25%50%75%max
release_year                     
19401.035.0NaN35.035.035.035.035.01.061.0...61.061.01.080.0NaN80.080.080.080.080.0
19781.060.0NaN60.060.060.060.060.01.086.0...86.086.01.080.0NaN80.080.080.080.080.0
19821.060.0NaN60.060.060.060.060.01.068.0...68.068.01.080.0NaN80.080.080.080.080.0
19861.035.0NaN35.035.035.035.035.01.067.0...67.067.01.080.0NaN80.080.080.080.080.0
19871.060.0NaN60.060.060.060.060.01.058.0...58.058.01.080.0NaN80.080.080.080.080.0

5 rows × 24 columns

Se quiser ver como ficou o agrupamento, você pode passar o objeto groupby para a função list():

# Cast grouping as a list and check out one year
list(df_by_year)[10]
(1995,             title rating                                ratinglevel  \
 766         Balto      G  General Audiences. Suitable for all ages.   
 967  Heavyweights     PG              some rude language and pranks   

      ratingdescription  release_year  user_rating_score  user_rating_size  
 766                 35          1995               64.0                80  
 967                 60          1995               74.0                80  )

Etapa 3. Combine

Digamos que você queira a média ou a mediana de user_rating_score para cada ano. Você pode aplicar os métodos .mean() ou .median(), respectivamente, ao objeto groupby e "combinar" tudo em um novo DataFrame.

# Get median values by year and print first 5 rows
df_med_by_year = df_by_year.median()
df_med_by_year.head()
 ratingdescriptionuser_rating_scoreuser_rating_size
release_year   
194035.061.080.0
197860.086.080.0
198260.068.080.0
198635.067.080.0
198760.058.080.0

Há uma sutileza importante sobre o índice do DataFrame df_med_by_year. Lembre que o índice de um DataFrame consiste nos rótulos das linhas. Veja o índice do DataFrame original df:

# Print index of df
print(df.index)
Int64Index([  0,   2,   3,   4,   5,   6,   7,   8,   9,  10,
            ...
            908, 911, 917, 931, 962, 966, 967, 972, 973, 979],
           dtype='int64', length=246)

Esse índice é composto pelos números das linhas originais, rotulados por inteiros. O '1' está faltando porque você removeu algumas linhas acima. Já o índice de df_med_by_year é composto pelos valores da coluna original pela qual você agrupou, os anos de release_year:

# Print index
print(df_med_by_year.index)
Int64Index([1940, 1978, 1982, 1986, 1987, 1989, 1990, 1992, 1993, 1994, 1995,
            1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
            2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017],
           dtype='int64', name='release_year')

O seu interesse é a coluna user_rating_score, que contém a mediana das notas por ano. Você pode fatiar a coluna user_rating_score de df_med_by_year e plotá-la em função do ano (dado pelo índice do DataFrame df_rat_by_year):

# Slice out user rating and plot
df_rat_by_year = df_med_by_year['user_rating_score']
plt.scatter(df_rat_by_year.index, df_rat_by_year)
plt.xlabel('year of release')
plt.ylabel('median rating');

Netflix groupby split-apply-combine

Olhando o gráfico, a mediana das notas claramente aumenta ao longo do tempo. Seria preciso aplicar estatísticas mais robustas para me convencer da tendência de forma geral, mas este é um exemplo de como a análise exploratória de dados é um ótimo ponto de partida para pesquisas mais profundas.

Groupbys e split-apply-combine no dia a dia

Objetos groupby não são intuitivos. Eles, no entanto, correspondem ao ato natural de dividir um conjunto de dados com respeito a uma de suas colunas (ou mais de uma, mas vamos deixar isso para outro post sobre agrupamento por múltiplas colunas e índices hierárquicos).

O princípio split-apply-combine não é apenas elegante e prático: é algo que cientistas de dados usam diariamente, como no exemplo acima. Para conhecer mais aplicações, confira o artigo original do Hadley Wickham aqui, The Split-Apply-Combine Strategy for Data Analysis. Se quiser trocar ideias, respostas e/ou reflexões, fale comigo no Twitter: @hugobowne.

Confira também outros tutoriais úteis de pandas no DataCamp, incluindo:

Temas

Relacionado

data-frames-in-python-banner_cgzjxy.jpeg

Tutorial

Pandas Tutorial: DataFrames em Python

Tutorial

Tutorial de seleção de colunas em Python