Cómo usar Python para pronosticar demanda, tráfico y más para SEO

La previsión puede ofrecer un gran valor en SEO. Aquí le mostramos cómo obtener respuestas basadas en datos sobre posibles tendencias en la búsqueda orgánica usando Python.

Ya sea que se trate de la demanda de búsqueda, los ingresos o el tráfico de la búsqueda orgánica, en algún momento de su carrera de SEO, seguramente se le pedirá que entregue un pronóstico.

En esta columna, aprenderá cómo hacerlo de manera precisa y eficiente, gracias a Python.

Vamos a explorar cómo:

  • Extraiga y trace sus datos.
  • Utilice métodos automatizados para estimar los parámetros del modelo de mejor ajuste.
  • Aplique el método Dickey-Fuller aumentado (ADF) para probar estadísticamente una serie temporal.
  • Estimar el número de parámetros para un modelo SARIMA.
  • Pruebe sus modelos y comience a hacer pronósticos.
  • Interprete y exporte sus pronósticos.

Antes de entrar en materia, definamos los datos. Independientemente del tipo de métrica, intentamos pronosticar que los datos ocurren con el tiempo.

En la mayoría de los casos, es probable que esto ocurra durante una serie de fechas.. Así que efectivamente, las técnicas que estamos revelando aquí son técnicas de pronóstico de series de tiempo.

Entonces, ¿por qué pronosticar?

Para responder una pregunta con otra pregunta, ¿por qué no pronosticarías?

Estas técnicas se han utilizado durante mucho tiempo en finanzas para precios de acciones, por ejemplo, y en otros campos.. ¿Por qué el SEO debería ser diferente?

Con múltiples intereses, como el titular del presupuesto y otros colegas, por ejemplo, el gerente de SEO y el director de marketing, habrá expectativas sobre lo que puede ofrecer el canal de búsqueda orgánica y si esas expectativas se cumplirán o no.

Los pronósticos proporcionan una respuesta basada en datos.

Información útil de pronóstico para profesionales de SEO

Tomando el enfoque basado en datos usando Python, hay algunas cosas a tener en cuenta:

Los pronósticos funcionan mejor cuando hay muchos datos históricos.

La cadencia de los datos determinará el marco de tiempo necesario para su pronóstico.

Por ejemplo, si tiene datos diarios como los que tendría en el análisis de su sitio web, tendrá más de 720 puntos de datos, lo cual está bien.

Con Google Trends, que tiene una cadencia semanal, necesitará al menos 5 años para obtener 250 puntos de datos.

En cualquier caso, debe apuntar a un período de tiempo que le brinde al menos 200 puntos de datos (un número extraído de mi experiencia personal).

A los modelos les gusta la consistencia.

Si la tendencia de sus datos tiene un patrón, por ejemplo, es cíclico porque hay estacionalidad, entonces es más probable que sus pronósticos sean confiables.

Por esa razón, los pronósticos no manejan muy bien las tendencias emergentes porque no hay datos históricos en los que basar el futuro, como veremos más adelante.

Entonces, ¿cómo funcionan los modelos de pronóstico?

Autocorrelación

La autocorrelación es la medida en que el punto de datos es similar al punto de datos anterior.

Esto puede brindarle al modelo información sobre cuánto impacto tiene un evento en el tiempo sobre el tráfico de búsqueda y si el patrón es estacional.

estacionalidad

La estacionalidad informa al modelo si existe un patrón cíclico y las propiedades del patrón, por ejemplo, cuánto tiempo o el tamaño de la variación entre los máximos y mínimos.

Estacionariedad

La estacionariedad es la medida de cómo la tendencia general está cambiando con el tiempo.. Una tendencia no estacionaria mostraría una tendencia general hacia arriba o hacia abajo, a pesar de los altibajos de los ciclos estacionales.

Con lo anterior en mente, los modelos «harán» cosas con los datos para que sean más lineales y, por lo tanto, más predecibles.

Con la teoría de las paradas fuera del camino, comencemos a pronosticar.

Explorando sus datos

# Import your libraries
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf 
from statsmodels.tsa.seasonal import seasonal_decompose                        
from sklearn.metrics import mean_squared_error
from statsmodels.tools.eval_measures import rmse
import warnings
warnings.filterwarnings("ignore")
from pmdarima import auto_arima

Estamos utilizando datos de Google Trends, que es una exportación CSV.

Estas técnicas se pueden utilizar en cualquier serie de datos de tiempo, ya sean los suyos, los clics, los ingresos, etc. de su cliente o empresa.

# Import Google Trends Data
df = pd.read_csv("exports/keyword_gtrends_df.csv", index_col=0)
df.head()
Example data from Google Trends.

Como era de esperar, los datos de Google Trends son una serie temporal muy simple con fecha, consulta y resultados que abarcan un período de 5 años.

Es hora de formatear el marco de datos para pasar de largo a ancho.

Esto nos permite ver los datos con cada consulta de búsqueda como columnas:

df_unstacked = ps_trends.set_index(["date", "query"]).unstack(level=-1)
df_unstacked.columns.set_names(['hits', 'query'], inplace=True)
ps_unstacked = df_unstacked.droplevel('hits', axis=1)
ps_unstacked.columns = [c.replace(' ', '_') for c in ps_unstacked.columns]
ps_unstacked = ps_unstacked.reset_index()
ps_unstacked.head()
Formatted dataframe.

Ya no tenemos una columna de aciertos, ya que estos son los valores de las consultas en sus respectivas columnas.

Este formato no solo es útil para SARIMA (que exploraremos aquí) sino también para redes neuronales como la memoria a largo plazo (LSTM).

Grafiquemos los datos:

ps_unstacked.plot(figsize=(10,5))
Plotting the data.

De la gráfica (arriba), notará que los perfiles de «PS4» y «PS5» son diferentes. Para los que no son jugadores entre ustedes, «PS4» es la cuarta generación de la consola Sony Playstation y «PS5» la quinta.

Las búsquedas de «PS4» son muy estacionales, ya que son un producto establecido y tienen un patrón regular, aparte del final, cuando aparece la «PS5».

La “PS5” no existía hace 5 años, lo que explicaría la ausencia de tendencia en los primeros 4 años de la trama anterior.

Elegí esas dos consultas para ayudar a ilustrar la diferencia en la efectividad de pronóstico para las dos características muy diferentes.

Descomponer la tendencia

Descompongamos ahora las características estacionales (o no estacionales) de cada tendencia:

ps_unstacked.set_index("date", inplace=True)
ps_unstacked.index = pd.to_datetime(ps_unstacked.index)
query_col = 'ps5'

a = seasonal_decompose(ps_unstacked[query_col], model = "add")
a.plot();
Time series data.

Lo anterior muestra los datos de la serie temporal y la tendencia suavizada general que surge a partir de 2020.

El cuadro de tendencia estacional muestra picos repetidos, lo que indica que hay estacionalidad a partir de 2016. Sin embargo, no parece particularmente fiable dado lo plana que es la serie temporal desde 2016 hasta 2020.

También es sospechosa la falta de ruido, ya que la trama estacional muestra un patrón prácticamente uniforme que se repite periódicamente.

El Resid (que significa «Residual») muestra cualquier patrón de lo que queda de los datos de la serie temporal después de tener en cuenta la estacionalidad y la tendencia, que en efecto no es nada hasta 2020, ya que está en cero la mayor parte del tiempo.

Para «ps4»:

Time series data.

Podemos ver fluctuaciones a corto plazo (Estacionalidad) y largo plazo (Tendencia), con algo de ruido (Resid).

El siguiente paso es utilizar el método Dickey-Fuller aumentado (ADF) para probar estadísticamente si una serie temporal determinada es estacionaria o no.

from pmdarima.arima import ADFTest

adf_test = ADFTest(alpha=0.05)
adf_test.should_diff(ps_unstacked[query_col])
PS4: (0.09760939899434763, True)
PS5: (0.01, False)

Podemos ver que el valor p de «PS5» que se muestra arriba es superior a 0,05, lo que significa que los datos de la serie temporal no son estacionarios y, por lo tanto, necesitan diferenciarse.

«PS4», por otro lado, es inferior a 0,05 a 0,01;

El objetivo de todo esto es comprender los parámetros que se usarían si estuviéramos construyendo manualmente un modelo para pronosticar las búsquedas de Google.

Ajuste de su modelo SARIMA

Dado que usaremos métodos automatizados para estimar los parámetros del modelo de mejor ajuste (más adelante), ahora estimaremos la cantidad de parámetros para nuestro modelo SARIMA.

He elegido SARIMA porque es fácil de instalar. Aunque el Profeta de Facebook es elegante matemáticamente hablando (utiliza métodos de Monte Carlo), no se mantiene lo suficiente y muchos usuarios pueden tener problemas para instalarlo.

En cualquier caso, SARIMA se compara bastante bien con Prophet en términos de precisión.

Para estimar los parámetros de nuestro modelo SARIMA, tenga en cuenta que establecemos m en 52 ya que hay 52 semanas en un año, que es como se espacian los períodos en Google Trends.

También configuramos todos los parámetros para que comiencen en 0 para que podamos dejar que auto_arima haga el trabajo pesado y busque los valores que mejor se ajusten a los datos para la previsión.

ps5_s = auto_arima(ps_unstacked['ps4'],
           trace=True,
           m=52, # there are 52 periods per season (weekly data)
           start_p=0,
           start_d=0,
           start_q=0, 
           seasonal=False)

Respuesta a lo anterior:

Performing stepwise search to minimize aic

 ARIMA(3,0,3)(0,0,0)[0]             : AIC=1842.301, Time=0.26 sec
 ARIMA(0,0,0)(0,0,0)[0]             : AIC=2651.089, Time=0.01 sec
...
 ARIMA(5,0,4)(0,0,0)[0] intercept   : AIC=1829.109, Time=0.51 sec

Best model:  ARIMA(4,0,3)(0,0,0)[0] intercept
Total fit time: 6.601 seconds

La impresión anterior muestra que los parámetros que obtienen los mejores resultados son:

PS4: ARIMA(4,0,3)(0,0,0)
PS5: ARIMA(3,1,3)(0,0,0)

La estimación de PS5 se detalla más al imprimir el resumen del modelo:

ps5_s.summary()
SARIMAX results.

Lo que sucede es esto: la función busca minimizar la probabilidad de error medida tanto por el criterio de información de Akaike (AIC) como por el criterio de información bayesiano.

AIC = -2Log(L) + 2(p + q + k + 1)

Tal que L es la verosimilitud de los datos, k = 1 si c ≠ 0 y k = 0 si c = 0

BIC = AIC + [log(T) - 2] + (p + q + k + 1)

Al minimizar AIC y BIC, obtenemos los parámetros mejor estimados para p y q.

Probar el modelo

Ahora que tenemos los parámetros, podemos comenzar a hacer pronósticos. Primero, vamos a ver cómo se comporta el modelo con los datos anteriores.. Esto nos da una indicación de qué tan bien podría funcionar el modelo para períodos futuros.

ps4_order = ps4_s.get_params()['order']
ps4_seasorder = ps4_s.get_params()['seasonal_order']
ps5_order = ps5_s.get_params()['order']
ps5_seasorder = ps5_s.get_params()['seasonal_order']

params = {
    "ps4": {"order": ps4_order, "seasonal_order": ps4_seasorder},
    "ps5": {"order": ps5_order, "seasonal_order": ps5_seasorder}
}

results = []
fig, axs = plt.subplots(len(X.columns), 1, figsize=(24, 12))  

for i, col in enumerate(X.columns):
    #Fit best model for each column
    arima_model = SARIMAX(train_data[col],
                          order = params[col]["order"],
                          seasonal_order = params[col]["seasonal_order"])
    arima_result = arima_model.fit()

    #Predict
    arima_pred = arima_result.predict(start = len(train_data),
                                      end = len(X)-1, typ="levels")\
                             .rename("ARIMA Predictions")

    #Plot predictions
    test_data[col].plot(figsize = (8,4), legend=True, ax=axs[i])
    arima_pred.plot(legend = True, ax=axs[i])
    arima_rmse_error = rmse(test_data[col], arima_pred)

    mean_value = X[col].mean()
    results.append((col, arima_pred, arima_rmse_error, mean_value))
    print(f'Column: {col} --> RMSE Error: {arima_rmse_error} - Mean: {mean_value}\n')

Column: ps4 --> RMSE Error: 8.626764032898576 - Mean: 37.83461538461538
Column: ps5 --> RMSE Error: 27.552818032476257 - Mean: 3.973076923076923

Las previsiones muestran que los modelos son buenos cuando hay suficiente historial hasta que cambian repentinamente, como lo han hecho para PS4 a partir de marzo.

Para PS5, los modelos son prácticamente inútiles desde el primer momento.

Lo sabemos porque el error cuadrático medio (RMSE) es 8,62 para PS4, que es más de un tercio del RMSE de PS5 de 27,5.. Dado que Google Trends varía de 0 a 100, este es un margen de error del 27%.

Pronosticar el futuro

En este punto, ahora haremos el temerario intento de pronosticar el futuro en función de los datos que tenemos hasta la fecha:

oos_train_data = ps_unstacked
oos_train_data.tail()
Data to use to forecast.

Como puede ver en el extracto de la tabla anterior, ahora estamos utilizando todos los datos disponibles.

Ahora, predeciremos los próximos 6 meses (definidos como 26 semanas) en el siguiente código:

oos_results = []
weeks_to_predict = 26
fig, axs = plt.subplots(len(ps_unstacked.columns), 1, figsize=(24, 12)) 

for i, col in enumerate(ps_unstacked.columns):
    #Fit best model for each column
    s = auto_arima(oos_train_data[col], trace=True)
    oos_arima_model = SARIMAX(oos_train_data[col],
                          order = s.get_params()['order'],
                          seasonal_order = s.get_params()['seasonal_order'])
    oos_arima_result = oos_arima_model.fit()
    #Predict
    oos_arima_pred = oos_arima_result.predict(start = len(oos_train_data),
                                      end = len(oos_train_data) + weeks_to_predict, typ="levels").rename("ARIMA Predictions")

    #Plot predictions
    oos_arima_pred.plot(legend = True, ax=axs[i])
    axs[i].legend([col]);
    mean_value = ps_unstacked[col].mean()

    oos_results.append((col, oos_arima_pred, mean_value))
    print(f'Column: {col} - Mean: {mean_value}\n')

La salida:

Performing stepwise search to minimize aic

 ARIMA(2,0,2)(0,0,0)[0] intercept   : AIC=1829.734, Time=0.21 sec
 ARIMA(0,0,0)(0,0,0)[0] intercept   : AIC=1999.661, Time=0.01 sec
...
 ARIMA(1,0,0)(0,0,0)[0]             : AIC=1865.936, Time=0.02 sec

Best model:  ARIMA(1,0,0)(0,0,0)[0] intercept
Total fit time: 0.722 seconds
Column: ps4 - Mean: 37.83461538461538
Performing stepwise search to minimize aic
 ARIMA(2,1,2)(0,0,0)[0] intercept   : AIC=1657.990, Time=0.19 sec
 ARIMA(0,1,0)(0,0,0)[0] intercept   : AIC=1696.958, Time=0.01 sec
...
 ARIMA(4,1,4)(0,0,0)[0]             : AIC=1645.756, Time=0.56 sec

Best model:  ARIMA(3,1,3)(0,0,0)[0]          
Total fit time: 7.954 seconds
Column: ps5 - Mean: 3.973076923076923

Esta vez, automatizamos la búsqueda de los mejores parámetros de ajuste y los introdujimos directamente en el modelo.

Ha habido muchos cambios en las últimas semanas de los datos.. Aunque las tendencias pronosticadas parecen probables, no parecen muy precisas, como se muestra a continuación:

Graph of forecast from the data.

Ese es el caso de esas dos palabras clave;

La calidad del pronóstico dependerá de cuán estables sean los patrones históricos y, obviamente, no tendrá en cuenta eventos imprevisibles como el COVID-19.

Comience a hacer pronósticos para SEO

Si no estaba entusiasmado con la herramienta de visualización de datos matplotlib de Python, ¡no tema!

Para exportar sus pronósticos:

df_pred = pd.concat([pd.Series(res[1]) for res in oos_results], axis=1)
df_pred.columns = [x + str('_preds') for x in ps_unstacked.columns]
df_pred.to_csv('your_forecast_data.csv')

Lo que aprendimos aquí es dónde es útil hacer pronósticos usando modelos estadísticos o es probable que agregue valor para los pronósticos, particularmente en sistemas automatizados como tableros, es decir, cuando hay datos históricos y no cuando hay un pico repentino, como PS5.

Más recursos:

Imagen destacada: ImageFlow/Shutterstock

Leer el articulo original en Search Engine Journal.

¡Danos un Voto!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

¿Tienes una pregunta?

Luis Narciso
Sobre SEO
(Posicionamiento Web)

Frank Fajardo
Sobre Diseño Web, Anuncios, Diseño y Redes Sociales