Netflix Investor: Quarterly Earnings

09 de maio, 2020

1. Descrição geral do problema


Netflix Logo

A Netflix é uma provedora global de filmes e séries de televisão via streaming sediada em Los Gatos, Califórnia, e que atualmente possui mais de 160 milhões de assinantes. Fundada em 1997 nos Estados Unidos, a empresa surgiu como um serviço de entrega de DVD pelo correio. A expansão do streaming, disponível nos Estados Unidos a partir de 2007, começou pelo Canadá em 2010. Hoje, mais de 190 países têm acesso à plataforma. Sua primeira websérie original de sucesso foi House of Cards, lançada em 2013. Hoje em dia, a empresa produz centenas de horas de programação original em diferentes países do mundo, querendo aprimorar-se nas aplicações e em novas programações.

Neste projeto iremos analisar os principais dados financeiros fornecidos trimestralmente pela Netflix entre 2012 e 2018. Os dados foram extraídos da página Netflix Investor.

Objetivo: utilizar a linguagem R para preparar um resumo estatístico dos dados da empresa, demonstrando sua saúde financeira e outras informações relevantes, que poderão ajudar potenciais investidores a decidir sobre investir ou não na companhia.


2. Carregando Dados

2.1 Importando bibliotecas necessárias

Começaremos nosso projeto, importanto todas as bilbiotecas necessárias, para a realização das fases iniciais de exploração, e transformação dos dados (Data Munging).

In [ ]:
# Definindo a ocultação de warnings.

options(warn = -1)

# Difinindo o nome das bibliotecas que serão utilizadas.

librarys <- c(
    'ggplot2',
    'plyr',
    'corrplot',
    'caret',
    'GGally',
    'dplyr',
    'DMwR',
    'e1071',
    'data.table', 
    'readxl', 
    'lubridate',
    'car',
    'xts',
    'forecast',
    'tseries',
    'tidyr',
    'ggfortify'
)
In [ ]:
# Instalando bibliotecas necesárias diretamente do repósito no Github.

devtools:: install_github("brisneve/ggplottimeseries")

# Atribuindo bilioteca a lista de importações.

librarys <- append(librarys, 'ggplottimeseries')
In [ ]:
# Se não possuir uma das bibliotecas importadas abaixo, use a função a seguir:

install.packages(librarys, character.only = TRUE)
In [ ]:
# Importando as bibliotecas presentes na lista.

for(l in librarys) {
    
    # Importa a biblioteca especificada.

    library (
        package        = l,    # Define o nome da biblioteca que deve ser importada.
        character.only = TRUE  # Define que o nome do pacote deve ser considerado como uma String.
    ) 
}

2.2 Carregando Dados

In [ ]:
# Importando os dados do dataset.

dataset <- read_xlsx("/content/datasets/Netflix_Data.xlsx", col_names =  T)

# Criando cópia do dataset.

data <- dataset

# Visualizando os primeiros registros do conjunto de dados.

head(data)
A tibble: 6 × 13
TimeTotal subscriptions at end of periodPaid subscriptions at end of periodFree TrailsRevenueCost of revenuesMarketingContribution profitContribution MarginCost per Customer (excluding marketing)Revenue per CustomerEarnings per CustomerSegment
<chr><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><chr>
March 31,2012 2341022022138850666536077679381 665080.13115.4111921.643106.231909Streaming
June 30,2012 2393822686125253270537857470959 831720.15615.8147722.253536.438758Streaming
September 30,20122510123801130055602739912465955 909480.16415.9007222.151596.250866Streaming
December 31,2012 27146254711675589471420390597771093040.18515.4862621.714846.228579Streaming
March 31,2013 29174279131261638649440334669651313500.20615.0933721.891036.797662Streaming
June 30,2013 29807286241183671089452598671771513140.22515.1842922.514487.330191Streaming

3. Data Munging

3.1 Visão geral dos dados

Antes de prosseguirmos, é importante destacarmos que cada observação do dataset, contém dados que foram extraídos do site da Netflix Investor, e estão distribuídos nas seguintes variáveis:

Variável Tipo Descrição
Time Character É a data de emissão do resultado trimestral;
Total subscriptions at end of period Double É o número total de assinaturas registradas no final do trimestre;
Paid subscriptions at end of period Double É o número de assinaturas pagas registradas no final do trimestre;
Free Trails Double É o número de assinaturas em período de utilização gratuita registradas no final do trimestre;
Revenue Double É a receita trimestral gerada para o Streaming doméstico (em milhares de dólares);
Cost of revenues Double É o custo trimestral da receita gerada para o Streaming doméstico (em milhares de dólares);
Marketing Double É o custo trimestral de Marketing gerado para o Streaming doméstico (em milhares de dólares);
Contribution profit Double É o lucro da contribuição trimestral (em milhares de dólares);
Contribution Margin Double É a margem de contribuição trimestral (Contribution profit / Revenue);
Cost per Customer (excluding marketing) Double É a taxa de custo por cliente desconsiderando os gastos com Marketing (Cost of revenues / Total subscriptions at end of period);
Revenue per Customer Double É a taxa de receita por cliente (Revenue / Total subscriptions at end of period);
Earnings per Customer Double É a taxa de lucro por cliente ((Revenue - Cost of revenues) / Total subscriptions at end of period) e;
Segment Character É o segmento de operação da companhia ao qual os dados se referem.

Nosso primeio passo, será trocar o nome das colunas para facilitar a análise nas próximas etapas.

In [ ]:
# Alterando os nomes das colunas do dataset.

names(data) <- c(
  "date",
  "totalSubscriptions",
  "paidSubscriptions",
  "freeTrails",
  "revenue",
  "costOfRevenues",
  "marketing",
  "contributionProfit",
  "contributionMargin",
  "costPerCustomer",
  "revenuePerCustomer",
  "earningsPerCustomer",
  "segment"
)

# Visualizando os primeiros registros do conjunto de dados.

head(data)
A tibble: 6 × 13
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegment
<chr><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><chr>
March 31,2012 2341022022138850666536077679381 665080.13115.4111921.643106.231909Streaming
June 30,2012 2393822686125253270537857470959 831720.15615.8147722.253536.438758Streaming
September 30,20122510123801130055602739912465955 909480.16415.9007222.151596.250866Streaming
December 31,2012 27146254711675589471420390597771093040.18515.4862621.714846.228579Streaming
March 31,2013 29174279131261638649440334669651313500.20615.0933721.891036.797662Streaming
June 30,2013 29807286241183671089452598671771513140.22515.1842922.514487.330191Streaming
In [ ]:
# Verificando a existência de registros duplicados.

table(duplicated(data))
FALSE 
   28 

Bom, constatamos que não há registros duplicados no conjunto de dados.

In [ ]:
# Verificando a existência de valores NA no dataset.

data.frame(anyNA = sapply(data, anyNA))
A data.frame: 13 × 1
anyNA
<lgl>
dateFALSE
totalSubscriptionsFALSE
paidSubscriptionsFALSE
freeTrailsFALSE
revenueFALSE
costOfRevenuesFALSE
marketingFALSE
contributionProfitFALSE
contributionMarginFALSE
costPerCustomerFALSE
revenuePerCustomerFALSE
earningsPerCustomerFALSE
segmentFALSE

Não foi detectado nenhum valor NA dentro do conjunto de dados.

3.2 Alterando os tipos de dados das variáveis

Algumas varíaveis no dataset, foram associadas a tipos de dados incorretos. Observe que:

  • A variável date indica uma data, e por isso, a converteremos para o tipo Date;
  • As variáveis totalSubscriptions, paidSubscriptions e freeTrails são quantitativas discreta, pois apresentam valores baseados em contagens, e por isso, as converteremos para o tipo de dado integer e;
  • A variável segment é qualitativa nominal, o que a faz ser melhor representada, pelo tipo de dado factor.

Começaremos alterando o tipo de dado da variável date.

In [ ]:
# Alterando o tipo de dado da variável date.

data$date <- as.Date(data$date, format = "%B %d, %Y")

Converteremos o tipo de dado da variável segment.

In [ ]:
# Alterando o tipo de dado da variável segment.

data$segment <- as.factor(data$segment)

Agora, iremos verificar, se existe algum valor frácionário no conjunto de dados das variáveis totalSubscriptions, paidSubscriptions e freeTrails, que justifique, a utilização do tipo de dado com o qual o dataset foi carregado.

In [ ]:
# Verificando os valores únicos existentes, em cada variável do dataset, antes da conversão do tipo de dado.

uniqValuesBefore <- sapply(data, function(v) {
    sort(unique(v))
})

# Exibindo valores únicos, em cada variável do dataset, que irá sofrer a conversão.

uniqValuesBefore[c('totalSubscriptions', 'paidSubscriptions', 'freeTrails')]
$totalSubscriptions
  1. 23410
  2. 23938
  3. 25101
  4. 27146
  5. 29174
  6. 29807
  7. 31092
  8. 33420
  9. 35674
  10. 36244
  11. 37219
  12. 39114
  13. 41397
  14. 42300
  15. 43181
  16. 44738
  17. 45714
  18. 46004
  19. 46479
  20. 47905
  21. 49375
  22. 50323
  23. 51345
  24. 52810
  25. 55087
  26. 55959
  27. 56957
  28. 58486
$paidSubscriptions
  1. 22022
  2. 22686
  3. 23801
  4. 25471
  5. 27913
  6. 28624
  7. 29925
  8. 31712
  9. 34377
  10. 35085
  11. 36265
  12. 37698
  13. 40315
  14. 41057
  15. 42068
  16. 43401
  17. 44461
  18. 44879
  19. 45461
  20. 46379
  21. 47896
  22. 48725
  23. 49918
  24. 50870
  25. 53469
  26. 54539
  27. 55450
  28. 56421
$freeTrails
  1. 954
  2. 1018
  3. 1082
  4. 1113
  5. 1125
  6. 1159
  7. 1167
  8. 1183
  9. 1243
  10. 1252
  11. 1253
  12. 1261
  13. 1297
  14. 1300
  15. 1337
  16. 1388
  17. 1416
  18. 1420
  19. 1427
  20. 1479
  21. 1507
  22. 1526
  23. 1598
  24. 1618
  25. 1675
  26. 1708
  27. 1940
  28. 2065

Bom, não encontramos nenhum valor fracionário e por isso iremos converter as variáveis para o tipo de dado inteiro.

In [ ]:
# Alterando o tipo da variável totalSubscriptions para o tipo inteiro.

data$totalSubscriptions <- as.integer(data$totalSubscriptions)

# Alterando o tipo da variável paidSubscriptions para o tipo inteiro.

data$paidSubscriptions <- as.integer(data$paidSubscriptions)

# Alterando o tipo da variável freeTrails para o tipo inteiro.

data$freeTrails <- as.integer(data$freeTrails)

# Visualizando os primeiros registros do conjunto de dados.

head(data)
A tibble: 6 × 13
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegment
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct>
2012-03-312341022022138850666536077679381 665080.13115.4111921.643106.231909Streaming
2012-06-302393822686125253270537857470959 831720.15615.8147722.253536.438758Streaming
2012-09-302510123801130055602739912465955 909480.16415.9007222.151596.250866Streaming
2012-12-3127146254711675589471420390597771093040.18515.4862621.714846.228579Streaming
2013-03-3129174279131261638649440334669651313500.20615.0933721.891036.797662Streaming
2013-06-3029807286241183671089452598671771513140.22515.1842922.514487.330191Streaming
In [ ]:
# Verificando os valores únicos existentes, em cada variável do dataset, após a conversão do tipo de dado.

uniqValuesAfter <- sapply(data, function(v) {
    sort(unique(v))
})

# Exibindo valores únicos, em cada variável do dataset, que sofreu a conversão.

uniqValuesAfter[c('totalSubscriptions', 'paidSubscriptions', 'freeTrails')]
$totalSubscriptions
  1. 23410
  2. 23938
  3. 25101
  4. 27146
  5. 29174
  6. 29807
  7. 31092
  8. 33420
  9. 35674
  10. 36244
  11. 37219
  12. 39114
  13. 41397
  14. 42300
  15. 43181
  16. 44738
  17. 45714
  18. 46004
  19. 46479
  20. 47905
  21. 49375
  22. 50323
  23. 51345
  24. 52810
  25. 55087
  26. 55959
  27. 56957
  28. 58486
$paidSubscriptions
  1. 22022
  2. 22686
  3. 23801
  4. 25471
  5. 27913
  6. 28624
  7. 29925
  8. 31712
  9. 34377
  10. 35085
  11. 36265
  12. 37698
  13. 40315
  14. 41057
  15. 42068
  16. 43401
  17. 44461
  18. 44879
  19. 45461
  20. 46379
  21. 47896
  22. 48725
  23. 49918
  24. 50870
  25. 53469
  26. 54539
  27. 55450
  28. 56421
$freeTrails
  1. 954
  2. 1018
  3. 1082
  4. 1113
  5. 1125
  6. 1159
  7. 1167
  8. 1183
  9. 1243
  10. 1252
  11. 1253
  12. 1261
  13. 1297
  14. 1300
  15. 1337
  16. 1388
  17. 1416
  18. 1420
  19. 1427
  20. 1479
  21. 1507
  22. 1526
  23. 1598
  24. 1618
  25. 1675
  26. 1708
  27. 1940
  28. 2065

Por precaução, vamos nos certificar de que não perdemos nenhuma informação, e que todos os dados do conjunto das variáveis convertidas, permanecem iguais.

In [ ]:
# Verificando se todos os valores únicos da variável totalSubscriptions permanecem iguais, após a conversão do tipo de dado.

as.data.frame(table(totalSubscriptions = uniqValuesBefore$totalSubscriptions == uniqValuesAfter$totalSubscriptions))
A data.frame: 1 × 2
totalSubscriptionsFreq
<fct><int>
TRUE28
In [ ]:
# Verificando se todos os valores únicos da variável paidSubscriptions permanecem iguais, após a conversão do tipo de dado.

as.data.frame(table(paidSubscriptions = uniqValuesBefore$paidSubscriptions == uniqValuesAfter$paidSubscriptions))
A data.frame: 1 × 2
paidSubscriptionsFreq
<fct><int>
TRUE28
In [ ]:
# Verificando se todos os valores únicos da variável freeTrails permanecem iguais, após a conversão do tipo de dado.

as.data.frame(table(freeTrails = uniqValuesBefore$freeTrails == uniqValuesAfter$freeTrails))
A data.frame: 1 × 2
freeTrailsFreq
<fct><int>
TRUE28

Perfeito, nenhum dos valores das variáveis que manipulamos foi alterado, ou seja, não perdemos nenhuma informação, e já temos nosso dataset pronto para as próximas fases de análise.

4. Feature Engennier

Ter variáveis que representem o mês e o ano de cada um dos registros do conjunto de dados, pode ser interessante, e por isso, extrairemos estas informações e as armazenaremos nas variáveis month e year.

In [ ]:
# Criando variáveis para armazenar o mês e o ano de cada registro do dataset.

data <- data %>%
    mutate(
        month = as.factor(months(date)),
        year  = as.factor(year(date))
    )

# Visualizando as primeiras linhas do dataset.

head(data)
A tibble: 6 × 15
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyear
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct>
2012-03-312341022022138850666536077679381 665080.13115.4111921.643106.231909StreamingMarch 2012
2012-06-302393822686125253270537857470959 831720.15615.8147722.253536.438758StreamingJune 2012
2012-09-302510123801130055602739912465955 909480.16415.9007222.151596.250866StreamingSeptember2012
2012-12-3127146254711675589471420390597771093040.18515.4862621.714846.228579StreamingDecember 2012
2013-03-3129174279131261638649440334669651313500.20615.0933721.891036.797662StreamingMarch 2013
2013-06-3029807286241183671089452598671771513140.22515.1842922.514487.330191StreamingJune 2013

Ao analisar as características do dataset, e como elas podem se relacionar, vemos a possibilidade de se criar duas variáveis novas, que podem ser úteis para o processo de análise:

  • A primeira, para indicar a proporção de avaliações gratuitas em relação ao total de assinaturas e;
  • A segunda, para indicar a proporção do custo com marketing em relação ao custo total.

Iremos denominá-las como: freeTrialsFromTotal e marketingFromTotal, respectivamente.

In [ ]:
# Criando as variáveis freeTrialsFromTotal e marketingFromTotal.

data <- data %>%
    mutate(
        
        # Proporção de avaliações gratuitas do total de assinaturas.
        
        freeTrialsFromTotal = freeTrails / totalSubscriptions * 100,  
        
        # Proporção de custo com marketing em relação ao custo total. 
        
        marketingFromTotal  = marketing / (costOfRevenues + marketing) * 100   
    ) 

# Visualizando as primeiras linhas do dataset.

head(data)
A tibble: 6 × 17
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyearfreeTrialsFromTotalmarketingFromTotal
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct><dbl><dbl>
2012-03-312341022022138850666536077679381 665080.13115.4111921.643106.231909StreamingMarch 20125.92909018.03470
2012-06-302393822686125253270537857470959 831720.15615.8147722.253536.438758StreamingJune 20125.23017815.78505
2012-09-302510123801130055602739912465955 909480.16415.9007222.151596.250866StreamingSeptember20125.17907714.18146
2012-12-3127146254711675589471420390597771093040.18515.4862621.714846.228579StreamingDecember 20126.17033812.44921
2013-03-3129174279131261638649440334669651313500.20615.0933721.891036.797662StreamingMarch 20134.32234213.20030
2013-06-3029807286241183671089452598671771513140.22515.1842922.514487.330191StreamingJune 20133.96886612.92425

5. Análise exploratória dos dados

5.1 Criando funções auxiliares

5.1.1 Funções para a criação de gráficos

Iremos definir algumas funções, para padronizar as plotagens de gráficos que criaremos.

In [ ]:
# Definindo uma função para criar gráficos de barras.

barPlot <- function(col, data, x = 'date', color = '#EE4266', startTime = '2012-01-31') {

    data %>%
        mutate(date = format(date, "%b, %Y")) %>%
        ggplot(aes(x = reorder(date, 1:length(date)))) + 
            geom_bar(aes_string(y = col), stat = 'identity', fill = color, alpha = 0.85) +
            theme_bw() +
            xlab('Quarter') +
            ggtitle(paste("Bar chart for variable:", col)) + 
            coord_flip()
}
In [ ]:
# Definindo uma função para criar gráficos de Séries Temporais.

timeSeriesPlot <- function(col, data, x = 'date', color = '#69B3A2', startTime = '2012-01-31') {

    data %>%
        ggplot(aes_string(x = x, y = col)) + 
            geom_line(color = color, size = 2) +
            geom_point(size = 2) + 
            theme_bw() +
            theme(axis.text.x = element_text(angle = 60, hjust = 1)) + 
            scale_x_date(
                limits      = as.Date(c(startTime, NA)),
                date_labels = "%b, %Y", 
                date_breaks = '3 months'
            ) +
            xlab('Quarter') +
            ggtitle(paste("Time series chart for variable:", col)) 
}
In [ ]:
# Definindo uma função para criar gráficos Boxplot.

boxPlot <- function(col, data, x = 'date', color = c('#777DA7', '#04F06A', '#D5573B'), 
                    startTime = '2012-01-31', categ = 'None') {

    # Cria gráficos de Boxplot, para representar os valores da variável especificada, agrupados por ano.

    if(categ == 'year') {

        data %>% 
            select_at(c('year', col)) %>%
            ggplot(aes_string(x = 'year', y = col)) + 
                geom_boxplot(fill = color[1], alpha = 0.85) +
                theme_bw() +
                ggtitle(paste("Boxplot by year for variable:", col)) + 
                xlab('Year')

    # Cria gráficos de Boxplot, para representar os valores da variável especificada, agrupados por mês.

    } else if (categ == 'month'){
        
        data %>% 
            select_at(c('month', col)) %>%
            ggplot(aes_string(x = 'month', y = col)) + 
                geom_boxplot(fill = color[2], alpha = 0.85) +
                theme_bw() +
                ggtitle(paste("Boxplot by month for variable:", col)) + 
                xlab('Month')

    # Cria gráficos de Boxplot, para representar os valores da variável especificada.

    } else {
                
        data %>%
            ggplot(aes_string(x = col)) + 
                geom_boxplot(fill = color[3], alpha = 0.85) +
                theme_bw() +
                theme(axis.text.y = element_blank()) +
                ggtitle(paste("Boxplot for variable:", col))
    }
}
In [ ]:
# Definindo uma função para criar gráficos de Densidade.

densityPlot <- function(col, data, x = 'date', color = '#258EA6', startTime = '2012-01-31') {

    data %>%
        ggplot(aes_string(x = col)) + 
            geom_density(fill = color, alpha = 0.75) +
            theme_bw() +
            ggtitle(paste("Density chart for variable:", col))
}

5.1.2 Funções para calcular estatísticas

Criaremos uma função, para padronizar as estatísticas que calcularemos, em cada uma das variáveis a serem estudadas.

In [ ]:
# Definindo uma função, para gerar um dataframe, com as estatísticas de uma variável do dataset.

varStats <- function(col, data) {

    isFactor = sapply(data, is.factor)

    if(!isFactor[col]) {                 

        # Calcula estatísticas de variáveis numéricas.
        
        result <- data %>%
            summarize(
                Min       = min(get(col)),
                Q1        = unname(quantile(get(col), probs = c(0.25))),
                Median    = median(get(col)),
                mean      = mean(get(col)),
                Q3        = unname(quantile(get(col), probs = c(0.75))),
                Max       = max(get(col)),
                Sd        = sd(get(col)),
                Sk        = skewness(get(col)),
                Ck        = kurtosis(get(col))
            )

    } else {                                      

        # Contabiliza o número de registros, dentro de cada classe da variável.

        result <- data %>%
            group_by_at(col) %>%
            summarise(
                count = n(),
                prop  = n() / nrow(data) * 100
            )
    }

    # Retorna o resultado das estatísticas geradas.

    data.frame(result, row.names = col)
}

O coeficente de Assimetria (Skewness), indica como os dados estão distribuídos, e para interpretar seu resultado podemos olhar a tabela a seguir:

Índice de Assimetria Descrição
SK ≈ 0 Os dados são simétricos. Tanto a cauda do lado direito, quanto a do lado esquerdo da função densidade de probabilidade, são iguais;
SK < 0 A assimetria é negativa. A cauda do lado esquerdo da função densidade de probabilidade, é maior que a do lado direito e;
SK > 0 A assimetria é positiva. A cauda do lado direito da função densidade de probabilidade, é maior que a do lado esquerdo.

O coeficiente de Curtose (Kurtosis), é uma medida que caracteriza o achatamento da curva da função de distribuição, e para interpretar seu resultado, podemos olhar a tabela a seguir:

Índice de Curtose Descrição
CK ≈ 0 A distribuição é normal, e é chamada de Curtose Mesocúrtica;
CK < 0 A Cauda é mais leve que a normal. Para um coeficiente de Curtose negativo, tem-se uma Curtose Platicúrtica e;
CK > 0 A Cauda é mais pesada que a normal. Para um coeficiente de Curtose positivo, tem-se uma Curtose Leptocúrtica.

Atenção: Há diferentes fórmulas para calcular estes coeficientes. Mas, para este estudo utilizamos as funções fornecidas pela biblioteca e1071, com suas respectivas configurações padrão. Em caso de dúvida, consulte a documentação.

Também criaremos um função, para nos auxiliar na detecção de outliers.

In [ ]:
# Função para extrair os registros, que apresentem valores outliers, para um determinada variável.

getOutliers <- function(col, data) {

    # Extraindo variável, a ser manipulada, pelo nome.

    dt <- data %>% select_at(col)

    # Renomeando variável.

    colnames(dt) <- 'var'

    # Determinando o limite superior e inferior da variável especificada.

    limits <- dt %>% summarise(
        lowerOutliers = quantile(var, probs = c(.25)) - 1.5 * IQR(var),
        upperOutliers = quantile(var, probs = c(.75)) + 1.5 * IQR(var)
    )

    # Identificando registros, que estão fora dos limites especificados.

    data[dt$var < limits$lowerOutliers | dt$var > limits$upperOutliers, ]
}

As demais funções a seguir, foram criadas com o objetivo de abstrair a complexidade dos cálculos estatísticos, e aumentar a legibilidade das análises.

In [ ]:
# Função para calcular o aumento, ou a diminuição, percentual do valor da variável especificada, entre o primeiro e último
# trimestre registrados.

statsBetweenFirstAndLastQuarters <- function(col, data) {

    # Capturando os valores da coluna especificada, com o registro da data mais antiga, e da mais recente.

    minDate <- data[data$date == min(data$date), col]
    maxDate <- data[data$date == max(data$date), col]

    # Calculando o valor do ganho percentual, e a diferença entre os valores registrados.

    data.frame(
        percentIncrease = (maxDate - minDate) / minDate * 100,  # Define o valor percentual.
        amp             = maxDate - minDate                     # Define a amplitude entre os valores registrados.
    ) 
}
In [ ]:
# Função para calcular o aumento, ou a diminuição, percentual do valor da variável especificada, entre o primeiro e último
# trimestre de cada ano registrado.

statsBetweenFirstAndLastQuartersToYear <- function(col, data) {

    # Capturando os valores da coluna especificada, com o registro da data mais antiga, e da mais recente, para cada ano.

    dt <- data %>%
        select_at(c('year', 'month', col)) %>%
        group_by(year) %>%
        filter(month == 'March' | month == 'December')

    # Criando dataframe para armazenar as estatísticas.

    result <- data.frame()

    # Extraindo os anos limite, presentes no conjunto de dados.

    years <- unique(year(data$date))

    # Aplica o cálculo das estatísticas, para cada um dos anos presentes no dataset.

    for (y in years){
        
        # Extraí os dados do ano especificado.

        firstQuarter <- dt[dt$year == y & dt$month == 'March', col]
        lastQuarter  <- dt[dt$year == y & dt$month == 'December', col]

        # Calcula as estatísticas, e armazena os resultados, no dataframe.

        result <- rbind(
            result,
            data.frame(
                
                # Define o valor percentual.
                
                percentIncrease = (lastQuarter - firstQuarter) / firstQuarter * 100, 
                
                # Define a amplitude entre os valores registrados.
                
                amp             = lastQuarter - firstQuarter,                         
                row.names = as.character(y)
            )
        )
    }
    
    # Retorna os resultados obtidos pelo cálculo das estatísticas.

    result
}
In [ ]:
# Função para calcular o aumento, ou a diminuição, percentual dos trimestres, em relação aos anos.

statsBetweenQuartersPerYear <- function(col, data) {

    # Criando dataframe para armazenar as estatísticas.

    result <- data.frame()

    # Define o nome, e a sequência, com a qual os meses serão analisados.

    for(m in unique(months(data$date))) {

        # Capturando os valores, da coluna especificada, e o mês, a ser analisado.

        dt <- data %>%
            select_at(c('year', 'month', col)) %>%
            group_by(year) %>%
            filter(month == m)

        # Criando um dataframe, para armazenar as estatísticas geradas, para o mês em análise.

        rows <- data.frame()

        # Define um loop, para percorrer cada linha do dataframe.

        for(l in 2:nrow(dt)) {
            
            # Extraí os dados, do ano do registro selecionado, e do registro anterior.

            firstYear <- dt[dt$year == dt$year[l - 1], col]
            lastYear  <- dt[dt$year == dt$year[l], col]

            # Calcula as estatísticas, e armazena os resultados no dataframe.

            rows <- rbind(
                rows,
                data.frame(
                    
                    # Define o valor percentual.
                    
                    percentIncrease = (lastYear - firstYear) / firstYear * 100,                                 
                    row.names = paste(as.character(dt$year[l]), ' - ', as.character(dt$year[l - 1]), sep = '')
                )
            )
        }

        # Identifica o mês, ao qual, as estatíscas pertecencem.

        colnames(rows) <- paste('pInc', unique(dt$month), sep = '_')

        # Atribui os resultados gerados, ao dataframe de resultados.

        if(ncol(result) == 0) {
            
            # Caso o dataframe esteja vazio, o preenchemos com os resultados computados, para o primeiro mês analisado.
            
            result <- rows                
        } else {
            
            # Caso o dataframe não esteja vazio, adiconamos a coluna com as novas estatísticas, ao dataframe.
            
            result <- cbind(result, rows) 
        }
    }

    # Retorna os resultados gerados.

    result
}

5.1.3 Funções para aplicar testes estatísticos

5.1.3.1 Teste de normalidade (Teste de Shapiro-Wilks)

Muitos dos testes estatísticos, incluindo correlação, regressão, teste t e análise de variância (ANOVA), assumem certas características sobre os dados. Eles exigem que os dados sigam uma distribuição normal ou distribuição gaussiana. Esses testes são chamados de testes paramétricos, porque sua validade depende da distribuição dos dados.

Antes de usar um teste paramétrico, devemos executar alguns testes preliminares para garantir que as premissas do teste sejam atendidas. Nas situações em que as suposições são violadas, recomenda-se a realização de testes não paramatricos.

É possível usar um teste de significância comparando a distribuição da amostra com a normal, a fim de verificar se os dados mostram ou não um desvio grave da normalidade.

Existem vários métodos para o teste de normalidade, como o teste de normalidade Kolmogorov-Smirnov (KS) e o teste de Shapiro-Wilk.

O método de Shapiro-Wilk é amplamente recomendado para teste de normalidade e fornece melhor potência que o KS. É baseado na correlação entre os dados e as pontuações normais correspondentes, e é ele que utilizaremos em nossas análises.

As hipóteses deste teste são:

Teste de hipótese
H0: A amostra provêm de uma população normal.
Ha: A amostra não provêm de uma população normal.

Para saber mais sobre o teste de Shapiro-Wilk, consulte este link.

Caso deseje se aprofundar nos testes de normalidade, consulte este link.

In [ ]:
# Definindo uma função, para avaliar a normalidade, das variáveis numéricas do dataset.

normalityTest <- function(data, sig = 0.05) {

    # Determinando quais variáveis do dataset são do tipo numérico.

    numVars <- sapply(data, is.numeric)

    # Aplicando o teste de Shapiro-Wilks, a cada uma das variáveis numéricas.

    shapiroTest <- sapply(data[numVars], shapiro.test)['p.value', ]

    # Transpondo dados.

    normTest <- t(data.frame(shapiroTest))

    # Atribuindo o nome da coluna com os p-values.

    colnames(normTest) <- 'pValue'

    # Criando uma variável, para determinar se o p-value é menor do que o nível de significância especificado 
    # (por padrão, o nível de significância é de 5%).

    thereIsEvidenceOfNormality <- sapply(normTest, function(pvalue) { ifelse(pvalue > sig, TRUE, FALSE) })

    # Convertendo variáveis para um dataframe.

    normTest <- data.frame(normTest)

    # Retornando um dataframe com os resultados obtidos.

    cbind(normTest, thereIsEvidenceOfNormality)
}
5.1.3.2 Teste de Levene

O teste de Bartlett é usado para testar a homogeneidade das variâncias em k amostras, onde k pode ser maior que dois. É adaptado para dados normalmente distribuídos. O teste de Levene, é uma alternativa mais robusta ao teste de Bartlett quando as distribuições dos dados não são normais.

As hipóteses deste teste são:

Teste de hipótese
H0: As variâncias entre os grupos é homogênea.
Ha: As variâncias entre os grupos não é homogênea.

Para saber mais sobre testes estatísticos para comparar variâncias, consulte este link.

In [ ]:
# Definindo uma função, para avaliar a homogeneidade das variâncias dos subgrupos categorizados, 
# das variáveis numéricas do dataset.

normVarTest <- function(categ, data, sig = 0.05) {

    # Determinando quais variáveis do dataset, são do tipo numérico.

    numVars <- sapply(data, is.numeric)
    
    # Determinando a posição da variável target, dentro do dataset.

    isTarget <- sapply(colnames(data), function(n){n == categ})

    # Capturando variáveis que serão utilizadas no teste.

    data <- data[numVars | isTarget]

    # Definindo os nomes das variáveis target.

    target <- colnames(data) 

    # Elimina o nome da varíavel categórica da lista.

    target <- target[target != categ]

    # Aplica o teste de Levene, com as variáveis numérica e categórica especificadas.

    result <- sapply(target, function(t) {

        leveneTest(formula(paste(t, '~', categ)), data = data)[1, 'Pr(>F)']
    })
    
    # Transpondo dados.

    normTest <- data.frame(result)

    # Atribuindo o nome da coluna com os p-values.

    colnames(normTest) <- 'pValue'

    # Criando uma variável, para determinar se o p-value é menor do que o nível de significância especificado 
    # (por padrão, o nível de significância é de 5%).

    thereIsEvidenceOfNormVar <- sapply(normTest, function(pvalue) { ifelse(pvalue > sig, TRUE, FALSE) })

    # Renomeando o nome da variável especificada.

    colnames(thereIsEvidenceOfNormVar) <- 'thereIsEvidenceOfNormVar'
    
    # Convertendo as variáveis para um dataframe.

    normTest <- data.frame(normTest)

    # Retornando um dataframe com os resultados obtidos.

    cbind(normTest, thereIsEvidenceOfNormVar)
}
5.1.3.3 Teste Anova (One-Way)

A análise de variância One-Way (ANOVA), também conhecido como one-factor ANOVA, é uma extensão do teste t para duas amostras independentes, e visa comparar a média em uma situação em que há mais do que dois grupos. Na ANOVA One-Way, os dados são organizados em vários grupos, com base em uma única variável de agrupamento (também chamada de variável fator).

Observe que, se você tiver apenas dois grupos, poderá usar o teste t. Nesse caso, o teste F e o teste t são equivalentes.

O teste ANOVA só pode ser aplicado quando:

  1. As observações são obtidas de forma independente e aleatória a partir da população definida pelos níveis de fator;

  2. Os dados de cada nível de fator são normalmente distribuídos;

  3. Essas populações normais têm uma variância comum (O teste de Levene pode ser usado para verificar isso) e;

  4. Os dados devem ser contínuos.

As hipóteses deste teste são:

Teste de hipótese
H0: As médias dos grupos são iguais.
Ha: As médias dos grupos não são iguais.

Para saber mais sobre testes estatísticos para comparar variâncias, consulte este link.

In [ ]:
# Definindo uma função, para aplicar a análise de variância nos subgrupos categorizados, das variáveis numéricas do dataset.

anovaTest <- function(categ, parametricVars, data, sig = 0.05) {

    # Aplicando o teste Anova, com as variáveis numérica e categórica especificadas.

    result <- sapply(parametricVars, function(t) {

        anovaModel <- lm(formula(paste(t, '~', categ)), data = data)
        
        anova(anovaModel)[categ, 'Pr(>F)']
    })

    # Transpondo dados.

    anovaTest <- data.frame(result)

    # Atribuindo o nome da coluna com os p-values.

    colnames(anovaTest) <- 'pValue'
    
    # Criando uma variável, para determinar se o p-value é menor do que o nível de significância especificado 
    # (por padrão, o nível de significância é de 5%).

    thereIsEvidenceForEqualsMeans <- sapply(anovaTest, function(pvalue) { ifelse(pvalue > sig, TRUE, FALSE) })

    # Renomeando o nome da variável especificada.

    colnames(thereIsEvidenceForEqualsMeans) <- 'thereIsEvidenceForEqualsMeans'
    
    # Convertendo variáveis para um dataframe.

    anovaTest <- data.frame(anovaTest)

    # Retornando um dataframe com os resultados obtidos.

    cbind(anovaTest, thereIsEvidenceForEqualsMeans)
}
5.1.3.4 Teste de soma de classificação de Kruskal-Wallis

O teste de Kruskal-Wallis por classificação é uma alternativa não-paramétrica ao teste ANOVA One-Way, que estende o teste de Wilcoxon de duas amostras na situação em que existem mais de dois grupos. É recomendado quando as suposições do teste ANOVA One-Way não são atendidas.

As hipóteses deste teste são:

Teste de hipótese
H0: As médias dos grupos são iguais.
Ha: As médias dos grupos não são iguais.

Para saber mais sobre testes estatísticos para comparar variâncias, consulte este link.

In [ ]:
# Definindo uma função, para aplicar a análise de variância nos subgrupos categorizados, das variáveis numéricas não 
# paramétricas do dataset.

kruskalTest <- function(categ, nonParametricVars, data, sig = 0.05) {

    # Aplicando o teste de Kruskal-Wallis, com as variáveis numérica e categórica especificadas.

    result <- sapply(nonParametricVars, function(t) {

        kruskal.test(formula(paste(t, '~', categ)), data = data)$p.value
    })

    # Transpondo dados.

    krTest <- data.frame(result)

    # Atribuindo o nome da coluna com os p-values.

    colnames(krTest) <- 'pValue'
    
    # Criando um variável, para determinar se o p-value é menor do que o nível de significância especificado 
    # (por padrão, o nível de significância é de 5%).

    thereIsEvidenceForEqualsMeans <- sapply(krTest, function(pvalue) { ifelse(pvalue > sig, TRUE, FALSE) })

    # Renomeando o nome da variável especificada.

    colnames(thereIsEvidenceForEqualsMeans) <- 'thereIsEvidenceForEqualsMeans'
    
    # Convertendo variáveis para um dataframe.

    krTest <- data.frame(krTest)

    # Retornando um dataframe com os resultados obtidos.

    cbind(krTest, thereIsEvidenceForEqualsMeans)
}

5.2 Visão geral dos dados

Nesta etapa, buscaremos entender a disposição e as características das nossas variáveis, além de extrair insigths para enriquecer nossa análise.

Após todas as alterações que fizemos, a configuração das variavéis no dataset passou a ser a seguinte:

Variável Tipo Descrição
date Date É a data de emissão do resultado trimestral;
totalSubscriptions integer É o número total de assinaturas registradas no final do trimestre;
paidSubscriptions integer É o número de assinaturas pagas registradas no final do trimestre;
freeTrails integer É o número de assinaturas em período de utilização gratuita registradas no final do trimestre;
revenue Double É a receita trimestral gerada para o Streaming doméstico (em milhares de dólares);
costOfRevenues Double É o custo trimestral da receita gerada para o Streaming doméstico (em milhares de dólares);
marketing Double É o custo trimestral de Marketing gerado para o Streaming doméstico (em milhares de dólares);
contributionProfit Double É o lucro da contribuição trimestral (em milhares de dólares);
contributionMargin Double É a margem de contribuição trimestral (contributionProfit / revenue);
costPerCustomer Double É a taxa de custo por cliente desconsiderando os gastos com Marketing (costOfRevenues / totalSubscriptions);
revenuePerCustomer Double É a taxa de receita por cliente (revenue / totalSubscriptions);
earningsPerCustomer Double É a taxa de lucro por cliente ((revenue - costOfRevenues) / totalSubscriptions);
segment Factor É o segmento de operação da companhia ao qual os dados se referem;
month Factor É o mês de emissão do resultado trimestral;
year Factor É o ano de emissão do resultado trimestral;
freeTrialsFromTotal Double É a proporção de avaliações gratuitas do total de assinaturas (freeTrails / totalSubscriptions * 100) e;
marketingFromTotal Double É a proporção de custo com marketing em relação ao custo total (marketing / (costOfRevenues + marketing) * 100).

Podemos verificar as informações desta tabela, com o auxílio da função glimpse.

In [ ]:
# Verificando os tipos das colunas carregadas do dataset.

glimpse(data)
Rows: 28
Columns: 17
$ date                <date> 2012-03-31, 2012-06-30, 2012-09-30, 2012-12-31, …
$ totalSubscriptions  <int> 23410, 23938, 25101, 27146, 29174, 29807, 31092, …
$ paidSubscriptions   <int> 22022, 22686, 23801, 25471, 27913, 28624, 29925, …
$ freeTrails          <int> 1388, 1252, 1300, 1675, 1261, 1183, 1167, 1708, 1…
$ revenue             <dbl> 506665, 532705, 556027, 589471, 638649, 671089, 7…
$ costOfRevenues      <dbl> 360776, 378574, 399124, 420390, 440334, 452598, 4…
$ marketing           <dbl> 79381, 70959, 65955, 59777, 66965, 67177, 60637, …
$ contributionProfit  <dbl> 66508, 83172, 90948, 109304, 131350, 151314, 1664…
$ contributionMargin  <dbl> 0.131, 0.156, 0.164, 0.185, 0.206, 0.225, 0.237, …
$ costPerCustomer     <dbl> 15.41119, 15.81477, 15.90072, 15.48626, 15.09337,…
$ revenuePerCustomer  <dbl> 21.64310, 22.25353, 22.15159, 21.71484, 21.89103,…
$ earningsPerCustomer <dbl> 6.231909, 6.438758, 6.250866, 6.228579, 6.797662,…
$ segment             <fct> Streaming, Streaming, Streaming, Streaming, Strea…
$ month               <fct> March, June, September, December, March, June, Se…
$ year                <fct> 2012, 2012, 2012, 2012, 2013, 2013, 2013, 2013, 2…
$ freeTrialsFromTotal <dbl> 5.929090, 5.230178, 5.179077, 6.170338, 4.322342,…
$ marketingFromTotal  <dbl> 18.034701, 15.785048, 14.181462, 12.449210, 13.20…

Verificamos a existência de 17 variáveis e 28 observações dentro do dataset.

5.3 Explorando a distribuição de cada variável individualmente

Nesta etapa, iremos analisar as informações que conseguimos extrair de cada variável do dataset individualmente.

5.3.1 Variável date

Bom, vamos começar verificando a data da primeira e da última observação registrada.

In [ ]:
# Verificando a data da primeira observação registrada.

min(data$date)
In [ ]:
# Verificando a data da última observação registrada.
 
max(data$date)

Como esperávamos, o intervalo de tempo se inicia no fim do primeiro trimestre de 2012, e finda no último trimestre de 2018.

Avaliaremos os dias que estão associados a cada observação.

In [ ]:
# Contabilizando os dias em que cada registro foi emitido.

table(day(data$date))
30 31 
14 14 

Vemos que as informações trimestrais, sempre são emitidas no último dia do mês (no dia 30 ou 31).

Nosso próximo passo, é garantir que temos os dados, de cada um dos trimestres dentro deste intervalo de tempo.

In [ ]:
# Contabilizando o número de registros por ano.

table(year(data$date))
2012 2013 2014 2015 2016 2017 2018 
   4    4    4    4    4    4    4 

Para cada ano temos 4 registros, um para cada trimestre.

In [ ]:
# Contabilizando o número de registros por mês.

table(months(data$date))
 December      June     March September 
        7         7         7         7 

Para cada um dos meses trimestrais temos 7 registros, um para cada ano.

In [ ]:
# Contabilizando o número de registros por ano e por mês.

data %>%
    group_by(year(date), months(date)) %>%
    nrow()
28

Contabilizamos que, cada um dos 7 anos presentes no dataset, apresenta informações de cada um dos seus 4 meses trimestrais, totalizando 28 registros distintos.

Portanto, possuímos informações dos 4 trimestres entre os anos de 2012 a 2018.

5.3.2 Variável totalSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'totalSubscriptions'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro do total de assinaturas que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à esquerda para o conjunto de dados, ou seja, os valores inferiores à mediana variam mais do que os superiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<int><dbl><dbl><dbl><dbl><int><dbl><dbl><dbl>
totalSubscriptions234103283842740.541407.11496125848610649.09-0.1514012-1.24513

Destacamos que:

  • A média e a mediana do total de assinaturas apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à esquerda dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.3 Variável paidSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'paidSubscriptions'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro do total de assinaturas pagas que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à esquerda para o conjunto de dados, ou seja, os valores inferiores à mediana variam mais do que os superiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<int><dbl><dbl><dbl><dbl><int><dbl><dbl><dbl>
paidSubscriptions2202231265.2541562.540031.7148103.255642110549.02-0.1846556-1.247453

Destacamos que:

  • A média e a mediana do total de assinaturas pagas apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à esquerda dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

Observação: As estatísticas das variáveis totalSubscriptions e paidSubscriptions são muito semelhantes. Isto pode se dar ao fato de uma ser um subconjunto da outra, visto que para pagar a assinatura o cliente deve ser um assinante. Pode ser interessante verificar, se as diferenças entre estas variáveis são estatisticamente significativas, e faremos isso nas próximas etapas.

5.3.4 Variável freeTrails

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrails'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, há um registro do número de assinantes em período de utilização gratuita discrepante dentro do conjunto de dados. Vemos que o outlier está localizado no limite superior do gráfico, ou seja, há um registro em que o aumento das assinaturas em período de utilização experimental fugiu do padrão, indicando que nesta época o número de clientes interessados em conhecer os serviços da Netflix aumentou consideravelmente.

Vamos identificar qual é este registro!

In [ ]:
# Extraindo registros, em que o valor da variável especificada, tenha sido considerado um outlier.

getOutliers(col = col, data = data)
A tibble: 1 × 17
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyearfreeTrialsFromTotalmarketingFromTotal
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct><dbl><dbl>
2018-12-3158486564212065199609210934463127395899070.29618.6958634.129415.43354StreamingDecember20183.53075922.24025

Interessante! O quarto trimestre de 2018 apresentou o valor destoante, e pode estar indicando que o número de assinantes em período de utilização gratuita, pode vir a crescer significativamente no próximo ano.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<int><dbl><dbl><dbl><dbl><int><dbl><dbl><dbl>
freeTrails95411791318.51375.3931511.752065264.20110.75476650.1175865

Destacamos que:

  • A média e a mediana do número de assinantes em período de utilização gratuita apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais pesada do que a normal, ou seja, temos uma curtose leptocúrtica.

5.3.5 Variável revenue

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenue'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro de receita que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
revenue506665730686.210449371122321147890619960924613860.4184065-1.126538

Destacamos que:

  • A média e a mediana da receita apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.6 Variável costOfRevenues

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costOfRevenues'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro do custo de receita que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
costOfRevenues360776490850.5628802.5663300.78085811093446212688.20.3793461-1.101774

Destacamos que:

  • A média e a mediana da receita apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.7 Variável marketing

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketing'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, há registros sobre o gasto com marketing discrepantes dentro do conjunto de dados. Os outliers estão localizados no limite superior do gráfico, ou seja, há registros em que o custo com marketing fugiu do padrão, indicando que nestas épocas as despesas aumentaram consideravelmente.

Vamos identificar quais são estes registros.

In [ ]:
# Extraindo registros, em que o valor da variável especificada, tenha sido considerado um outlier.

getOutliers(col = col, data = data)
A tibble: 5 × 17
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyearfreeTrialsFromTotalmarketingFromTotal
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct><dbl><dbl>
2017-12-31528105087019401630274 9161002110575031170.30917.3470930.8705513.52346StreamingDecember 20173.67354718.72472
2018-03-31550875346916181820019 9364802507196328200.34817.0000233.0389916.03897StreamingMarch 20182.93717221.11853
2018-06-30559595453914201893222 9699952512986719290.35517.3340333.8323116.49828StreamingJune 20182.53757220.57639
2018-09-3056957554501507193731410384732105956882460.35518.2325834.0136215.78105StreamingSeptember20182.64585616.86017
2018-12-3158486564212065199609210934463127395899070.29618.6958634.1294015.43354StreamingDecember 20183.53075922.24025

Constatamos que a partir do último trimestre de 2017, os gastos com marketing aumentaram consideravelmente, em relação aos demais trimestres observados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
marketing597776963483840.5114086.7125240.531273968678.391.4812871.012792

Destacamos que:

  • A média e a mediana dos gastos com marketing apresentam valores muito distantes;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais pesada do que a normal, ou seja, temos uma curtose leptocúrtica.

5.3.8 Variável contributionProfit

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionProfit'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro do lucro da contribuição trimestral que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
contributionProfit66508171836.8342003.5344933.8503188.2688246191727.80.1952984-1.289478

Destacamos que:

  • A média e a mediana do lucro da contribuição trimestral apresentam valores próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.9 Variável contributionMargin

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionMargin'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro da margem de contribuição trimestral que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à esquerda para o conjunto de dados, ou seja, os valores inferiores à mediana variam mais do que os superiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
contributionMargin0.1310.236250.3130.28557140.33850.3810.06902572-0.7246015-0.7118044

Destacamos que:

  • A média e a mediana da margem de contribuição apresentam valores muito distantes;
  • O coeficiente de assimetria (Sk) confirma a assimetria à esquerda dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.10 Variável costPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costPerCustomer'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro da taxa de custo por cliente que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
costPerCustomer14.0717715.0173515.4487315.8245616.5962818.695861.222350.7195072-0.570221

Destacamos que:

  • A média e a mediana da taxa de custo por cliente apresentam valores muito próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.11 Variável revenuePerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenuePerCustomer'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro da taxa de receita por cliente que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
revenuePerCustomer21.643122.4824924.4464126.1265429.8089334.12944.276260.655355-1.117468

Destacamos que:

  • A média e a mediana da taxa de receita por cliente apresentam valores muito próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.12 Variável earningsPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'earningsPerCustomer'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, não há registro da taxa de ganhos por cliente que seja discrepante dentro do conjunto de dados.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
earningsPerCustomer6.2285797.323829.73988110.3019812.7019816.498283.290370.4343922-1.158638

Destacamos que:

  • A média e a mediana da taxa de ganhos por cliente apresentam valores muito próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.13 Variável segment

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'segment'

# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
`summarise()` ungrouping output (override with `.groups` argument)

A data.frame: 1 × 3
segmentcountprop
<fct><int><dbl>
segmentStreaming28100

Detectamos que não há variação alguma entre os valores da variável segment, ou seja, só há uma classe associada a todos os registro do dataset.

Ao analisarmos a documentação, observamos que a Netflix opera em três segmentos: International Streaming, Domestic Streaming e Domestic DVD. A categoria na qual os dados estão classificados é denomidada Streaming, e ao compará-los com os dados fornecidos pela Netflix Investor, identificamos que o segmento do qual as informações foram extraídas foi o doméstico.

5.3.14 Variável freeTrialsFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrialsFromTotal'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, há registros sobre a proporção de avaliações gratuitas do total de assinaturas discrepantes dentro do conjunto de dados. Os outliers estão localizados no limite superior do gráfico, ou seja, há registros em que a proporção fugiu do padrão, indicando que nestas épocas as avaliações em período experimental aumentaram consideravelmente.

Vamos identificar quais são estes registros!

In [ ]:
# Extraindo registros em que o valor da variável especificada tenha sido considerado um outlier.

getOutliers(col = col, data = data)
A tibble: 2 × 17
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyearfreeTrialsFromTotalmarketingFromTotal
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct><dbl><dbl>
2012-03-312341022022138850666536077679381 665080.13115.4111921.643106.231909StreamingMarch 20125.92909018.03470
2012-12-3127146254711675589471420390597771093040.18515.4862621.714846.228579StreamingDecember20126.17033812.44921

Interessante! Pelas análises que fizemos até aqui, não há nenhuma explicação para que o primeiro e quarto trimestres de 2012 apresentem tais valores discrepantes. Mas, certamente deve haver um motivo para isso, não é mesmo?

Quando pesquisamos sobre o que ocorreu com a Netflix em 2012, constatamos que neste ano a empresa passou a assumir um papel ativo como produtora, e distribuidora de filmes e séries de televisão, e para esse fim, passou a oferecer uma variedade de conteúdo Original por meio de sua biblioteca on-line.

Dito isto, podemos supor que uma explicação plausível, para estas proporções de avaliações gratuitas em relação ao total de assinaturas registradas terem apresentado valores discrepantes, pode ter sido causada pela mudança do paradigma operacional, que a empresa adotou quando passou a criar produtos autorais.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
freeTrialsFromTotal2.1902362.717183.1804793.5227253.8072496.1703381.0884081.030699-0.07389296

Destacamos que:

  • A média e a mediana da proporção de avaliações gratuitas do total de assinaturas apresentam valores muito próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais leve do que a normal, ou seja, temos uma curtose platicúrtica.

5.3.15 Variável marketingFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketingFromTotal'

# Criando um gráfico Boxplot para a variável especificada.

boxPlot(col = col, data = data)

O boxplot nos indica que, há registros sobre a proporção de custo com marketing em relação ao total de custos discrepantes dentro do conjunto de dados. Vemos que os outliers estão localizados no limite superior do gráfico, ou seja, há registros em que a proporção fugiu do padrão indicando que nestas épocas as proporções de gastos com marketing foram consideravelmente mais altas.

Vamos identificar quais são estes registros.

In [ ]:
# Extraindo registros em que o valor da variável especificada tenha sido considerado um outlier.

getOutliers(col = col, data = data)
A tibble: 3 × 17
datetotalSubscriptionspaidSubscriptionsfreeTrailsrevenuecostOfRevenuesmarketingcontributionProfitcontributionMargincostPerCustomerrevenuePerCustomerearningsPerCustomersegmentmonthyearfreeTrialsFromTotalmarketingFromTotal
<date><int><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><fct><fct><fct><dbl><dbl>
2018-03-31550875346916181820019 9364802507196328200.34817.0000233.0389916.03897StreamingMarch 20182.93717221.11853
2018-06-30559595453914201893222 9699952512986719290.35517.3340333.8323116.49828StreamingJune 20182.53757220.57639
2018-12-3158486564212065199609210934463127395899070.29618.6958634.1294015.43354StreamingDecember20183.53075922.24025

Verificamos que a partir do primeiro trimestre de 2018, a proporção de gastos com marketing passou a fugir do padrão, o que pode estar indicando uma nova tendência para estas despesas nos próximos anos.

In [ ]:
# Criando um gráfico de Densidade para a variável especificada.

densityPlot(col = col, data = data)

O gráfico de densidade evidência uma assimetria à direita para o conjunto de dados, ou seja, os valores superiores à mediana variam mais do que os inferiores.

In [ ]:
# Calculando algumas estatísticas para a variável especificada.

varStats(col, data = data)
A data.frame: 1 × 9
MinQ1MedianmeanQ3MaxSdSkCk
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
marketingFromTotal9.74698911.4043213.2169313.9724214.5823622.240253.3744851.0538260.03189463

Destacamos que:

  • A média e a mediana das proporção de custo com marketing em relação ao total de custos apresentam valores muito próximos;
  • O coeficiente de assimetria (Sk) confirma a assimetria à direita dos dados e;
  • O coeficiente de curtose (Ck) evidência que a calda é mais pesada do que a normal, ou seja, temos uma curtose leptocúrtica.

5.4 Explorando a distribuição de cada variável a partir dos resultados trimestrais e anuais

Nesta etapa, iremos analisar as informações que conseguimos extrair de cada variável agrupada por trimestre e ano.

5.4.1 Variável totalSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'totalSubscriptions'

# Criando um gráfico de barras para a variável especificada.

barPlot(col = col, data = data)

Parece haver um crescimento aproximadamente linear do total de assinantes entre cada um dos trimestres de 2012 a 2018.

Vamos verificar de quanto foi o aumento, do número total de indivíduos cadastrados, entre o primeiro e o último trimestres analisados.

In [ ]:
# Calculando a diferença absoluta, e percentual dos valores da variável especificada, entre o primeiro e último trimestre 
# analisados.

statsBetweenFirstAndLastQuarters(col = col, data = data)
A data.frame: 1 × 2
totalSubscriptionstotalSubscriptions.1
<dbl><int>
149.833435076

Constatamos que o crescimento total entre 2012 e 2018 foi de 35.076 assinantes, o que corresponde a um aumento de aproximadamente 150%.

Iremos repetir este mesmo processo, mas agora, para avaliar o aumento do número total de indivíduos cadastrados entre o primeiro e o último trimestre de cada ano.

In [ ]:
# Calculando a diferença absoluta e percentual, dos valores da variável especificada, entre o primeiro e último trimestre 
# de cada ano analisado.

statsBetweenFirstAndLastQuartersToYear(col = col, data = data)
A data.frame: 7 × 2
totalSubscriptionstotalSubscriptions.1
<dbl><int>
201215.9589923736
201314.5540554246
2014 9.6428773440
2015 8.0706333341
2016 4.7928422191
2017 6.9569623435
2018 6.1702403399

Concluímos que 2012 e 2013 foram os anos que apresentaram o maior aumento no número total de assinantes, e isto pode estar associado com o empenho da empresa em expandir seu mercado streaming. Os anos subsequentes apresentam uma queda nesta porcentagem, com destaque para o ano de 2016, que apresentou a pior taxa de crescimento registrada entre os anos analisados.

Ao buscar possíveis motivos para os resultados obtidos em 2016, vemos na timeline dos acontecimentos da empresa, o surgimento de acusações sobre sua conduta acerca da neutralidade na rede. Podemos supor, que o desgaste gerado a imagem da organização com estes debates, contribuiu para o baixo crescimento no ano.

Vamos avaliar, o aumento do número total de indivíduos cadastrados, entre os trimestres dos anos.

In [ ]:
# Calculando a diferença percentual, dos valores da variável especificada, entre os trimestres dos anos.

statsBetweenQuartersPerYear(col = col, data = data)
A data.frame: 6 × 4
pInc_MarchpInc_JunepInc_SeptemberpInc_December
<dbl><dbl><dbl><dbl>
2013 - 201224.62195624.51750423.86757523.112061
2014 - 201322.28011221.59559819.70603417.037702
2015 - 201416.04249616.70897316.01870014.378483
2016 - 201510.428292 8.756501 7.637618 7.078993
2017 - 2016 8.008488 9.38831410.46924410.239015
2018 - 201711.56860811.19965010.92998310.747964

Bom, observamos que as menores porcentagens de crescimento dos trimestres ao longo dos anos, estão entre os anos de 2015 e 2017.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a quantidade total de assinantes aumenta a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para o número total de assinantes, entre os trimestres dos anos.

5.4.2 Variável paidSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'paidSubscriptions'

# Criando um gráfico de barras para a variável especificada.

barPlot(col = col, data = data)

Torna-se evidente que a variável paidSubscriptions, representa um subconjunto da variável totalSubscriptions quando comparamos seus gráficos de barras trimestrais. Sua tendência de crescimento, parece seguir a da variável totalSubscriptions.

In [ ]:
# Calculando a diferença absoluta, e percentual dos valores da variável especificada, entre o primeiro e último trimestre 
# analisados.

statsBetweenFirstAndLastQuarters(col = col, data = data)
A data.frame: 1 × 2
paidSubscriptionspaidSubscriptions.1
<dbl><int>
156.202934399

Constatamos que o crescimento total entre 2012 e 2018 foi de 34.399 assinaturas pagas, o que corresponde a um aumento de aproximadamente 156.2%.

Iremos repetir este mesmo processo, mas agora, para avaliar o aumento do número total de indivíduos cadastrados entre o primeiro e o último trimestre de cada ano.

In [ ]:
# Calculando a diferença absoluta e percentual, dos valores da variável especificada, entre o primeiro e último trimestre 
# de cada ano analisado.

statsBetweenFirstAndLastQuartersToYear(col = col, data = data)
A data.frame: 7 × 2
paidSubscriptionspaidSubscriptions.1
<dbl><int>
201215.6616113449
201313.6101463799
2014 9.6605293321
2015 7.6547193086
2016 4.3138931918
2017 6.2092872974
2018 5.5209562952

Concluímos que 2012 e 2013 foram os anos que apresentaram o maior aumento em assinaturas pagas, e isto pode estar associada com o empenho da empresa em expandir seu mercado streaming. Os anos subsequentes, apresentam uma queda nesta porcentagem, com destaque para o ano de 2018, que apresentou a pior taxa de crescimento registrada entre os anos analisados.

In [ ]:
# Calculando a diferença percentual, dos valores da variável especificada, entre os trimestres dos anos.

statsBetweenQuartersPerYear(col = col, data = data)
A data.frame: 6 × 4
pInc_MarchpInc_JunepInc_SeptemberpInc_December
<dbl><dbl><dbl><dbl>
2013 - 201226.75052226.17473325.73001124.502375
2014 - 201323.15766822.57196821.18629918.876135
2015 - 201417.27317717.02151916.00165415.128124
2016 - 201510.284013 9.309009 8.065513 6.861593
2017 - 2016 7.725872 8.569710 9.804008 9.683262
2018 - 201711.63562711.93227311.08217510.912129

Bom, observamos que as menores porcentagens de crescimento dos trimestres ao longo dos anos, estão entre os anos de 2015 e 2017.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a quantidade total de assinaturas pagas aumenta a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver uma diferença significativa, para o número de assinaturas pagas, entre os trimestres dos anos.

5.4.3 Variável freeTrails

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrails'

# Criando um gráfico de barras para a variável especificada.

barPlot(col = col, data = data)

O gráfico nos mostra, que o último trimestre de cada ano analisado, foi o que apresentou o maior número de assinantes em período de utilização gratuita. Essa tendência parece se propagar, na maior parte dos casos, para o primeiro trimestre do ano seguinte. O segundo e o terceiro trimestre, são aqueles que predominantemente apresentam os menores números de assinantes.

Vamos garantir que nossos olhos não estão nos enganando, e que o último trimestre de cada ano realmente apresenta o maior valor para esta variável.

In [ ]:
# Capturando o trimestre que apresentou o maior valor para a variável especificada em cada ano.

data %>%
    select(year, month, freeTrails) %>%
    group_by(year) %>%
    filter(freeTrails == max(freeTrails))
A grouped_df: 7 × 3
yearmonthfreeTrails
<fct><fct><int>
2012December1675
2013December1708
2014December1416
2015December1337
2016December1526
2017December1940
2018December2065

Excelente! Para cada um dos anos analisados, o último trimestre sempre apresentou o maior número de assinantes em período de utilização gratuita.

Muito bem, agora iremos verificar o trimestre que apresentou o menor número de cadastros em período experimental em cada ano.

In [ ]:
# Capturando o trimestre que apresentou o menor valor para a variável especificada em cada ano.

dt <- data %>%
    select(year, month, freeTrails) %>%
    group_by(year) %>%
    filter(freeTrails == min(freeTrails))

# Visualizando o resultado.

dt
A grouped_df: 7 × 3
yearmonthfreeTrails
<fct><fct><int>
2012June 1252
2013September1167
2014September 954
2015March 1082
2016September1018
2017September1427
2018June 1420

Contar o número de vezes que cada mês aparece dentro desta tabela, também parece interessante!

In [ ]:
# Calculando a frequência absoluta e relativa, para a número de ocorrências de cada trimestre observado.

dt[, 'month'] %>%
    group_by(month) %>%
    summarise (
        freq       = n(),
        percentage = n() / nrow(dt) * 100 
    ) %>%
    arrange(desc(freq))
`summarise()` ungrouping output (override with `.groups` argument)

A tibble: 3 × 3
monthfreqpercentage
<fct><int><dbl>
September457.14286
June 228.57143
March 114.28571

Vemos que o terceiro trimestre do ano, é o que apresenta o menor número de assinantes no período experimental, em 57.14% dos casos observados, o segundo trimestre em 28.57%, e o primeiro trimestre em 14.29%.

Para finalizar, criaremos um gráfico de boxplot que agrupa os valores da variável freeTrails pelo ano ao qual pertence.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Veja só, de 2012 até 2015 observamos um tendência de queda, e a partir de 2016 vemos um tendência de crescimento, que se destaca ainda mais a partir de 2017. Em 2013, o valor do quarto trimestre foi tão alto a ponto de ser representado como um outlier.

Uma estratégia interessante, que a Netflix poderia adotar para aumentar o número e a fidelização dos clientes que experimentam sua plataforma, seria a de focar no quarto trimestre do ano; diminuindo os preços dos seus serviços, aumentando seu marketing e o período de acesso gratuito, além de usar este trimestre para realizar o lançamento de novos conteúdos.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece haver uma diferença significativa, para o número de assinaturas em período de utilização gratuita, entre os trimestres dos anos. O primeiro e o quarto trimestre, são aqueles que apresentam os maiores valores.

5.4.4 Variável revenue

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenue'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente linear, da receita trimestral gerada entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a receita gerada aumenta a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para a receita gerada, entre os trimestres dos anos.

5.4.5 Variável costOfRevenues

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costOfRevenues'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente linear, do custo trimestral da receita gerada entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, o custo da receita gerada aumenta a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para o custo da receita gerada, entre os trimestres dos anos.

5.4.6 Variável marketing

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketing'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente exponencial, do custo de marketing entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, o custo de marketing aumenta expencialmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença mediana significativa, para o custo de marketing, entre os trimestres dos anos. Também notamos uma alta variância no terceiro e quarto trimestre.

5.4.7 Variável contributionProfit

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionProfit'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente linear, do lucro da contribuição entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, o lucro da contribuição aumenta linearmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para o lucro da contribuição, entre os trimestres dos anos.

5.4.8 Variável contributionMargin

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionMargin'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente logístico, da margem de contribuição entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a margem de contribuição aumenta logisticamente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para a margem de contribuição, entre os trimestres dos anos.

5.4.9 Variável costPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costPerCustomer'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente exponencial, do custo por cliente entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, o custo por cliente aumenta exponencialmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece haver uma diferença significativa, para o custo por cliente, entre os trimestres dos anos. O primeiro trimestre, apresenta um valor mediano menor do que os demais.

5.4.10 Variável revenuePerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenuePerCustomer'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente exponencial, da taxa de receita por cliente entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a taxa de receita por cliente aumenta exponencialmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para a taxa de receita por cliente, entre os trimestres dos anos.

5.4.11 Variável earningsPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'earningsPerCustomer'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente linear, da taxa de lucro por cliente entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a taxa de lucro por cliente aumenta linearmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para a taxa de lucro por cliente, entre os trimestres dos anos.

5.4.12 Variável freeTrialsFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrialsFromTotal'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um decrescimento aproximadamente exponecial, da proporção de avaliações gratuitas do total de assinaturas entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'year')

Como vimos anteriormente, a proporção de avaliações gratuitas do total de assinaturas diminui exponecialmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece haver uma diferença significativa, para a proporção de avaliações gratuitas do total de assinaturas, entre os trimestres dos anos. O terceiro trimestre, apresenta um valor mediano menor do que os demais.

5.4.13 Variável marketingFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketingFromTotal'

# Criando um gráfico de série temporal para a variável especificada.

timeSeriesPlot(col = col, data = data)

A série temporal demonstra, um crescimento aproximadamente exponencial, da proporção de custo com marketing em relação ao custo total entre os anos em análise.

In [ ]:
# Criando um gráfico Boxplot que agrupe a variável especificada pelo ano ao qual seu registro está associada.

boxPlot(col = col, data = data %>% select_at(c('year', col)), categ = 'year')

Como vimos anteriormente, a proporção de custo com marketing em relação ao custo total aumenta exponencialmente a cada ano.

In [ ]:
# Criando um gráfico Boxplot, que agrupe a variável especificada, pelo trimestre ao qual seu registro está associada.

boxPlot(col = col, data = data, categ = 'month')

Parece não haver um diferença significativa, para a proporção de custo com marketing em relação ao custo total, entre os trimestres dos anos.

5.5 Aplicando teste Anova One-Way as variáveis do dataset

Para aplicarmos o teste Anova One-Way, os 4 requisitos a seguir devem ser cumpridos:

  1. As observações são obtidas de forma independente e aleatória a partir da população definida pelos níveis de fator;

  2. Os dados de cada nível de fator são normalmente distribuídos;

  3. Essas populações normais têm uma variância comum e;

  4. Os dados devem ser contínuos.

As variáveis que passarem com êxito por as etapas, serão classificadas como variáveis paramétricas, e poderão ser analisadas pelo teste ANOVA One-Way.

As variáveis que descumprirem algum destes pontos, serão classificadas como variáveis não paramétricas, e poderão ser analisadas pelo teste de Kruskal-Wallis.

In [ ]:
# Aplicando o teste de Shapiro-Wilks, para avaliar a normalidade das variáveis.

nmT <- normalityTest(data)

# Verificando quais variáveis são continuas.

isContinuous <- !sapply(data, is.integer)

Iremos utilizar o ano, para categorizar as variáveis que serão analisadas.

In [ ]:
# Definindo a variável categórica que será utilizada nos testes.

categ <- 'year'

# Aplicando o teste de Levene, para verificar se a variância entre os grupos é homogênea.

nmVarT <- normVarTest(categ = categ, data)

# Determinando quais variáveis são paramétricas.

varsForParametricTests <- rownames(nmT)[nmT$thereIsEvidenceOfNormality & nmVarT$thereIsEvidenceOfNormVar]

# Determinando quais variáveis são não paramétricas.

varsForNonParametricTests <- rownames(nmT)[!(nmT$thereIsEvidenceOfNormality & nmVarT$thereIsEvidenceOfNormVar)]

# Determinando quais variáveis são contínuas.

continuousCols <- varsForParametricTests %in% colnames(data[isContinuous])

# Definindo variáveis que devem passar por testes não paramétricos.

varsForNonParametricTests <- append(varsForNonParametricTests, varsForParametricTests[!continuousCols])

# Definindo variáveis que devem passar por testes paramétricos.

varsForParametricTests <- varsForParametricTests[continuousCols]
In [ ]:
# Aplicando o teste ANOVA One-Way, para as variáveis paramétricas, segundo a variável categórica especificada.

anovaTest(categ = categ, parametricVars = varsForParametricTests, data = data)
A data.frame: 3 × 2
pValuethereIsEvidenceForEqualsMeans
<dbl><lgl>
revenue0.000000000000000008403435FALSE
costOfRevenues0.000000000000013494042149FALSE
contributionProfit0.000000000000000546453504FALSE

Concluímos que, há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que as médias das variáveis revenue, costOfRevenues e ContributionProfit sejam iguais entre os anos.

In [ ]:
# Aplicando o teste de Kruskal-Wallis, para as variáveis não paramétricas, segundo a variável categórica especificada.

kruskalTest(categ = categ, nonParametricVars = varsForNonParametricTests, data = data)
A data.frame: 10 × 2
pValuethereIsEvidenceForEqualsMeans
<dbl><lgl>
marketing0.0007761152FALSE
contributionMargin0.0011541486FALSE
costPerCustomer0.0007809553FALSE
revenuePerCustomer0.0002281178FALSE
earningsPerCustomer0.0001977781FALSE
freeTrialsFromTotal0.0029392189FALSE
marketingFromTotal0.0088238841FALSE
totalSubscriptions0.0001809473FALSE
paidSubscriptions0.0001809473FALSE
freeTrails0.0375644146FALSE

Concluímos que, há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que as médias das variáveis marketing, contributionMargin, contributionMargin, costPerCustomer, revenuePerCustomer, earningsPerCustomer, freeTrialsFromTotal, marketingFromTotal, totalSubscriptions, paidSubscriptions e freeTrails sejam iguais entre os anos.

Também podemos categorizar as variáveis que estamos analisando por mês.

In [ ]:
# Definindo a variável categórica que será utilizada nos testes.

categ <- 'month'

# Aplicando o teste de Levene, para verificar se a variância entre os grupos é homogênea.

nmVarT <- normVarTest(categ = categ, data)

# Determinando quais variáveis são paramétricas.

varsForParametricTests <- rownames(nmT)[nmT$thereIsEvidenceOfNormality & nmVarT$thereIsEvidenceOfNormVar]

# Determinando quais variáveis são não paramétricas.

varsForNonParametricTests <- rownames(nmT)[!(nmT$thereIsEvidenceOfNormality & nmVarT$thereIsEvidenceOfNormVar)]

# Determinando quais variáveis são contínuas.

continuousCols <- varsForParametricTests %in% colnames(data[isContinuous])

# Definindo variáveis que devem passar por testes não paramétricos.

varsForNonParametricTests <- append(varsForNonParametricTests, varsForParametricTests[!continuousCols])

# Definindo variáveis que devem passar por testes paramétricos.

varsForParametricTests <- varsForParametricTests[continuousCols]
In [ ]:
# Aplicando o teste ANOVA One-Way, para as variáveis paramétricas, segundo a variável categórica especificada.

anovaTest(categ = categ, parametricVars = varsForParametricTests, data = data)
A data.frame: 3 × 2
pValuethereIsEvidenceForEqualsMeans
<dbl><lgl>
revenue0.9523326TRUE
costOfRevenues0.8872829TRUE
contributionProfit0.9900704TRUE

Concluímos que, não há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que as médias das variáveis revenue, costOfRevenues e ContributionProfit sejam iguais entre os trimestres.

In [ ]:
# Aplicando o teste de Kruskal-Wallis, para as variáveis não paramétricas, segundo a variável categórica especificada.

kruskalTest(categ = categ, nonParametricVars = varsForNonParametricTests, data = data)
A data.frame: 10 × 2
pValuethereIsEvidenceForEqualsMeans
<dbl><lgl>
marketing0.83829462 TRUE
contributionMargin0.99843972 TRUE
costPerCustomer0.50465343 TRUE
revenuePerCustomer0.89216640 TRUE
earningsPerCustomer0.99432772 TRUE
freeTrialsFromTotal0.23780293 TRUE
marketingFromTotal0.55864741 TRUE
totalSubscriptions0.91508755 TRUE
paidSubscriptions0.91508755 TRUE
freeTrails0.01361153FALSE

Concluímos que, não há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que as médias das variáveis marketing, contributionMargin, contributionMargin, costPerCustomer, revenuePerCustomer, earningsPerCustomer, freeTrialsFromTotal, marketingFromTotal, totalSubscriptions e paidSubscriptions sejam iguais entre os trimestres.

Há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que a média da variável freeTrails seja igual entre os trimestres.

5.6 Analisando correlações entre as variáveis

Nesta etapa desejamos verificar como as variáveis se correlacionam, ou seja, como uma variável ajuda a prever o valor de outra variável no dataset.

In [ ]:
# Determinando quais variáveis do dataset são do tipo numérico.

numVars <- sapply(data, is.numeric)

# Criando um pair plot para visualizar as correlações entre as variáveis númericas. 

ggpairs(data[numVars]) + theme_bw() + theme(axis.text.x = element_text(angle = 45, vjust = 1))

Algo interessante de se notar neste gráfico, é a alta correlação existente entre praticamente todas as variáveis do dataset.

Vamos investigar mais detalhadamente a força e a direção dessas correlações.

In [ ]:
# Verificando a correlação entre as variáveis do dataset.

corrplot(cor(data[numVars]),
         method      = 'color', 
         addCoef.col = 'white', 
         type        = 'upper', 
         tl.col      = 'black',
         diag        = F
)

Como suspeitávamos, há correlações praticamente perfeitas entre quase todas as variáveis do conjunto de dados. Ou seja, boa parte das variáveis carregam, praticamente, as mesmas informações que as outras.

Destacamos a correlação entre as variáveis totalSubscriptions e paidSubscriptions, que é perfeitamente positiva (correlação igual a 1).

6. Análise exploratória dos dados como Séries Temporais

Nesta etapa, iremos analisar as variáveis do dataset como séries temporais.

6.1 Criando funções auxiliares

6.1.1 Funções para aplicar testes estatísticos

6.1.1.1 Teste Augmented Dickey-Fuller (ADF)

Em estatística e econometria, o Teste de Dickey-Fuller aumentado ou Teste ADF (do acrônimo em inglês Augmented Dickey-Fuller) é um teste de raiz unitária em séries temporais. Ele é uma versão aumentada do Teste de Dickey-Fuller, é aplicado a modelos mais complicados de séries temporais.

A estatística ADF, usada no teste, é um número negativo, e quanto mais negativo, mais indicativo o teste se torna de rejeitar a hipótese nula de que existe raiz unitária na série.

O nome do teste faz referência aos estatísticos D. A. Dickey e W. A. Fuller.

As hipóteses deste teste são:

Teste de hipótese
H0: A série é não é estácionária.
Ha: A série é estacionária.

Para saber mais sobre testes estatísticos para comparar variâncias, consulte este link.

In [ ]:
# Definindo uma função, para aplicar o teste ADF, as séries temporais do dataset.

adfTest <- function(data, freq = 4, sig = 0.05) {

    # Aplicando o teste ADF a cada variável do dataset.

    result <- sapply(data, function(t) {

        # Convertendo a variável especificada em uma série temporal.

        t <- ts(t, frequency = freq)

        # Aplicando o teste ADF, e capturando o p-value gerado.

        adf.test(t, alternative = "stationary")$p.value
    })

    # Transpondo dados.

    dfTest <- data.frame(result)

    # Atribuindo o nome da coluna com os p-values.

    colnames(dfTest) <- 'pValue'
    
    # Criando um variável, para determinar se o p-value é menor do que o nível de significância especificado 
    # (por padrão, o nível de significância é de 5%).

    isStationary <- sapply(dfTest, function(pvalue) { ifelse(pvalue < sig, TRUE, FALSE) })

    # Renomeando o nome da variável especificada.

    colnames(isStationary) <- 'isStationary'
    
    # Convertendo variáveis para um dataframe.

    dfTest <- data.frame(dfTest)

    # Retornando um dataframe com os resultados obtidos.

    cbind(dfTest, isStationary)
}

6.1.2 Funções para a criação de gráficos

In [ ]:
# Definindo uma função, para plotar gráficos das compontentes da série temporal especificada.

tsDecomposePlot <- function(col, data, type, freq = 4, color = '#FF4E00') {

    # Convertendo a variável especificada em uma série temporal.

    t <- ts(data[col], frequency = freq)

    # Cria o gráfico da série temporal decomposta.
    
    ggdecompose(dts2(t, type = type)) + 
        geom_line(color = color, size = 2) +
        geom_point(size = 2) + 
        theme_bw() +
        xlab('Year') +
        ggtitle(paste("Time series components chart for variable:", col))
}
In [ ]:
# Definindo uma função, para plotar um gráfico de série temporal, com as previsões geradas. 

plotPredQuarters <- function(col, data, h = 2, freq = 4, colors = c('#06D6A0', '#FF4E00'), size = 2) {
 
    # Convertendo a variável especificada em uma série temporal.

    t <- ts(data[, col], frequency = freq)

    # Criando um modelo ARIMA.

    model <- auto.arima(t)

    # Realizando a previsão de h trimestres, segundo o modelo ARIMA criado.

    pred <- forecast(model, h = h)

    # Criando um gráfico de série temporal, com as previsões geradas para a variável especificada.

    autoplot(pred, predict.colour = colors[1], predict.size = size, colour = colors[2], size = size) +
        theme_bw() +
        xlab('Year') +
        ggtitle(paste("Forecasting chart for variable", col, "on the next", toString(h), "quartes")) 
}

6.1.3 Função para gerar previsões com o modelo ARIMA

In [ ]:
# Definindo uma função para gerar previsões para séries temporais utilizando o modelo ARIMA.

predQuarters <- function(data, h = 2, freq = 4) {
    
    # Capturando as métricas obtidas, e as previsões geradas, para cada uma das variáveis do dataset.

    result <- sapply(colnames(data), function(col){

        # Convertendo a variável especificada em uma série temporal.

        t <- ts(data[, col], frequency = freq)

        # Criando um modelo ARIMA.

        model <- auto.arima(t)

        # Imprimindo uma mensagem, para identificar a qual variável o modelo pertence.

        print(paste('---------------', 'model ARIMA for variable:', col, '---------------'))

        # Capturando as métricas do modelo criado, e imprimindo um resumo estatístico.

        metrics <- summary(model)

        # Realizando a previsão de h trimestres, segundo o modelo ARIMA criado.

        pred <- forecast(model, h = h)

        # Capturando os valores previstos para os trimestres.

        quarters <- data.frame(t(pred$mean))

        # Criando um nome para as colunas que vão armazenar os resultados previstos.

        colnames(quarters) <- sapply(1:h, function(n){paste('Qtr', toString(n), sep ="") } )

        # Criando um Dataframe com métricas, e com os resultados dos trimestres previstos.

        cbind(data.frame(metrics), quarters)
    })

    # Convertendo os resultados gerados para uma matriz de números inteiros.

    result <- sapply(as.data.frame(t(result)), as.numeric)

    # Convertendo resultados da matriz para um Dataframe.

    result <- as.data.frame(result)

    # Atribuindo o nome das linhas do Dataframe.

    rownames(result) <- colnames(data)
    
    # Retornando os resultados gerados.

    result
}

6.2 Aplicando o teste de estacionariedade ADF

In [ ]:
# Eliminando variáveis desnecessárias para a análise.

dt <- data %>% select(-c(date, segment, month, year))
In [ ]:
# Aplicando o teste ADF.

adfTest(dt)
A data.frame: 13 × 2
pValueisStationary
<dbl><lgl>
totalSubscriptions0.6083427FALSE
paidSubscriptions0.6738019FALSE
freeTrails0.9215495FALSE
revenue0.9900000FALSE
costOfRevenues0.9900000FALSE
marketing0.9900000FALSE
contributionProfit0.3717972FALSE
contributionMargin0.9900000FALSE
costPerCustomer0.8844615FALSE
revenuePerCustomer0.9194801FALSE
earningsPerCustomer0.9575494FALSE
freeTrialsFromTotal0.4192908FALSE
marketingFromTotal0.9900000FALSE

Não há evidências estatisticamente significativas, com um nível de significância de 5%, para rejeitar a hipótese nula de que as variáveis listadas acima são não estácionárias.

6.3 Decompondo Séries Temporais

6.3.1 Variável totalSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'totalSubscriptions'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável totalSubscriptions, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.2 Variável paidSubscriptions

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'paidSubscriptions'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável paidSubscriptions, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.3 Variável freeTrails

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrails'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável freeTrails, notamos que o quarto trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.4 Variável revenue

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenue'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável revenue, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.5 Variável costOfRevenues

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costOfRevenues'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável costOfRevenues, notamos que o terceiro trimestre é o de maior sazonalidade e o primeiro trimestre é o de menor sazonalidade.

6.3.6 Variável marketing

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketing'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável marketing, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.7 Variável contributionProfit

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionProfit'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável contributionProfit, notamos que o primeiro trimestre é o de maior sazonalidade e o quarto trimestre é o de menor sazonalidade.

6.3.8 Variável contributionMargin

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionMargin'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável contributionMargin, notamos que o primeiro trimestre é o de maior sazonalidade e o quarto trimestre é o de menor sazonalidade.

6.3.9 Variável costPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costPerCustomer'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável costPerCustomer, notamos que o terceiro trimestre é o de maior sazonalidade e o primeiro trimestre é o de menor sazonalidade.

6.3.10 Variável revenuePerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenuePerCustomer'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável revenuePerCustomer, notamos que o terceiro trimestre é o de maior sazonalidade e o quarto trimestre é o de menor sazonalidade.

6.3.11 Variável earningsPerCustomer

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'earningsPerCustomer'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável earningsPerCustomer, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.12 Variável freeTrialsFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrialsFromTotal'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável freeTrialsFromTotal, notamos que o quarto trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.3.13 Variável marketingFromTotal

In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'marketingFromTotal'

# Criando um gráfico de cada um dos componentes que constituem a série temporal.

tsDecomposePlot(col, dt, type = 'additive')

Ao analisar o gráfico de sazonalidade da variável marketingFromTotal, notamos que o primeiro trimestre é o de maior sazonalidade e o terceiro trimestre é o de menor sazonalidade.

6.4 Criando modelos preditivos com o algoritmo ARIMA

Nesta etapa, iremos realizar previsões para os 2 próximos trimestres, utilizando o algoritmo ARIMA.

In [ ]:
# Definindo como os valores frácionários devem ser exibidos (Sem notação científica).

options(scipen = 999)
In [ ]:
# Criando o modelo ARIMA,  e realizando as previsões para cada uma das variáveis do dataset.

predModels <- predQuarters(dt)
[1] "--------------- model ARIMA for variable: totalSubscriptions ---------------"
Series: t 
ARIMA(1,1,0)(0,1,1)[4] 

Coefficients:
         ar1     sma1
      0.4926  -0.5596
s.e.  0.1794   0.2656

sigma^2 estimated as 130382:  log likelihood=-167.89
AIC=341.78   AICc=343.04   BIC=345.18

Training set error measures:
                   ME     RMSE      MAE         MPE      MAPE       MASE
Training set -28.3991 312.7082 212.1465 -0.06274342 0.4727725 0.04012416
                    ACF1
Training set -0.01719786
[1] "--------------- model ARIMA for variable: paidSubscriptions ---------------"
Series: t 
ARIMA(0,1,0)(0,1,0)[4] 

sigma^2 estimated as 227394:  log likelihood=-174.48
AIC=350.96   AICc=351.15   BIC=352.09

Training set error measures:
                    ME     RMSE      MAE         MPE      MAPE      MASE
Training set -12.65349 432.1901 261.6475 -0.04148491 0.5829097 0.0498776
                  ACF1
Training set 0.3044022
[1] "--------------- model ARIMA for variable: freeTrails ---------------"
Series: t 
ARIMA(0,1,0)(1,1,0)[4] 

Coefficients:
         sar1
      -0.5021
s.e.   0.1893

sigma^2 estimated as 26499:  log likelihood=-149.83
AIC=303.66   AICc=304.26   BIC=305.93

Training set error measures:
                   ME     RMSE      MAE       MPE     MAPE      MASE      ACF1
Training set 17.83265 144.2936 107.0604 0.9031796 7.966087 0.6296125 -0.176234
[1] "--------------- model ARIMA for variable: revenue ---------------"
Series: t 
ARIMA(0,2,2) 

Coefficients:
          ma1      ma2
      -0.4623  -0.3369
s.e.   0.1767   0.1642

sigma^2 estimated as 1006004006:  log likelihood=-305.79
AIC=617.57   AICc=618.66   BIC=621.34

Training set error measures:
                   ME     RMSE      MAE       MPE    MAPE       MASE
Training set 6923.985 29364.76 16578.21 0.6041965 1.29637 0.07284754
                    ACF1
Training set -0.09827153
[1] "--------------- model ARIMA for variable: costOfRevenues ---------------"
Series: t 
ARIMA(0,2,1) 

Coefficients:
          ma1
      -0.8346
s.e.   0.1026

sigma^2 estimated as 392591671:  log likelihood=-294.23
AIC=592.45   AICc=592.97   BIC=594.97

Training set error measures:
                   ME    RMSE      MAE       MPE     MAPE      MASE       ACF1
Training set 4206.209 18722.4 12735.66 0.4709101 1.697254 0.1232717 -0.2350266
[1] "--------------- model ARIMA for variable: marketing ---------------"
Series: t 
ARIMA(3,2,1)(1,0,0)[4] 

Coefficients:
          ar1      ar2      ar3      ma1    sar1
      -0.2904  -0.4785  -0.5722  -0.6850  0.6081
s.e.   0.2133   0.1998   0.2189   0.1766  0.2396

sigma^2 estimated as 461705289:  log likelihood=-296.39
AIC=604.78   AICc=609.2   BIC=612.32

Training set error measures:
                   ME     RMSE      MAE      MPE     MAPE      MASE       ACF1
Training set 5002.352 18608.57 12612.45 3.098885 10.32527 0.3717084 -0.1413394
[1] "--------------- model ARIMA for variable: contributionProfit ---------------"
Series: t 
ARIMA(3,1,3) with drift 

Coefficients:
          ar1      ar2      ar3      ma1      ma2     ma3     drift
      -0.1801  -0.2024  -0.8851  -0.1737  -0.2051  0.0875  22388.30
s.e.   0.1367   0.1257   0.0982   0.2731   0.2190  0.2833   1343.66

sigma^2 estimated as 542693053:  log likelihood=-308.15
AIC=632.31   AICc=640.31   BIC=642.68

Training set error measures:
                    ME     RMSE      MAE       MPE     MAPE      MASE
Training set -566.2694 19688.52 16212.79 -1.763085 5.229727 0.1742554
                    ACF1
Training set 0.005141017
[1] "--------------- model ARIMA for variable: contributionMargin ---------------"
Series: t 
ARIMA(0,2,1) 

Coefficients:
          ma1
      -0.8438
s.e.   0.1060

sigma^2 estimated as 0.0005197:  log likelihood=61.3
AIC=-118.61   AICc=-118.09   BIC=-116.09

Training set error measures:
                       ME       RMSE       MAE       MPE     MAPE      MASE
Training set -0.005589154 0.02154096 0.0149265 -2.050004 4.939782 0.3747237
                   ACF1
Training set -0.0492704
[1] "--------------- model ARIMA for variable: costPerCustomer ---------------"
Series: t 
ARIMA(0,1,0)(0,1,1)[4] 

Coefficients:
         sma1
      -0.6432
s.e.   0.2124

sigma^2 estimated as 0.1994:  log likelihood=-14.64
AIC=33.28   AICc=33.88   BIC=35.55

Training set error measures:
                   ME      RMSE      MAE       MPE     MAPE      MASE
Training set 0.122424 0.3958072 0.257729 0.7317992 1.581853 0.3442854
                   ACF1
Training set -0.1510286
[1] "--------------- model ARIMA for variable: revenuePerCustomer ---------------"
Series: t 
ARIMA(0,1,1) with drift 

Coefficients:
         ma1   drift
      0.5298  0.4608
s.e.  0.1308  0.1424

sigma^2 estimated as 0.2591:  log likelihood=-19.2
AIC=44.41   AICc=45.45   BIC=48.3

Training set error measures:
                      ME      RMSE       MAE         MPE     MAPE      MASE
Training set -0.00228819 0.4809781 0.3293516 -0.09214398 1.249257 0.1672852
                   ACF1
Training set 0.08142942
[1] "--------------- model ARIMA for variable: earningsPerCustomer ---------------"
Series: t 
ARIMA(2,1,2)(1,0,0)[4] with drift 

Coefficients:
         ar1      ar2      ma1     ma2    sar1   drift
      0.9694  -0.8993  -1.1104  0.5434  0.4668  0.3765
s.e.  0.1326   0.0899   0.2694  0.2505  0.2101  0.0722

sigma^2 estimated as 0.2716:  log likelihood=-18.87
AIC=51.73   AICc=57.63   BIC=60.8

Training set error measures:
                     ME     RMSE       MAE        MPE     MAPE      MASE
Training set 0.02236924 0.451347 0.3342417 -0.3734447 3.240277 0.2078094
                   ACF1
Training set 0.00471821
[1] "--------------- model ARIMA for variable: freeTrialsFromTotal ---------------"
Series: t 
ARIMA(0,1,0)(0,1,0)[4] 

sigma^2 estimated as 0.1934:  log likelihood=-13.74
AIC=29.48   AICc=29.67   BIC=30.62

Training set error measures:
                     ME      RMSE      MAE      MPE    MAPE      MASE      ACF1
Training set 0.05183338 0.3985764 0.311091 1.391069 10.5757 0.4768415 -0.129934
[1] "--------------- model ARIMA for variable: marketingFromTotal ---------------"
Series: t 
ARIMA(0,1,0) 

sigma^2 estimated as 4.795:  log likelihood=-59.47
AIC=120.95   AICc=121.11   BIC=122.24

Training set error measures:
                    ME     RMSE      MAE        MPE    MAPE      MASE
Training set 0.1508421 2.150251 1.617689 -0.2687539 11.2178 0.6709821
                    ACF1
Training set -0.04247918
In [ ]:
# Imprimindo dataset com as previsões, e as métricas dos modelos criados, para cada variável.

predModels
A data.frame: 13 × 9
MERMSEMAEMPEMAPEMASEACF1Qtr1Qtr2
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
totalSubscriptions -28.399099594 312.70819925 212.1464616-0.06274342 0.47277250.04012416-0.017197862 60349.4031246 61124.4523230
paidSubscriptions -12.653488588 432.19009530 261.6475055-0.04148491 0.58290970.04987760 0.304402242 59020.0000000 60090.0000000
freeTrails 17.832647617 144.29361363 107.0603631 0.90317957 7.96608690.62961253-0.176233975 1881.0826183 1842.2542184
revenue6923.98536362729364.7603087216578.2144734 0.60419654 1.29636980.07284754-0.0982715272062940.72030902132588.5897542
costOfRevenues4206.20935172218722.4004189812735.6641273 0.47091013 1.69725420.12327172-0.2350266271132034.33940051170622.6788009
marketing5002.35233872618608.5723903412612.4533585 3.0988852010.32527420.37170840-0.141339417 383009.2315103 410417.7718147
contributionProfit-566.26936412319688.5219164616212.7927566-1.76308516 5.22972740.17425538 0.005141017 623486.3182855 679230.5334210
contributionMargin -0.005589154 0.02154096 0.0149265-2.05000370 4.93978220.37472374-0.049270398 0.2902444 0.2844889
costPerCustomer 0.122424027 0.39580722 0.2577290 0.73179916 1.58185250.34428538-0.151028558 18.4081149 19.0717130
revenuePerCustomer -0.002288190 0.48097807 0.3293516-0.09214398 1.24925710.16728523 0.081429415 34.4142878 34.8751264
earningsPerCustomer 0.022369235 0.45134704 0.3342417-0.37344465 3.24027660.20780940 0.004718210 16.2469930 17.1318585
freeTrialsFromTotal 0.051833384 0.39857635 0.3110910 1.3910693610.57570450.47684154-0.129934037 2.7943849 2.3947850
marketingFromTotal 0.150842115 2.15025069 1.6176886-0.2687538511.21780380.67098212-0.042479184 22.2402458 22.2402458

Iremos utilizar a métrica RMSE, para avaliar os modelos criados e os valores previstos.

In [ ]:
# Determinando a métrica, que deve ser utilizada para avaliar os modelos

metric <- 'RMSE'

# Capturando o índice da coluna com o nome da métrica especificada.

metricCol <- grep(metric, colnames(predModels))  

# Capturando os índices das colunas com as letras iniciais Qtr (são colunas com as previsões geradas).

qtrsCols  <- grep('Qtr', colnames(predModels)) 

# Criando uma única lista com os índices capturados.

cols <- append(metricCol, qtrsCols)  

# Ordenando as linhas do dataframe, em ordem crescente, segundo os valores da coluna de métricas.

predModelsOrdered <- predModels[order(predModels[, metric]), cols]
In [ ]:
# Imprimindo Dataframe ordenado.

predModelsOrdered
A data.frame: 13 × 3
RMSEQtr1Qtr2
<dbl><dbl><dbl>
contributionMargin 0.02154096 0.2902444 0.2844889
costPerCustomer 0.39580722 18.4081149 19.0717130
freeTrialsFromTotal 0.39857635 2.7943849 2.3947850
earningsPerCustomer 0.45134704 16.2469930 17.1318585
revenuePerCustomer 0.48097807 34.4142878 34.8751264
marketingFromTotal 2.15025069 22.2402458 22.2402458
freeTrails 144.29361363 1881.0826183 1842.2542184
totalSubscriptions 312.70819925 60349.4031246 61124.4523230
paidSubscriptions 432.19009530 59020.0000000 60090.0000000
marketing18608.57239034 383009.2315103 410417.7718147
costOfRevenues18722.400418981132034.33940051170622.6788009
contributionProfit19688.52191646 623486.3182855 679230.5334210
revenue29364.760308722062940.72030902132588.5897542

6.5 Criando gráficos dos melhores modelos preditivos gerados

Por fim, vamos gerar gráficos, com os valores previstos para os modelos que apresentaram os melhores scores, segundo a métrica selecionada.

In [ ]:
# Capturando o nome, dos 5 melhores modelos ARIMA, segundo a métrica especificada.

rownames(predModelsOrdered)[1:5]
  1. 'contributionMargin'
  2. 'costPerCustomer'
  3. 'freeTrialsFromTotal'
  4. 'earningsPerCustomer'
  5. 'revenuePerCustomer'
In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'contributionMargin'

# Criando um gráfico da série temporal com as previsões geradas.

plotPredQuarters(col, dt)
In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'costPerCustomer'

# Criando um gráfico da série temporal com as previsões geradas.

plotPredQuarters(col, dt)
In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'freeTrialsFromTotal'

# Criando um gráfico da série temporal com as previsões geradas.

plotPredQuarters(col, dt)
In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'earningsPerCustomer'

# Criando um gráfico da série temporal com as previsões geradas.

plotPredQuarters(col, dt)
In [ ]:
# Definindo o nome da variável a ser analisada.

col <- 'revenuePerCustomer'

# Criando um gráfico da série temporal com as previsões geradas.

plotPredQuarters(col, dt)

E assim, finalizamos nossa análise estatística sobre os dados financeiros trimestrais da Netflix!

Entre em contato comigo!

Caso tenha alguma dúvida, sugestão ou apenas queira trocar uma ideia sobre este projeto, não hesite em entrar em contato comigo!