1. Introducción

El análisis de una serie de tiempo es proceso que tiene el fin de encontrar un patrón o tendencia de los datos de la serie a analizar, en este caso nosotras analizaremos una serie de tiempo del precio de las acciones de Netflix en un periodo de 5 años (del 5 de febrero de 2018 al 5 de febrero de 2022), obtenida de la siguiente página https://www.kaggle.com/datasets/jainilcoder/netflix-stock-price-prediction, esto con el propósito de predecir el precio de dichas acciones a 30 días dado que los precios de las acciones son muy volátiles pues dependen del mercado es muy difícil de predecir correctamente a largo plazo.

El propósito de nuestro análisis es ayudar a los inversionistas (que son los principales interesados en dichas acciones) a tomar una decisión correcta para que puedan tener una inversión buena y exitosa, lo cual basados en estos datos al final podremos afirmar o refutar nuestra hipótesis, la cual es que el precio de las acciones de Netflix es una buena inversión.

library(readxl)    ## Leer archivos de Excel
library(fpp3)      ## Librería del libro de ST
## ── Attaching packages ────────────────────────────────────────────── fpp3 0.5 ──
## ✔ tibble      3.2.1     ✔ tsibble     1.1.3
## ✔ dplyr       1.1.3     ✔ tsibbledata 0.4.1
## ✔ tidyr       1.3.0     ✔ feasts      0.3.1
## ✔ lubridate   1.9.3     ✔ fable       0.3.3
## ✔ ggplot2     3.4.4     ✔ fabletools  0.3.4
## ── Conflicts ───────────────────────────────────────────────── fpp3_conflicts ──
## ✖ lubridate::date()    masks base::date()
## ✖ dplyr::filter()      masks stats::filter()
## ✖ tsibble::intersect() masks base::intersect()
## ✖ tsibble::interval()  masks lubridate::interval()
## ✖ dplyr::lag()         masks stats::lag()
## ✖ tsibble::setdiff()   masks base::setdiff()
## ✖ tsibble::union()     masks base::union()
library(forecast)  ## Centrado de la serie de tiempo
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(zoo)       ## Lo usamos para poder dividir los trimestres
## 
## Attaching package: 'zoo'
## The following object is masked from 'package:tsibble':
## 
##     index
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(TSstudio)  ## Para el análisis descriptivo y predictivo de datos de series temporales
library(fpp2)
## ── Attaching packages ────────────────────────────────────────────── fpp2 2.5 ──
## ✔ fma       2.5     ✔ expsmooth 2.3
## 
## 
## Attaching package: 'fpp2'
## The following object is masked from 'package:fpp3':
## 
##     insurance
library(readr)
# Instalar las bibliotecas si aún no están instaladas
# install.packages("xts")
# install.packages("ggplot2")

# Cargar las bibliotecas
library(xts)
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
## 
##     first, last
library(ggplot2)

2. Desarrollo

En esta parte seguiremos una serie de pasos para saber si nuestra hipótesis es correcta o no,en general los análisis de series de tiempo tienen los siguientes tres pasos:

2.1 Análisis exploratorio

Dentro del análisis exploratorio transformaremos nuestros datos a una serie de tiempo para poner descomponer nuestra serie donde podemos ver en los componentes: tendencia, estacional, cíclico y aleatorio.

NFLX <- read.csv("NFLX.csv") 

# Install and load the lubridate package
# install.packages("lubridate")
library(lubridate)
NFLX <- NFLX |>select(Date,Price)

# Crear una secuencia de fechas basada en el número de filas
NFLX$Date <- seq(as.Date("2018-02-05"), by = "days", length.out = nrow(NFLX))

# Convertir la columna "Date" a formato de fecha con lubridate
NFLX$Date <- lubridate::ymd(NFLX$Date)

# Verificar la clase de la columna "Date" después de la conversión
print(class(NFLX$Date))
## [1] "Date"
head(NFLX)
##         Date  Price
## 1 2018-02-05 254.26
## 2 2018-02-06 265.72
## 3 2018-02-07 264.56
## 4 2018-02-08 250.10
## 5 2018-02-09 249.47
## 6 2018-02-10 257.95
# Graficar la serie de tiempo

library(ggplot2)

ggplot(NFLX, aes(x = Date, y = Price)) +
  geom_line() +
  labs(title = "Serie Temporal de NFLX",
       x = "Fecha",
       y = "Precio") 

### 2.1.1 Descomponer la serie

# Crear una serie de tiempo
ts_data <- ts(NFLX$Price, frequency = 365)  # Ya que es una frecuencia diaria
datoprice <- as_tsibble(NFLX, index = "Date")
datoprice
## # A tsibble: 1,009 x 2 [1D]
##    Date       Price
##    <date>     <dbl>
##  1 2018-02-05  254.
##  2 2018-02-06  266.
##  3 2018-02-07  265.
##  4 2018-02-08  250.
##  5 2018-02-09  249.
##  6 2018-02-10  258.
##  7 2018-02-11  258.
##  8 2018-02-12  266 
##  9 2018-02-13  280.
## 10 2018-02-14  279.
## # ℹ 999 more rows
# Descomponer la serie temporal
decomposition <- decompose(ts_data)

# Graficar la descomposición
par(mfrow = c(3, 1))
plot(decomposition)

2.1.2 Analizar la tendencia, los residuos y la estacionalidad

El elemento estacional es un componente de la serie que recoge las oscilaciones que se producen en períodos iguales o inferiores a un año y que se repiten de forma regular en los diferentes años.

Si se considera el año como período marco o de repetición pueden observarse las fluctuaciones de la magnitud a lo largo de sus meses o de sus trimestres, cuatrimestres, etc. El origen de las variaciones estacionales (componente estacional) puede estar en factores físico-naturales como son las estaciones climatológicas o en factores culturales o de tradición: fiestas navideñas, vacaciones, horarios comerciales, etc.

En este caso vamos a estudiar la estacionalidad de nuestra serie de tiempo por medio de la descomposición que ya hicimos anteriormente, podemos observar que presenta un patrón, el cual nos hace ver que nuestra serie es estacional.

# Obtener la componente estacional
seasonal_component <- decomposition$seasonal

La tendencia secular o tendencia a largo plazo de una serie es por lo común el resultado de factores a largo plazo.Las tendencias a largo plazo se ajustan a diversos esquemas. Algunas se mueven continuamente hacía arriba, otras declinan, y otras más permanecen igual en un cierto período o intervalo de tiempo.

# Obtener la componente de tendencia
trend_component <- decomposition$trend

2.1.3 Ruido blanco

Las Series Temporales tienen como objetivo el de descomponer la serie observadas en dos partes: una de las partes es la que depende del pasado, la otra es la parte impredecible.

Un Ruido Blanco es una serie tal que su media es cero, la varianza es constante y es incorrelacionada.

#NOTA:Esta grafico lo podemos quitar por si quieres
# Obtener el ruido blanco (residuos)
residuals <- ts_data - trend_component - seasonal_component
# Graficar el ruido blanco
plot(residuals, main = "Ruido Blanco de la Serie Temporal", xlab = "Fecha", ylab = "Residuos")

2.1.4 Prueba Dickey and Fuller

Una forma de corregir la no estacionariedad es por el método de la raíz unitaria ya que esta prueba se usa para descubrir la primera diferencia o regresión que se debe usar para hacerla estacionaria.

# Cargar el paquete tseries
library(tseries)

# Realizar la prueba de Dickey-Fuller
adf_result <- adf.test(ts_data)

# Imprimir el resultado
print(adf_result)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  ts_data
## Dickey-Fuller = -1.7847, Lag order = 10, p-value = 0.6695
## alternative hypothesis: stationary

Como podemos ver, nuestra serie de tiempo cumple con la estacionariedad por la prueba de raíz unitaria de Dickey-Fuller.

2.2 Ajuste del modelo

Ahora desestacionalizaremos los precios y así tener mejores datos para poder pronósticar a 30 días.

DesPrice <- datoprice |>
  model(stl = STL(Price))
components(DesPrice)
## # A dable: 1,009 x 8 [1D]
## # Key:     .model [1]
## # :        Price = trend + season_year + season_week + remainder
##    .model Date       Price trend season_week season_year remainder season_adjust
##    <chr>  <date>     <dbl> <dbl>       <dbl>       <dbl>     <dbl>         <dbl>
##  1 stl    2018-02-05  254.  350.       1.34        -35.6     -60.9          289.
##  2 stl    2018-02-06  266.  349.       0.529       -31.6     -52.6          297.
##  3 stl    2018-02-07  265.  349.       3.42        -28.6     -59.6          290.
##  4 stl    2018-02-08  250.  349.      -3.02        -34.0     -62.2          287.
##  5 stl    2018-02-09  249.  349.      -3.89        -29.7     -66.2          283.
##  6 stl    2018-02-10  258.  349.      -1.18        -28.7     -61.3          288.
##  7 stl    2018-02-11  258.  349.       2.72        -29.2     -64.3          285.
##  8 stl    2018-02-12  266   349.       1.45        -35.9     -48.6          300.
##  9 stl    2018-02-13  280.  349.       0.394       -32.3     -36.8          312.
## 10 stl    2018-02-14  279.  349.       3.43        -36.1     -37.7          311.
## # ℹ 999 more rows

Usaremos los datos desestacionalizados es decir la columna Season_adjust para tener menos variabilidad en la información.

ajustprice <- components(DesPrice)$season_adjust
trendprice <- components(DesPrice)$trend

fecha <- components(DesPrice)$Date
Fecha<-as.data.frame.Date(fecha)

datosajustados <- cbind(Fecha,ajustprice)

datosajustados1 <- as_tsibble(datosajustados, index = fecha)
datosajustados1
## # A tsibble: 1,009 x 2 [1D]
##    fecha      ajustprice
##    <date>          <dbl>
##  1 2018-02-05       289.
##  2 2018-02-06       297.
##  3 2018-02-07       290.
##  4 2018-02-08       287.
##  5 2018-02-09       283.
##  6 2018-02-10       288.
##  7 2018-02-11       285.
##  8 2018-02-12       300.
##  9 2018-02-13       312.
## 10 2018-02-14       311.
## # ℹ 999 more rows

2.2.1 Modelo SNAIVE

Observamos que los datos llegan al 31 de octubre del 2020 y estamos proyectando a 30 días a futuro, es decir hasta el 30 de noviembre del 2020 con el modelo SNAIVE.

# Generamos el modelo de regresión múltiple
datosajustados1|>
  model(SNAIVE(ajustprice)) |>
  forecast(h=30) |>
  autoplot(datosajustados1) +
  labs(y = "Precios ajustados",
       title = "Netflix")

datosajustados1
## # A tsibble: 1,009 x 2 [1D]
##    fecha      ajustprice
##    <date>          <dbl>
##  1 2018-02-05       289.
##  2 2018-02-06       297.
##  3 2018-02-07       290.
##  4 2018-02-08       287.
##  5 2018-02-09       283.
##  6 2018-02-10       288.
##  7 2018-02-11       285.
##  8 2018-02-12       300.
##  9 2018-02-13       312.
## 10 2018-02-14       311.
## # ℹ 999 more rows

2.2.2 Modelo ARIMA

Ocuparemos el modelo ARIMA pues proporcionan métodos más sofisticados para crear modelos de los componentes de tendencia y estacionales que los modelos de suavizado exponencial y disponen de la ventaja añadida de poder incluir variables predictoras en el modelo.

fit <- datosajustados1 |>
  model(ARIMA(ajustprice))
report(fit)
## Series: ajustprice 
## Model: ARIMA(2,1,2)(0,0,2)[7] 
## 
## Coefficients:
##          ar1     ar2      ma1      ma2     sma1     sma2
##       0.0677  0.8694  -0.0258  -0.7837  -0.3687  -0.2197
## s.e.  0.1034  0.1008   0.1139   0.1065   0.0324   0.0316
## 
## sigma^2 estimated as 61.1:  log likelihood=-3501.13
## AIC=7016.26   AICc=7016.37   BIC=7050.67
fit |> forecast(h=30) |>
  autoplot(datosajustados1) +
  labs(y = "Precio de Netflix", title = "Stock de Netflix")

 fit |>gg_tsresiduals()

3. Conclusión

Podemos observar en el gráfico del apartado 2.2.2 “Stock de Netflix” que los precios de la acción se mantienen “estables”, es decir no tienden a una alza tan pronunciada, por lo que se consideraría que por el momento puede ser riesgoso invertir en esta acción, ya que después de la perdida notoria que tuvo en los ultimos días uno podria pensar que ya toco su punto más bajo y ahora subira el precio de la acción por lo que seria conveninete comprar, pero dado el pronóstico con el modelo ARIMA, se visualiza que es probable que siga a la baja, es un riesgo que podemos tomar o no.

4. Referencias

  1. Rojas-Jimenez, K. (s/f). Capítulo 8 análisis de series de tiempo. Bookdown.org. Recuperado el 21 de noviembre de 2023, de https://bookdown.org/keilor_rojas/CienciaDatos/an%C3%A1lisis-de-series-de-tiempo.html

  2. Mordán, N. R., & Perfil, V. T. mi. (s/f). Economía Aplicada. Blogspot.com. Recuperado el 21 de noviembre de 2023, de https://betaeconomia.blogspot.com/2020/03/modificando-la-frecuencia-de-una-serie.html

  3. IBM Documentation. (2021, agosto 17). Ibm.com. https://www.ibm.com/docs/es/spss-modeler/saas?topic=series-arima