Netflix Data: Exploratory analysis

João Cláudio Nunes Carvalho
11 min readFeb 20, 2022

--

Exploramos aqui o dataset que pode ser encontrado em:

https://www.kaggle.com/atharvaj9/netflix-eda-shows-vs-movies/data

A análise exploratória de dados (EDA) é utilizada por cientistas de dados para analisar e investigar conjuntos de dados e resumir suas principais características, como por exemplo descobrir padrões, detectar anomalias, testar hipóteses e verificar suposições com a ajuda de estatísticas resumidas e representações gráficas.

Ele é usado para entender os dados primeiro e tentar coletar o máximo de insights deles e, em seguida, iniciar a análise sobre o tema.

Aqui iremos analisar os dados disponibilizados pelo Kagle, de acordo com o link acim, utilizaremos o python para essa análise e algumas de suas bibliotecas.

Vamos fazer a importação das bibliotecas:

No caso vamos usar o Numpy, o pandas, a biblioteca plotly, seaborn e matplotlib:

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.figure_factory as ff
import seaborn as sns

import matplotlib.pyplot as plt
#vamos armazenar o dataset obtido no kagle na variável netflix
#Storing netflix data in netflix variable
netflix = pd.read_csv('../input/netflix-shows/netflix_titles.csv')
#netflix

Para visualizarmos a a quantidade de linhas e colunas do DataFrame, utilizaremos a função shape:

#shape of data
netflix.shape

A propriedade shape retorna uma tupla representando a dimensionalidade do DataFrame. O formato da forma será (linhas, colunas).

Vamos aplicar aqui para ver o resultado:

#shape of data
netflix.shape

O resultado será:

(8807, 12)

Em seguida vamos ver aqui o nome das colunas que existem em nosso dataframe, para isso usaremos a função columns:

netflix.columns

E como resultado teremos:

Index(['show_id', 'type', 'title', 'director', 'cast', 'country', 'date_added','release_year', 'rating', 'duration', 'listed_in', 'description'],dtype='object')

Agora vamos obter as informações relativas a cada uma das colunas de nosso dataset, para isso utilizaremos a função info

#data information 
netflix.info()

o resultado será:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8807 entries, 0 to 8806
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 show_id 8807 non-null object
1 type 8807 non-null object
2 title 8807 non-null object
3 director 6173 non-null object
4 cast 7982 non-null object
5 country 7976 non-null object
6 date_added 8797 non-null object
7 release_year 8807 non-null int64
8 rating 8803 non-null object
9 duration 8804 non-null object
10 listed_in 8807 non-null object
11 description 8807 non-null object
dtypes: int64(1), object(11)
memory usage: 825.8+ KB

Vamos agora procurar em quais colunas temos valores nulos:

A função isnull() detecta valores ausentes ou nulos para um objeto escalar ou do tipo array, retornando True (caso tenha valou ausente) ou False( caso não encontre valor nulo)

#data information 
netflix.isnull()

temos valores nulos em director, cast, country, date_added, rating e duration.

A função sum() faz a soma da quantidade de valores nulos para cada coluna onde esses valores são encontrados:

netflix.isnull().sum()

Como resultado temos:

show_id            0
type 0
title 0
director 2634
cast 825
country 831
date_added 10
release_year 0
rating 4
duration 3
listed_in 0
description 0
dtype: int64

Para termos uma ideias dos valores que estão armazenados no dataset, vamos usar aqui a função head().

netflix.head()

Como resultado teremos:

Agora vamos fazer a correção dos valores ausentes (NaN)

  • Primeiro passo: como podemos ver todos os dados que temos para director e cast são dados categóricos, então substituiremos “Desconhecido” nos valores NaN no conjunto de dados.
  • Segundo passo: Podemos encontrar o país para um filme específico e onde ele é exibido, mas não temos certeza caso na coluna apareça o valor Nan, para esses casos iremos substituí-lo pelo valor “Ausente ou desconhecido”.
  • Terceiro passo: Mesmo passo para todos os atributos ausentes.
netflix['director'].fillna('Unknown',inplace=True)
netflix['cast'].fillna('Unknown',inplace=True)
netflix['country'].fillna('Unknown',inplace=True)
netflix['rating'].fillna('Missing',inplace=True)
netflix['duration'].fillna(0,inplace=True)

Vamos ver agora se ainda temos algum valor nulo nas colunas:

netflix.isnull().sum()

Como vemos abaixo, apenas em date_added que têm-se valores nulos.

show_id          0
type 0
title 0
director 0
cast 0
country 0
date_added 10
release_year 0
rating 0
duration 0
listed_in 0
description 0
dtype: int64

Vamos proceder com a correção:

iremos usar aqui a função dropna()

dropna() remove os valores nulos (valores ausentes) do DataFrame , deixando cair as linhas ou colunas que contêm os valores nulos. Os valores nulos são NaN ( Not a Number ) e NaT ( Not a Time ). O DataFrame. dropna() detecta esses valores e filtra o DataFrame de acordo.

#droping NA values for date values 
netflix = netflix.dropna()
netflix.isnull().sum()

Como resultado temos:

show_id         0
type 0
title 0
director 0
cast 0
country 0
date_added 0
release_year 0
rating 0
duration 0
listed_in 0
description 0
dtype: int64

Vamos agora fazer a conversão do formato de data, separando a data do mês e o ano do restantes:

netflix["date_added"] = pd.to_datetime(netflix["date_added"])

netflix["month_added"] = netflix["date_added"].dt.month
netflix["month_name_added"] = netflix["date_added"].dt.month_name()
netflix["year_added"] = netflix["date_added"].dt.year

Na coluna country, temos algumas linhas nas quais os países são separados por vírgula:

Iremos criar um vetor chamado countries que irá conter todos os países existentes na coluna country, porém sem nenhuma reptição, daí vamos acrescentar todos esses países como colunas em nosso dataframe, essas colunas serão adicionadas após a coluna description.

#criei aqui um vetor chamado countries totalmente vazio
countries = []
#len(netflix.index) -> quantidade de linhas do dataframe
#vou percorrer toda a coluna country, indicada aqui no comando iloc[x,5] pois country é a 6a coluna
for i in range(len(netflix.index)):
x = netflix.iloc[i, 5].split(',')
#separo o valor encontrado em cada linha em função da vírgula e armazeno no vetor x
#percorro o vetor x
#verifico se o país existe no vetor x está no vetor countries
#senão estiver eu acrescento o país em countries
for j in range(len(x)):
if x[j].strip() not in countries:
countries.append(x[j].strip())
else:
continue

countries.remove('')
#countries

Após o código acima eu posso chamar o seguinte comando

print(countries)

O resultado será um vetor com todos os países existentes em meu dataframe sem repetição.

['United States', 'South Africa', 'Unknown', 'India', 'Ghana', 'Burkina Faso', 'United Kingdom', 'Germany', 'Ethiopia', 'Czech Republic', 'Mexico', 'Turkey', 'Australia', 'France', 'Finland', 'China', 'Canada', 'Japan', 'Nigeria', 'Spain', 'Belgium', 'South Korea', 'Singapore', 'Italy', 'Romania', 'Argentina', 'Venezuela', 'Hong Kong', 'Russia', '', 'Ireland', 'Nepal', 'New Zealand', 'Brazil', 'Greece', 'Jordan', 'Colombia', 'Switzerland', 'Israel', 'Taiwan', 'Bulgaria', 'Algeria', 'Poland', 'Saudi Arabia', 'Thailand', 'Indonesia', 'Egypt', 'Denmark', 'Kuwait', 'Netherlands', 'Malaysia', 'Vietnam', 'Hungary', 'Sweden', 'Lebanon', 'Syria', 'Philippines', 'Iceland', 'United Arab Emirates', 'Norway', 'Qatar', 'Mauritius', 'Austria', 'Cameroon', 'Palestine', 'Uruguay', 'Kenya', 'Chile', 'Luxembourg', 'Cambodia', 'Bangladesh', 'Portugal', 'Cayman Islands', 'Senegal', 'Serbia', 'Malta', 'Namibia', 'Angola', 'Peru', 'Mozambique', 'Belarus', 'Zimbabwe', 'Puerto Rico', 'Pakistan', 'Cyprus', 'Guatemala', 'Iraq', 'Malawi', 'Paraguay', 'Croatia', 'Iran', 'West Germany', 'Albania', 'Georgia', 'Soviet Union', 'Morocco', 'Slovakia', 'Ukraine', 'Bermuda', 'Ecuador', 'Armenia', 'Mongolia', 'Bahamas', 'Sri Lanka', 'Latvia', 'Liechtenstein', 'Cuba', 'Nicaragua', 'Slovenia', 'Dominican Republic', 'Samoa', 'Azerbaijan', 'Botswana', 'Vatican City', 'Jamaica', 'Kazakhstan', 'Lithuania', 'Afghanistan', 'Somalia', 'Sudan', 'Panama', 'Uganda', 'East Germany', 'Montenegro']

O código abaixo acrescrenta todos os países do vetor countries, que estão listados logo acima, como colunas em nosso dataframe netflix.

se chamarmos len(countries), o retorno será 123.

#len(countries) -fornece a quantidade de países sem repetição
for i in range(len(countries)):#considero a quantidade de países
#adiciono cada país como uma coluna em netflix e atribuo 0 em
#todas as linhas

netflix[countries[i]] = 0
#percorro todas as linhas do dataframe (uso o índice j)
#verifico se o i-ésimo país está na coluna country, se tiver atribuo
#o valor 1 na coluna referente aquele país, lembre que adicionei #todos os possíveis países como colunas
for j in range(len(netflix.index)):
if countries[i] in netflix.iloc[j, 5]:
netflix[countries[i]][j] = 1

Claro que agora nosso dataframe está com uma quantidade enorme de colunas, para checar, pode chamar novamente a função

netflix.shape

o resultado será: (8807, 138)

Ou seja, antes nós tinhamos 15 colunas, como temos 123 países, agora temos 138 colunas no dataframe.

Vamos fazer o mesmo procedimento com a coluna listed_in, que são os gêneros de cada filme, vale ressaltar que nessa coluna podemos ter mais de uma categoria para um mesmo filme.

Só pra termos uma ideia do que tem na coluna listed_in:

chamaremos o comando netflix.listed_in

0                                           Documentaries
1 International TV Shows, TV Dramas, TV Mysteries
2 Crime TV Shows, International TV Shows, TV Act...
3 Docuseries, Reality TV
4 International TV Shows, Romantic TV Shows, TV ...
...
8802 Cult Movies, Dramas, Thrillers
8803 Kids' TV, Korean TV Shows, TV Comedies
8804 Comedies, Horror Movies
8805 Children & Family Movies, Comedies
8806 Dramas, International Movies, Music & Musicals
Name: listed_in, Length: 8807, dtype: object

temos aqui várias linhas onde temos mais de um gênero para um mesmo filme.

Ou seja iremos criar uma coluna chamada genres com todos os possíveis gêneros sem repetição.

Usaremos agora o mesmo código que usamos anteriormente para países.

genres = []for i in range(len(netflix.index)):
x = netflix.iloc[i, 10].split(‘,’)
for j in range(len(x)):
if x[j].strip() not in genres:
genres.append(x[j].strip())
else:
continue

se verificarmos o que tem no vetor genres, por meio do comando print(genres)

Obteremos:

['Documentaries',
'International TV Shows',
'TV Dramas',
'TV Mysteries',
'Crime TV Shows',
'TV Action & Adventure',
'Docuseries',
'Reality TV',
'Romantic TV Shows',
'TV Comedies',
'TV Horror',
'Children & Family Movies',
'Dramas',
'Independent Movies',
'International Movies',
'British TV Shows',
'Comedies',
'Spanish-Language TV Shows',
'Thrillers',
'Romantic Movies',
'Music & Musicals',
'Horror Movies',
'Sci-Fi & Fantasy',
'TV Thrillers',
"Kids' TV",
'Action & Adventure',
'TV Sci-Fi & Fantasy',
'Classic Movies',
'Anime Features',
'Sports Movies',
'Anime Series',
'Korean TV Shows',
'Science & Nature TV',
'Teen TV Shows',
'Cult Movies',
'TV Shows',
'Faith & Spirituality',
'LGBTQ Movies',
'Stand-Up Comedy',
'Movies',
'Stand-Up Comedy & Talk Shows',
'Classic & Cult TV']

Agora vamos acrescentar cada um desses gêneros como colunas em nosso data frame.

#len(genres)->fornece a quantidade de gêneros sem repetiçãofor i in range(len(genres)):#considero a quantidade de gêneros 
#adiciono cada gênero como uma coluna em netflix e atribuo 0
#em cada uma das linhas

netflix[genres[i]] = 0
#percorro todas as linhas do dataframe (uso o índice j)
#verifico se o i-ésimo gênero está na coluna listed_in, se tiver
# atribuo o valor 1 na coluna referente aquele gênero, lembre que #adicionei todos os possíveis gêneros como colunas
for j in range(len(netflix.index)):
if genres[i] in netflix.iloc[j, 10]:
netflix[genres[i]][j] = 1

observação: iloc[j,10] se refere a listed_in

Agora vamos remover as colunas que não serão utilizadas em nossa análise:

netflix = netflix.drop([‘listed_in’, ‘date_added’, ‘show_id’, ‘country’, ‘Unknown’], axis = 1) 
netflix

Nosso data frame fica assim

Vamos agora alterar a duração, removendo min e temporadas da duração e mantendo os dados no formato int.

netflix['duration'] = netflix['duration'].astype('str').map(lambda x: x.split(' ')[0])netflix['duration'] =netflix['duration'].astype('int')

Vamos agora fazer algumas análises por meio de gráficos.

# Iniciaremos verificando o conteúdo adicionado na Netflix ao longo dos anos, para ter uma ideia de qual ano estava dominando.

Antes vamos entender algumas funções:

reset_index em pandas é usado para redefinir o índice do objeto dataframe para a indexação padrão (0 para o número de linhas menos 1) ou para redefinir o índice de vários níveis.

agg() é usado para passar uma função ou lista de funções a serem aplicadas em uma série ou mesmo cada elemento da série separadamente.

content_by_year = netflix
.query('year_added != "No" & year_added != ""')
.groupby('year_added')
.agg({'type': 'count'})
.reset_index()
.rename(columns = {'type': 'content'})
.sort_values('content')
fig1 = px.bar(content_by_year['year_added'],x = content_by_year['year_added'], y = content_by_year['content'])fig1.update_traces(marker_color='blue')
fig1.show()

Como podemos ver a maior parte do conteúdo foi adicionado no período de 2018 e 2021. E sabemos que a Netflix continua adicionando conteúdo ao longo de meses também. Agora também veremos qual mês tem mais uploads de conteúdo.

content_by_month = netflix
.query(‘month_name_added != “No” & month_name_added != “”’)
.groupby(‘month_name_added’)
.agg({‘type’: ‘count’})
.reset_index()
.rename(columns = {‘type’: ‘content’})
.sort_values(‘content’)
fig2 = px.pie( names= content_by_month['month_name_added'], values= content_by_month['content'])fig2.show()

Podemos observar que a maior parte do conteúdo adicionado no catálogo da Netflix ocorre nos meses de dezembro e julho. Podemos fazer suposições de que dezembro é o mês principal por causa do Natal e julho é o período de verão. Assim, podemos ter certeza de que a maior parte do conteúdo adicionado ao longo dos meses ocorre no período de férias para que as pessoas possam assistir a programas e filmes com a família.

Análise da Proporção de filmes e programas de TV

Podemos confirmar que os filmes são mais do que programas de TV.

show_type = netflix.groupby(['type'])['type'].count()
total = len(netflix)

ratio = ((show_type/total)).round(2)
print(ratio)

# Using plotly we will plot how many types of Movies and TV shows are present

fig = px.pie(netflix['type'].value_counts().reset_index(), values = 'type', names = 'index', width = 600, height = 600)
fig.update_traces(textposition = 'inside',
textinfo = 'percent + label',
hole = 0.5,
marker = dict(colors = ['#b20710','#221f1f'], line = dict(color = 'white', width = 2)))

fig.update_layout(annotations = [dict(text = 'TV Shows <br> VS <br> Movies',
x = 0.5, y = 0.5, font_size = 28, showarrow = False,
font_family = 'Calibri Black',
font_color = 'black')],
showlegend = False)

fig.show()

Obtemos o seguinte gráfico:

Um outro gráfico pode ser feito com ajuda da biblioteca seaborn:

sns.set(style="darkgrid")
ax = sns.countplot(x="type", data=netflix_overall, palette="Set2")

Podemos confirmar que os filmes estão em maior quantidade do que os programas de TV.

Análise de classificações de filmes

plt.figure(figsize=(12,10))
sns.set(style="darkgrid")
ax = sns.countplot(x="rating", data=netflix, palette="Set2", order=netflix['rating'].value_counts().index[0:15])

A maior contagem de filmes é feita com a classificação ‘TV-MA’. “TV-MA” é uma classificação atribuída pelas Diretrizes dos Pais de TV a um programa de televisão projetado apenas para o público adulto.

O segundo maior é o ‘TV-14’, que significa conteúdo que pode ser inadequado para crianças menores de 14 anos.

A terceira maior é a muito popular classificação ‘R’. Um filme com classificação R é um filme que foi avaliado como tendo material que pode ser inadequado para crianças menores de 17 anos pela Motion Picture Association of America; a MPAA escreve “Abaixo de 17 anos requer acompanhamento dos pais ou responsável adulto”.

Análise da duração dos filmes

sns.set(style="darkgrid")
sns.kdeplot(data=netflix['duration'], shade=True)

Assim, uma boa quantidade de filmes na Netflix está entre 75–120 minutos. É aceitável, considerando o fato de que uma boa parte do público não pode assistir a um filme de 3 horas de uma só vez.

Anos de lançamento dos filmes

plt.figure(figsize=(12,10))
sns.set(style=”darkgrid”)
ax = sns.countplot(y=”release_year”, data=netflix, palette=”Set2", order=netflix[‘release_year’].value_counts().index[0:15])

Então, 2017 foi o ano em que a maioria dos filmes foi lançada.

Top10 gêneros nos filmes

plt.figure(figsize=(15,5))sns.barplot(
x = netflix[“listed_in”].value_counts().head(10).index,
y = netflix[“listed_in”].value_counts().head(10).values,
palette=”pink”)
plt.xticks(rotation=60)
plt.title(“Top10 gêneros nos filmes”,fontweight=”bold”)
plt.show()

--

--

João Cláudio Nunes Carvalho

Professor of Physics at the Federal Institute of Ceará. Phd in physics(UFC). MBA in Data Science and Analytics — USP — University of São Paulo