Modelo de pronóstico de autorregresión para el consumo de electricidad de los hogares


Dado el auge de los medidores de electricidad inteligentes y la amplia adopción de tecnología de generación de electricidad como los paneles solares, hay una gran cantidad de datos disponibles sobre el uso de electricidad.

Estos datos representan una serie temporal multivariante de variables relacionadas con la energía que, a su vez, podrían usarse para modelar e incluso pronosticar el consumo futuro de electricidad.

Los modelos de autocorrelación son muy simples y pueden proporcionar una forma rápida y efectiva de hacer pronósticos hábiles de uno o varios pasos para el consumo de electricidad.

Descripción del problema

El conjunto de datos de ‘ Consumo de energía del hogar ‘ es un conjunto de datos de series de tiempo multivariable que describe el consumo de electricidad de un solo hogar durante cuatro años.

Los datos se recopilaron entre diciembre de 2006 y noviembre de 2010 y cada minuto se recopilaron observaciones del consumo de energía dentro del hogar.

Es una serie multivariada compuesta por siete variables (además de la fecha y la hora); ellos son:

  • global_active_power : la potencia activa total consumida por el hogar (kilovatios).
  • global_reactive_power : La potencia reactiva total consumida por el hogar (kilovatios).
  • voltaje : Voltaje promedio (voltios).
  • global_intensity : Intensidad de corriente promedio (amperios).
  • sub_metering_1 : Energía activa para cocina (vatios-hora de energía activa).
  • sub_metering_2 : Energía activa para lavandería (vatios-hora de energía activa).
  • sub_metering_3 : Energía activa para sistemas de control climático (vatios-hora de energía activa).

Las energías activa y reactiva se refieren a los dos componentes de la potencia. La potencia activa se refiere a la parte de la potencia que realiza trabajo útil, como la alimentación de dispositivos eléctricos, la generación de calor, la iluminación, etc. Se mide en watts (W) y es la potencia que se contabiliza en la facturación eléctrica.

Por otro lado, la potencia reactiva se refiere a la parte de la potencia que no realiza trabajo útil, sino que está asociada al almacenamiento y liberación de energía en los componentes inductivos y capacitivos de un sistema eléctrico. Se mide en voltiamperios reactivos (VAR) y no contribuye directamente al trabajo útil, pero es necesario para mantener la tensión y el flujo de energía en el sistema.

El factor de potencia, representado por el coseno de fi (θ), es una medida de la eficiencia con la que la energía eléctrica se utiliza en un sistema. Es el coseno del ángulo de desfase entre la corriente y la tensión en un circuito de corriente alterna. Un factor de potencia cercano a 1 indica un uso eficiente de la energía, donde la potencia activa es alta en comparación con la potencia reactiva. Un factor de potencia bajo indica que hay una cantidad significativa de potencia reactiva en relación con la potencia activa

Se puede crear una cuarta variable de submedición restando la suma de tres variables de submedición definidas de la energía activa total de la siguiente manera:

sub_metering_remainder = (global_active_power * 1000 / 60) - (sub_metering_1 + sub_metering_2 + sub_metering_3)

Cargar y preparar conjunto de datos

El conjunto de datos se puede descargar desde el repositorio de UCI Machine Learning como un solo archivo .zip de 20 megabytes: consumo_electricidad_hogar.zip

Descargue el conjunto de datos y descomprímalo en su directorio de trabajo actual. Ahora tendrá el archivo “ home_power_consumption.txt ” que tiene un tamaño de aproximadamente 127 megabytes y contiene todas las observaciones.

Podemos usar la función read_csv() para cargar los datos y combinar las dos primeras columnas en una única columna de fecha y hora que podemos usar como índice.

dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])

A continuación, podemos marcar todos los valores faltantes indicados con un ‘ ‘ carácter con un valor NaN , que es un flotante.

Esto nos permitirá trabajar con los datos como una matriz de valores de punto flotante en lugar de tipos mixtos (menos eficientes).


# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')

También necesitamos completar los valores faltantes ahora que han sido marcados.

Un enfoque muy simple sería copiar la observación a la misma hora del día anterior. Podemos implementar esto en una función llamada fill_missing() que tomará la matriz NumPy de los datos y copiará los valores de hace exactamente 24 horas.

def fill_missing(values):
 one_day = 60 * 24
 for row in range(values.shape[0]):
 for col in range(values.shape[1]):
 if isnan(values[row, col]):
 values[row, col] = values[row - one_day, col]

Podemos aplicar esta función directamente a los datos dentro del DataFrame.

# fill missing
fill_missing(dataset.values)

Ahora podemos crear una nueva columna que contenga el resto de la submedición, utilizando el cálculo de la sección anterior.

values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])


Ahora podemos guardar la versión limpia del conjunto de datos en un nuevo archivo; en este caso, simplemente cambiaremos la extensión del archivo a .csv y guardaremos el conjunto de datos como ‘ home_power_consumption.csv ‘.

# save updated dataset
dataset.to_csv(‘household_power_consumption.csv’)

Uniendo todo esto, el ejemplo completo de cargar, limpiar y guardar el conjunto de datos se enumera a continuación.


# load and clean-up data
from numpy import nan
from numpy import isnan
from pandas import read_csv
from pandas import to_numeric
 
# fill missing values with a value at the same time one day ago
def fill_missing(values):
 one_day = 60 * 24
 for row in range(values.shape[0]):
 for col in range(values.shape[1]):
 if isnan(values[row, col]):
 values[row, col] = values[row - one_day, col]
 
# load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
# fill missing
fill_missing(dataset.values)
# add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
# save updated dataset
dataset.to_csv('household_power_consumption.csv')

Al ejecutar el ejemplo, se crea el nuevo archivo » home_power_consumption.csv » que podemos usar como punto de partida para nuestro proyecto de modelado.

Evaluación del modelo

Encuadre del problema

Hay muchas maneras de aprovechar y explorar el conjunto de datos de consumo de energía del hogar.En este enfoque, usaremos los datos para explorar una pregunta muy específica; eso es: Dado el consumo de energía reciente, ¿cuál es el consumo de energía esperado para la próxima semana? Esto requiere que un modelo predictivo pronostique la potencia activa total para cada día durante los próximos siete días.

Técnicamente, este encuadre del problema se conoce como un problema de pronóstico de serie de tiempo de múltiples pasos, dados los múltiples pasos de pronóstico. Un modelo que hace uso de múltiples variables de entrada puede denominarse modelo de pronóstico de serie de tiempo multivariante de múltiples pasos.

Un modelo de este tipo podría ser útil dentro del hogar en la planificación de gastos. También podría ser útil desde el punto de vista de la oferta para planificar la demanda de electricidad de un hogar específico.

Este marco del conjunto de datos también sugiere que sería útil reducir la muestra de las observaciones por minuto del consumo de energía a los totales diarios. Esto no es obligatorio, pero tiene sentido, dado que estamos interesados ​​en la potencia total por día.

Podemos lograr esto fácilmente usando la función resample() en pandas DataFrame. Llamar a esta función con el argumento ‘ D ‘ permite que los datos cargados indexados por fecha y hora se agrupen por día ( ver todos los alias de compensación ). Luego podemos calcular la suma de todas las observaciones para cada día y crear un nuevo conjunto de datos de consumo de energía diario para cada una de las ocho variables.

El ejemplo completo se muestra a continuación.

from pandas import read_csv
# load the new file
dataset = read_csv('household_power_consumption.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# resample data to daily
daily_groups = dataset.resample('D')
daily_data = daily_groups.sum()
# summarize
print(daily_data.shape)
print(daily_data.head())
# save
daily_data.to_csv('household_power_consumption_days.csv')

Al ejecutar el ejemplo, se crea un nuevo conjunto de datos de consumo de energía total diario y se guarda el resultado en un archivo separado llamado » home_power_conquisition_days.csv «.

Podemos usar esto como el conjunto de datos para ajustar y evaluar modelos predictivos para el marco elegido del problema.

Métrica de evaluación

Un pronóstico estará compuesto por siete valores, uno para cada día de la semana siguiente. Es común con los problemas de pronóstico de varios pasos evaluar cada paso de tiempo pronosticado por separado. Esto es útil por varias razones:

  • Para comentar sobre la aptitud en un plazo de entrega específico (p. ej., +1 día frente a +3 días).
  • Para contrastar modelos en función de sus habilidades en diferentes plazos de entrega (por ejemplo, modelos buenos en +1 día frente a modelos buenos en días +5).

Las unidades de la potencia total son los kilovatios y sería útil tener una métrica de error que también estuviera en las mismas unidades. Tanto el error cuadrático medio (RMSE) como el error absoluto medio (MAE) se ajustan a esta factura, aunque RMSE se usa más comúnmente y se adoptará en este tutorial. A diferencia de MAE, RMSE castiga más los errores de pronóstico.

La métrica de rendimiento para este problema será el RMSE para cada tiempo de entrega desde el día 1 hasta el día 7.

Como atajo, puede ser útil resumir el rendimiento de un modelo utilizando una puntuación única para ayudar en la selección del modelo.

Una puntuación posible que podría usarse sería el RMSE en todos los días de pronóstico.

La función Evaluation_forecasts() a continuación implementará este comportamiento y devolverá el rendimiento de un modelo basado en múltiples pronósticos de siete días.

def evaluate_forecasts(actual, predicted):
 scores = list()
 # calculate an RMSE score for each day
 for i in range(actual.shape[1]):
 # calculate mse
 mse = mean_squared_error(actual[:, i], predicted[:, i])
 # calculate rmse
 rmse = sqrt(mse)
 # store
 scores.append(rmse)
 # calculate overall RMSE
 s = 0
 for row in range(actual.shape[0]):
 for col in range(actual.shape[1]):
 s += (actual[row, col] - predicted[row, col])**2
 score = sqrt(s / (actual.shape[0] * actual.shape[1]))
 return score, scores

Ejecutar la función primero devolverá el RMSE general independientemente del día, luego una matriz de puntajes de RMSE para cada día.

Conjuntos de entrenamiento y prueba

Usaremos los primeros tres años de datos para entrenar modelos predictivos y el último año para evaluar modelos.

Los datos de un conjunto de datos determinado se dividirán en semanas estándar. Son semanas que comienzan en domingo y terminan en sábado.

Esta es una forma realista y útil de usar el encuadre elegido del modelo, donde se puede predecir el consumo de energía para la próxima semana. También es útil con el modelado, donde los modelos se pueden usar para predecir un día específico (por ejemplo, el miércoles) o la secuencia completa.

Dividiremos los datos en semanas estándar, trabajando hacia atrás desde el conjunto de datos de prueba.

El último año de los datos es 2010 y el primer domingo de 2010 fue el 3 de enero. Los datos terminan a mediados de noviembre de 2010 y el último sábado más cercano en los datos es el 20 de noviembre. Esto da 46 semanas de datos de prueba.

La primera y la última fila de datos diarios para el conjunto de datos de prueba se proporcionan a continuación para su confirmación

2010-01-03,2083.4539999999984,191.61000000000055,350992.12000000034,8703.600000000033,3842.0,4920.0,10074.0,15888.233355799992
...
2010-11-20,2197.006000000004,153.76800000000028,346475.9999999998,9320.20000000002,4367.0,2947.0,11433.0,17869.76663959999

Los datos diarios comienzan a finales de 2006.

El primer domingo en el conjunto de datos es el 17 de diciembre, que es la segunda fila de datos. La organización de los datos en semanas estándar proporciona 159 semanas estándar completas para entrenar un modelo predictivo. La función split_dataset() a continuación divide los datos diarios en conjuntos de entrenamiento y prueba y organiza cada uno en semanas estándar.

Se utilizan compensaciones de fila específicas para dividir los datos utilizando el conocimiento del conjunto de datos. Luego, los conjuntos de datos divididos se organizan en datos semanales mediante la función NumPy split()

# split a univariate dataset into train/test sets
def split_dataset(data):
 # split into standard weeks
 train, test = data[1:-328], data[-328:-6]
 # restructure into windows of weekly data
 train = array(split(train, len(train)/7))
 test = array(split(test, len(test)/7))
 return train, test

Podemos probar esta función cargando el conjunto de datos diario e imprimiendo la primera y la última fila de datos tanto del tren como de los conjuntos de prueba para confirmar que cumplen con las expectativas anteriores

El ejemplo de código completo se muestra a continuación.


# split into standard weeks
from numpy import split
from numpy import array
from pandas import read_csv
 
# split a univariate dataset into train/test sets
def split_dataset(data):
 # split into standard weeks
 train, test = data[1:-328], data[-328:-6]
 # restructure into windows of weekly data
 train = array(split(train, len(train)/7))
 test = array(split(test, len(test)/7))
 return train, test
 
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
train, test = split_dataset(dataset.values)
# validate train data
print(train.shape)
print(train[0, 0, 0], train[-1, -1, 0])
# validate test
print(test.shape)
print(test[0, 0, 0], test[-1, -1, 0])


Ejecutar el ejemplo muestra que, de hecho, el conjunto de datos del tren tiene 159 semanas de datos, mientras que el conjunto de datos de prueba tiene 46 semanas.

Podemos ver que la potencia activa total para el tren y el conjunto de datos de prueba para la primera y la última fila coinciden con los datos de las fechas específicas que definimos como los límites de las semanas estándar para cada conjunto


(159, 7, 8)
3390.46 1309.2679999999998
(46, 7, 8)
2083.4539999999984 2197.006000000004

Validación Walk-Forward

Los modelos se evaluarán utilizando un esquema llamado validación de avance .

Aquí es donde se requiere que un modelo haga una predicción de una semana, luego los datos reales de esa semana se ponen a disposición del modelo para que pueda usarse como base para hacer una predicción en la semana siguiente. Esto es tanto realista en cuanto a cómo se puede usar el modelo en la práctica como beneficioso para los modelos, permitiéndoles hacer uso de los mejores datos disponibles.

Podemos demostrar esto a continuación con la separación de los datos de entrada y los datos de salida/predichos.

Input, Predict
[Week1] Week2
[Week1 + Week2] Week3
[Week1 + Week2 + Week3] Week4

El enfoque de validación de avance para evaluar modelos predictivos en este conjunto de datos se implementa a continuación y se llama evaluar_modelo() .

El nombre de una función se proporciona para el modelo como el argumento » model_func «. Esta función es responsable de definir el modelo, ajustar el modelo a los datos de entrenamiento y hacer un pronóstico de una semana.

Los pronósticos hechos por el modelo luego se evalúan contra el conjunto de datos de prueba utilizando la función de evaluación_previsiones() previamente definida .

# evaluate a single model
def evaluate_model(model_func, train, test):
 # history is a list of weekly data
 history = [x for x in train]
 # walk-forward validation over each week
 predictions = list()
 for i in range(len(test)):
 # predict the week
 yhat_sequence = model_func(history)
 # store the predictions
 predictions.append(yhat_sequence)
 # get real observation and add to history for predicting the next week
 history.append(test[i, :])
 predictions = array(predictions)
 # evaluate predictions days for each week
 score, scores = evaluate_forecasts(test[:, :, 0], predictions)
 return score, scores

Una vez que tenemos la evaluación de un modelo, podemos resumir el rendimiento.

La función a continuación denominada resume_scores() mostrará el rendimiento de un modelo en una sola línea para facilitar la comparación con otros modelos.


# summarize scores
def summarize_scores(name, score, scores):
 s_scores = ', '.join(['%.1f' % s for s in scores])
 print('%s: [%.3f] %s' % (name, score, s_scores))

Ahora tenemos todos los elementos para comenzar a evaluar modelos predictivos en el conjunto de datos.

Análisis de autocorrelación

La correlación estadística resume la fuerza de la relación entre dos variables.

Podemos suponer que la distribución de cada variable se ajusta a una distribución gaussiana (curva de campana). Si este es el caso, podemos usar el coeficiente de correlación de Pearson para resumir la correlación entre las variables.

El coeficiente de correlación de Pearson es un número entre -1 y 1 que describe una correlación negativa o positiva respectivamente. Un valor de cero indica que no hay correlación.

Podemos calcular la correlación de las observaciones de series de tiempo con observaciones con pasos de tiempo anteriores, llamados retrasos. Debido a que la correlación de las observaciones de la serie temporal se calcula con valores de la misma serie en momentos anteriores, esto se denomina correlación serial o autocorrelación.

Una gráfica de la autocorrelación de una serie de tiempo por retraso se denomina función de autocorrelación , o el acrónimo ACF. Esta gráfica a veces se denomina correlograma o gráfica de autocorrelación.

Una función de autocorrelación parcial o PACF es un resumen de la relación entre una observación en una serie de tiempo con observaciones en pasos de tiempo anteriores con las relaciones de las observaciones intermedias eliminadas.

La autocorrelación para una observación y una observación en un paso de tiempo anterior se compone tanto de la correlación directa como de las correlaciones indirectas. Estas correlaciones indirectas son una función lineal de la correlación de la observación, con observaciones en pasos de tiempo intermedios.

Son estas correlaciones indirectas las que la función de autocorrelación parcial busca eliminar. Sin entrar en matemáticas, esta es la intuición de la autocorrelación parcial.

Podemos calcular gráficos de autocorrelación y autocorrelación parcial utilizando las funciones de modelos estadísticos plot_acf() y plot_pacf() respectivamente.

Para calcular y trazar la autocorrelación, debemos convertir los datos en una serie de tiempo univariante. Específicamente, la energía total diaria observada consumida.

La función to_series() a continuación tomará los datos multivariados divididos en ventanas semanales y devolverá una sola serie temporal univariada.

def to_series(data):
 # extract just the total power from each week
 series = [week[:, 0] for week in data]
 # flatten into a single series
 series = array(series).flatten()
 return series

Podemos llamar a esta función para el conjunto de datos de entrenamiento preparado.

Primero, se debe cargar el conjunto de datos de consumo de energía diario.

# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])


Luego, el conjunto de datos debe dividirse en conjuntos de entrenamiento y prueba con la estructura de ventana de semana estándar.

# split into train and test
train, test = split_dataset(dataset.values)

A continuación, se puede extraer una serie de tiempo univariante del consumo de energía diario del conjunto de datos de entrenamiento.

# convert training data into a series
series = to_series(train)


Entonces podemos crear una sola figura que contenga un gráfico ACF y PACF. Se puede especificar el número de pasos de tiempo de retraso. Arreglaremos esto para que sea un año de observaciones diarias, o 365 días.

# plots
pyplot.figure()
lags = 365
# acf
axis = pyplot.subplot(2, 1, 1)
plot_acf(series, ax=axis, lags=lags)
# pacf
axis = pyplot.subplot(2, 1, 2)
plot_pacf(series, ax=axis, lags=lags)
# show plot
pyplot.show()

El ejemplo completo se muestra a continuación.

Esperaríamos que la energía consumida mañana y la próxima semana dependa de la energía consumida en los días anteriores. Como tal, esperaríamos ver una fuerte señal de autocorrelación en los gráficos ACF y PACF.

# acf and pacf plots of total power
from numpy import split
from numpy import array
from pandas import read_csv
from matplotlib import pyplot
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.graphics.tsaplots import plot_pacf
 
# split a univariate dataset into train/test sets
def split_dataset(data):
 # split into standard weeks
 train, test = data[1:-328], data[-328:-6]
 # restructure into windows of weekly data
 train = array(split(train, len(train)/7))
 test = array(split(test, len(test)/7))
 return train, test
 
# convert windows of weekly multivariate data into a series of total power
def to_series(data):
 # extract just the total power from each week
 series = [week[:, 0] for week in data]
 # flatten into a single series
 series = array(series).flatten()
 return series
 
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# split into train and test
train, test = split_dataset(dataset.values)
# convert training data into a series
series = to_series(train)
# plots
pyplot.figure()
lags = 365
# acf
axis = pyplot.subplot(2, 1, 1)
plot_acf(series, ax=axis, lags=lags)
# pacf
axis = pyplot.subplot(2, 1, 2)
plot_pacf(series, ax=axis, lags=lags)
# show plot
pyplot.show()

Ejecutar el ejemplo crea una sola figura con gráficos ACF y PACF.

Las tramas son muy densas y difíciles de leer. Sin embargo, es posible que podamos ver un patrón de autorregresión familiar.

También podríamos ver algunas observaciones de retraso significativas dentro de un año. La investigación adicional puede sugerir un componente de autocorrelación estacional, lo que no sería un hallazgo sorprendente.

Podemos ampliar la gráfica y cambiar el número de observaciones de retraso de 365 a 50.

lags = 50

Volver a ejecutar el ejemplo de código con los resultados de este cambio es una versión ampliada de los gráficos con mucho menos desorden.

Podemos ver claramente un patrón de autorregresión familiar en las dos gráficas. Este patrón se compone de dos elementos:

  • ACF : una gran cantidad de observaciones de retraso significativas que se degradan lentamente a medida que aumenta el retraso.
  • PACF : algunas observaciones de retraso significativas que caen abruptamente a medida que aumenta el retraso.

La gráfica ACF indica que hay un fuerte componente de autocorrelación, mientras que la gráfica PACF indica que este componente es distinto para las primeras aproximadamente siete observaciones de retraso.

Esto sugiere que un buen modelo inicial sería un AR(7); que es un modelo de autorregresión con siete observaciones de retraso utilizadas como entrada.

Podemos desarrollar un modelo de autorregresión para series univariadas de consumo diario de energía.

La biblioteca de Statsmodels proporciona múltiples formas de desarrollar un modelo AR, como usar las clases AR, ARMA, ARIMA y SARIMAX.

Usaremos la implementación ARIMA ya que permite una fácil expansión en diferenciación y promedio móvil.

En primer lugar, los datos históricos compuestos por semanas de observaciones anteriores deben convertirse en una serie de tiempo univariada de consumo de energía diario. Podemos usar la función to_series() desarrollada en la sección anterior.

#convert history into a univariate series

series = to_series(history)


A continuación, se puede definir un modelo ARIMA pasando argumentos al constructor de la clase ARIMA.

Especificaremos un modelo AR(7), que en notación ARIMA es ARIMA(7,0,0).

#define the model

model = ARIMA(series, order=(7,0,0))


A continuación, el modelo se puede ajustar a los datos de entrenamiento.

#fit the model

model_fit = model.fit()


Ahora que el modelo se ha ajustado, podemos hacer una predicción.

Se puede hacer una predicción llamando a la función predict() y pasándole un intervalo de fechas o índices relativos a los datos de entrenamiento. Usaremos índices comenzando con el primer paso de tiempo más allá de los datos de entrenamiento y extendiéndolo seis días más, dando un total de un período de pronóstico de siete días más allá del conjunto de datos de entrenamiento.

#make forecast

yhat = model_fit.predict(len(series), len(series)+6)


Podemos envolver todo esto en una función a continuación llamada arima_forecast() que toma el historial

y devuelve un pronóstico de una semana.

arima forecast

def arima_forecast(history):
# convert history into a univariate series
series = to_series(history)
# define the model
model = ARIMA(series, order=(7,0,0))
# fit the model
model_fit = model.fit()
# make forecast
yhat = model_fit.predict(len(series), len(series)+6)
return yhat


Esta función se puede utilizar directamente en el arnés de prueba descrito anteriormente.

El ejemplo completo se muestra a continuación.

# arima forecast
from math import sqrt
from numpy import split
from numpy import array
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from matplotlib import pyplot
from statsmodels.tsa.arima.model import ARIMA
 
# split a univariate dataset into train/test sets
def split_dataset(data):
 # split into standard weeks
 train, test = data[1:-328], data[-328:-6]
 # restructure into windows of weekly data
 train = array(split(train, len(train)/7))
 test = array(split(test, len(test)/7))
 return train, test
 
# evaluate one or more weekly forecasts against expected values
def evaluate_forecasts(actual, predicted):
 scores = list()
 # calculate an RMSE score for each day
 for i in range(actual.shape[1]):
 # calculate mse
 mse = mean_squared_error(actual[:, i], predicted[:, i])
 # calculate rmse
 rmse = sqrt(mse)
 # store
 scores.append(rmse)
 # calculate overall RMSE
 s = 0
 for row in range(actual.shape[0]):
 for col in range(actual.shape[1]):
 s += (actual[row, col] - predicted[row, col])**2
 score = sqrt(s / (actual.shape[0] * actual.shape[1]))
 return score, scores
 
# summarize scores
def summarize_scores(name, score, scores):
 s_scores = ', '.join(['%.1f' % s for s in scores])
 print('%s: [%.3f] %s' % (name, score, s_scores))
 
# evaluate a single model
def evaluate_model(model_func, train, test):
 # history is a list of weekly data
 history = [x for x in train]
 # walk-forward validation over each week
 predictions = list()
 for i in range(len(test)):
 # predict the week
 yhat_sequence = model_func(history)
 # store the predictions
 predictions.append(yhat_sequence)
 # get real observation and add to history for predicting the next week
 history.append(test[i, :])
 predictions = array(predictions)
 # evaluate predictions days for each week
 score, scores = evaluate_forecasts(test[:, :, 0], predictions)
 return score, scores
 
# convert windows of weekly multivariate data into a series of total power
def to_series(data):
 # extract just the total power from each week
 series = [week[:, 0] for week in data]
 # flatten into a single series
 series = array(series).flatten()
 return series
 
# arima forecast
def arima_forecast(history):
 # convert history into a univariate series
 series = to_series(history)
 # define the model
 model = ARIMA(series, order=(7,0,0))
 # fit the model
 model_fit = model.fit()
 # make forecast
 yhat = model_fit.predict(len(series), len(series)+6)
 return yhat
 
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# split into train and test
train, test = split_dataset(dataset.values)
# define the names and functions for the models we wish to evaluate
models = dict()
models['arima'] = arima_forecast
# evaluate each model
days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']
for name, func in models.items():
 # evaluate and get scores
 score, scores = evaluate_model(func, train, test)
 # summarize scores
 summarize_scores(name, score, scores)
 # plot scores
 pyplot.plot(days, scores, marker='o', label=name)
# show plot
pyplot.legend()
pyplot.show()


Ejecutar el ejemplo primero imprime el rendimiento del modelo AR(7) en el conjunto de datos de prueba.

Nota : Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y compare el resultado promedio.

Podemos ver que el modelo alcanza el RMSE general de alrededor de 381 kilovatios.

Este modelo tiene habilidad en comparación con los modelos de pronóstico ingenuos, como un modelo que pronostica la semana siguiente utilizando observaciones del mismo período hace un año que logró un RMSE general de aproximadamente 465 kilovatios.

arima: [381.636] 393.8, 398.9, 357.0, 377.2, 393.9, 306.1, 432.2

También se crea un diagrama de líneas del pronóstico, que muestra el RMSE en kilovatios para cada uno de los siete tiempos de anticipación del pronóstico.

Podemos ver un patrón interesante.

Podríamos esperar que los plazos de entrega más tempranos sean más fáciles de pronosticar que los plazos de entrega posteriores, ya que el error en cada uno de los plazos de entrega sucesivos se compone.

En cambio, vemos que el viernes (tiempo de entrega +6) es el más fácil de pronosticar y el sábado (tiempo de entrega +7) es el más difícil de pronosticar. También podemos ver que los plazos de entrega restantes tienen un error similar en el rango medio a alto de 300 kilovatios.

Extensiones

En esta sección se enumeran algunas ideas para ampliar este articulo que tal vez desee explorar.

  • Sintoniza ARIMA . Los parámetros del modelo ARIMA no fueron ajustados. Explore o busque un conjunto de parámetros ARIMA (q, d, p) para ver si el rendimiento se puede mejorar aún más.
  • Explora AR estacional . Explore si el rendimiento del modelo AR se puede mejorar al incluir elementos de autorregresión estacional. Esto puede requerir el uso de un modelo SARIMA.
  • Explore la preparación de datos . El modelo se ajustó directamente a los datos sin procesar. Explore si la estandarización o la normalización o incluso las transformaciones de potencia pueden mejorar aún más la habilidad del modelo AR.

Fuente: https://machinelearningmastery.com/how-to-develop-an-autoregression-forecast-model-for-household-electricity-consumption/

Segmentation fault (core dumped)


«Fallo de segmentación» significa que ha intentado acceder a una memoria a la que no tiene acceso. Si recibe este error al ejecutar un script en Python en una maquina Linux , significa que el intérprete de Python se ha bloqueado. Sólo hay unas pocas razones por las que esto puede suceder:

  • Está usando un módulo de extensión de terceros escrito en C, y ese módulo de extensión ha fallado.
  • Está (directa o indirectamente) usando el módulo incorporado ctypes, y llamando a código externo que falla.
  • Hay algo mal en su instalación de Python.
  • Ha descubierto un bug en Python que debería reportar.

La primera es, con mucho, la más común. Si su código maneja una instancia de algún objeto de algún módulo de extensión de terceros, puede que quiera mirar la documentación. A menudo, cuando los módulos C se bloquean, es porque estás haciendo algo que no es válido, o al menos poco común y no probado. Pero si es su «fallo» en ese sentido o no – eso no importa. El módulo debería lanzar una excepción Python que pueda depurar, en lugar de bloquearse. Por lo tanto, probablemente debería informar de un error a quien escribió la extensión. Pero mientras tanto, en lugar de esperar meses a que se solucione el error y salga una nueva versión, tiene que averiguar qué hizo que provocó el fallo, y si hay alguna manera diferente de hacer lo que quiere o cambiar a otra biblioteca.

Por otro lado, dado que estás leyendo e imprimiendo datos desde otro lugar, es posible que su intérprete de Python sólo leyera la línea «Segmentation fault (core dumped)» e imprimiera fielmente lo que leyó. En ese caso, algún otro programa aguas arriba presumiblemente se estrelló. (Incluso es posible que nadie se haya bloqueado: si obtuviera esta página de la web y la imprimiera, obtendría la misma línea, ¿verdad?) .

Si no está seguro de cuál es el caso (y no quiere aprender a hacer gestión de procesos, inspección de core-files o depuración a nivel de C hoy mismo), hay una forma fácil de probarlo: después de la línea print añada una línea que diga print «Y estoy bien». Si ve eso después de la línea de fallo de segmentación, entonces Python no se estrelló, alguien más lo hizo. Si no lo ve, entonces probablemente es Python el que se ha colgado.

Una opción que puede ayudar para evitar errores de memoria insuficiente en aplicaciones es agregar mas espacio de intercambio a su servidor ( o swapping).

El Swap es una porción de almacenamiento de las unidades de disco duro que se reserva para el sistema operativo, a fin de almacenar de forma temporal datos que ya no es posible contener en la memoria RAM. Esto le permite aumentar la cantidad de información que su servidor puede conservar en su memoria funcional, con algunas advertencias. El espacio de intercambio en el disco duro se utilizará principalmente cuando ya no haya espacio suficiente en la RAM para mantener datos de aplicación en uso.

La información escrita en el disco será considerablemente más lenta que la información conservada en la RAM, aunque el sistema operativo preferiría seguir ejecutando datos de aplicación en la memoria y utilizar el espacio de intercambio para los datos antiguos. En general, disponer de espacio de intercambio como una segunda opción para cuando la RAM de su sistema se agote puede funcionar como una buena red de seguridad contra excepciones por falta de memoria en sistemas con almacenamiento no SSD disponible.

Si bien, en general, el intercambio se recomienda para sistemas que utilizan discos duros giratorios tradicionales, utilizarlo con SSD puede causar problemas de degradación de hardware con el paso del tiempo. Debido a esta consideración, no SUELE recomendarse habilitar el intercambio que utilice almacenamiento SSD

Veremos pues para intentar resolver el problema , crear una partición SWAP en Ubuntu, Linux Mint y derivados.


MÉTODO 1

Línea de comandos desde el Terminal (¡La forma más rápida!)


PASO 1:

El primer paso es comprobar si por casualidad hay alguna partición SWAP ya creada en su servidor:

sudo swapon --show

Introduzca su contraseña de root.

Si no ve ninguna salida, significa que el SWAP no existe.

PASO 2:

A continuación, veamos la estructura actual de particiones del disco duro de su ordenador:

df -h

El dispositivo con / en la columna Mounted on es nuestro disco en este caso. En este ejemplo, contamos con bastante espacio disponible (solo 16 GB utilizados).

Aunque existen varias opiniones acerca de la dimensión adecuada de un espacio de intercambio, depende de sus preferencias personales y de los requisitos de su aplicación. En general, una cantidad idéntica o equivalente al doble de la cantidad de RAM en su sistema es un buen punto de partida. Otra buena regla general es que cualquier cosa que supere los 4G de intercambio es probablemente innecesaria si lo está utilizando como una segunda opción a la falta de memoria RAM.


PASO 3:

Antes de iniciar los cambios deshabilite el uso de swap:

sudo swapoff -a

Hecho esto si repite el comando anterior verá que ya no hay swapping

PASO 4:

Ahora es el momento de crear el archivo SWAP. Asegúrese de que tiene espacio suficiente en el disco duro, porque lo demás es una cuestión de preferencia en cuanto al tamaño de SWAP que necesita.

Lo recomendable es si tiene un máximo de 4GB de RAM poner el doble de RAM para el SWAP (8GB para el SWAP) pero para ordenadores con más de 4GB se recomienda el mismo número de RAM para SWAP más 2GB. Por ejemplo si dispone de 8GB, poner 8GB + 2GB, totalizando 10GB para SWAP. Pero puede elegir libremente siempre que disponga espacio en el disco para albergarla.

Una opción posible es esta:

sudo dd if=/dev/zero of=/swapfile bs=50G count=10 status=progress

Otra posibilidad mas habitual es crearlo con el siguiente comando:

sudo fallocate -l 50G /swapfile

Podemos verificar que la cantidad correcta de espacio ya esta reservada ingresando lo siguiente:

ls -lh /swapfile


PASO 5:

El archivo SWAP ya está creado. Démosle permisos de root.

Ahora que tenemos un archivo del tamaño correcto disponible, debemos convertir esto en espacio de intercambio.

Primero, debemos restringir los permisos del archivo para que solo los usuarios con privilegios de root puedan leer el contenido. Esto impide que usuarios comunes puedan acceder al archivo, lo que tendría implicaciones de seguridad significativas.

sudo chmod 600 /swapfile

Verifique el cambio de permisos introduciendo lo siguiente:

ls -lh /swapfile

Como puede ver, los indicadores de lectura y escritura solo están habilitados para el root user.

Ahora por fin pues podemos marcar el archivo como espacio de intercambio .


PASO 6:

Marque el archivo como espacio SWAP:

sudo mkswap /swapfile

Puede que le de un error de montaje :

Si es asi , deberemos liberar el swapp nuevamente mediante el comando:

sudo  swapoff -a

Una vez vuelva a ejecutar el comando sudo mkswap /swapfile sin errores, puede habilitar nuevamente el swapping mediante el comando sudo swapon -a y ya puede verificar que el intercambio esté disponible ingresando lo siguiente:

sudo swapon --show


PASO 7:

Después de marcar el archivo, podemos habilitar el archivo de intercambio, lo cual permite que nuestro sistema empiece a utilizarlo asi que finalmente habilite pues el SWAP.

sudo swapon /swapfile


PASO 8:

Ahora puede comprobar usando el mismo comando swapon si el SWAP está creado.

sudo swapon --show


PASO 9:

Compruebe también de nuevo la estructura final de la partición.

free -h


PASO 10:

Una vez que todo está configurado, puede configurar el archivo SWAP como permanente, de lo contrario perderá el SWAP después de reiniciar.

Si quiere que los cambio sean definitivos ejecute este comando:

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab


Listo, ahora salga de la terminal. Puede comprobar el estado de SWAP en la utilidad System Monitor.

Nuestro intercambio se ha configurado con éxito y nuestro sistema operativo comenzará a utilizarlo según sea necesario.

Paso 11:

Ajustar sus ajustes de intercambio.

Existen algunas opciones que puede configurar y que tendrán efecto en el rendimiento de su sistema al gestionar un intercambio. La primera opcion es ajustar la propiedad swappiness y la segunda es ajustar la presion de la cache .

Ajuste de la propiedad “”swappiness”

El parámetro swappiness configura la frecuencia con la cual su sistema intercambia datos de la RAM al espacio de intercambio. Este es un valor entre 0 y 100 que representa un porcentaje. Con valores cercanos a cero, el núcleo no intercambiará datos en el disco a menos que sea absolutamente necesario. Recuerde que las interacciones con el archivo de intercambio son “exigentes” puesto que tardan mucho más que las interacciones con la RAM y pueden reducir el rendimiento considerablemente. Indicar al sistema que no dependa del intercambio en demasía hará que sea más rápido.

En un esfuerzo por mantener más espacio de RAM libre, se intentará verter al intercambio más datos de los valores cercanos a 100. Dependiendo del perfil de memoria de sus aplicaciones o para qué está utilizando su servidor, esto podría ser mejor en algunos casos.

Podemos ver el valor de intercambiabilidad actual al ingresar lo siguiente:

cat /proc/sys/vm/swappiness

Para un escritorio, un ajuste de intercambiabilidad de 60 no es un mal valor. Para un servidor, posiblemente le convenga acercarlo a 0.Podemos fijar la capacidad de intercambio en un valor diferente con el comando sysctl.

Por ejemplo, para establecer la intercambiabilidad en 10, podríamos ingresar lo siguiente:

sudo sysctl vm.swappiness=10

Esta configuración persistirá hasta el próximo reinicio. Podemos establecer este valor automáticamente en el reinicio añadiendo la línea a nuestro archivo /etc/sysctl.conf:

sudo nano /etc/sysctl.conf

En la parte inferior, puede añadir:

vm.swappiness=10

Guarde y cierre el archivo cuando haya terminado.

Ajustar la configuración de presión de caché

Otro valor relacionado que podría querer modificar es el vfs_cache_pressure. Esta ajuste determina en qué medida el sistema elegirá almacenar en caché información de inodos y entradas de directorio en lugar de otros datos.

Básicamente, estos son datos de acceso sobre el sistema de archivos. Generalmente, la búsqueda de esto supone costos muy altos y se solicita con mucha frecuencia, por lo cual el almacenamiento en caché . Puede ver el valor actual consultando el sistema de archivos proc nuevamente:

cat /proc/sys/vm/vfs_cache_pressure

Dado que está configurado actualmente, nuestro sistema elimina la información de inodo de la memoria caché demasiado rápido. Podemos establecer esto en un parámetro más conservador como 50 al ingresar:

sudo sysctl vm.vfs_cache_pressure=50

Una vez más, esto solo es válido para nuestra sesión actual. Podemos cambiar eso añadiéndolo en nuestro archivo de configuración como hicimos con nuestro ajuste de intercambiabilidad:

sudo nano /etc/sysctl.conf

En la parte inferior, añada la línea que especifique su nuevo valor:

vm.vfs_cache_pressure=50

Guarde y cierre el archivo cuando haya terminado.

PROBLEMAS PERSISTENTYES

A pesar de los cambios realizados ampliando el swapping hay varias razones por las cuales Ubuntu puede estar utilizando memoria física en lugar de la memoria de intercambio (swap):

  1. Tamaño de la memoria de intercambio: Si el tamaño de la memoria de intercambio es demasiado pequeño, es posible que el sistema no necesite utilizarla, ya que tiene suficiente memoria física disponible. Puede verificar el tamaño de la memoria de intercambio ejecutando el siguiente comando:cat /proc/sys/vm/swappiness Si el valor devuelto es 0, significa que la memoria de intercambio está desactivada. Si desea habilitarla, puede modificar el archivo /etc/sysctl.conf y ajustar el valor de vm.swappiness a un número mayor que 0 (por ejemplo, 10). Luego reinicie su sistema para que los cambios surtan efecto.
  2. Uso eficiente de la memoria: El kernel de Linux intenta utilizar la memoria física de manera eficiente y mantener la mayor cantidad posible de datos en la memoria principal para un acceso más rápido. Si hay suficiente memoria RAM disponible, es posible que el sistema no necesite hacer uso de la memoria de intercambio.
  3. Opciones de configuración del sistema: Es posible que la configuración del sistema esté optimizada para priorizar el uso de la memoria física en lugar de la memoria de intercambio. Puede verificar y ajustar la configuración del kernel a través del archivo /etc/sysctl.conf y los parámetros relacionados con la memoria y el intercambio.

En resumen, si su sistema tiene suficiente memoria RAM disponible y no está haciendo un uso intensivo de la CPU, es posible que no se utilice la memoria de intercambio. Esto no es necesariamente un problema, ya que la memoria física es más rápida que la memoria de intercambio y proporciona un mejor rendimiento. Sin embargo, si experimentas problemas de rendimiento o tu sistema se queda sin memoria física, puede ser necesario ajustar la configuración de la memoria de intercambio para garantizar un funcionamiento adecuado.

MÉTODO 2

Modo GUI usando GParted: Si quiere ir directamente a través de la interfaz gráfica puede usar la utilidad Gparted.

Gparted es un gestor de particiones gratuito y de código abierto para gestionar gráficamente las particiones. Puedes añadir, modificar o eliminar tus particiones con sólo pulsar un botón. Esta es una herramienta excelente para aquellos que no quieren gestionar sus discos a través de utilidades de línea de comandos. Puede ser fácilmente instalado y utilizado en casi todas las plataformas (tanto de 32 bits y 64 bits) como Windows, Linux o Mac OS X.

Si está utilizando una versión mínima de Ubuntu Linux, entonces la aplicación Gparted no estará presente en su sistema por defecto, por lo tanto, tenemos que instalarlo manualmente. Utilice los siguientes comandos.

1. Actualizar la caché del repositorio APT
Primero vamos a ejecutar el comando de actualización del sistema que vaciará y reconstruirá la caché del repositorio del gestor de paquetes APT del sistema. Además, instala, si hay alguna actualización disponible.

sudo apt update

2-Comando para instalar Gparted en Ubuntu 20.04 | 22.04
Como los paquetes para instalar Gparted está disponible a través del repositorio por defecto de Ubuntu 20.04 y 22.04 Linux, por lo tanto, no es necesario añadir ningún tipo de repositorio de terceros en absoluto. Sólo tiene que utilizar el gestor de paquetes APT comando que se indica a continuación.

sudo apt install gparted -y

4. Menú GParted
El uso de GParted es relativamente sencillo, ya que el programa se maneja completamente a través de una ventana. En esta ventana, puede ver la división del disco duro actualmente seleccionado. Se puede acceder a todas las opciones y acciones posibles a través de la barra de menú. Las opciones o acciones que no están disponibles son automáticamente ocultadas por GParted.

En el menú «GParted» puede seleccionar la unidad a particionar a través del elemento «Dispositivos» (si tiene varios discos duros), a través de «Actualizar Dispositivos» se leen de nuevo las unidades actualmente conectadas. La opción de menú «Ver» «Soporte de sistemas de archivos» muestra en una matriz qué acciones (ampliar, reducir, crear, etc.) son posibles con los diferentes tipos de sistemas de archivos soportados.

La opción «Editar» contiene tres acciones: «Deshacer» permite deshacer las últimas acciones realizadas, o «Borrar» para eliminar todas las acciones; los cambios realizados se llevan a cabo en la partición seleccionada mediante «Aplicar todas las operaciones».

GParted guarda primero todos los cambios en un buffer y sólo los ejecuta cuando se pulsa «Aplicar todas las operaciones». Una vez que se ha ejecutado «Aplicar todas las operaciones», los cambios no se pueden deshacer. Por lo tanto, «Aplicar todas las operaciones» sólo debe seleccionarse si se está seguro de los cambios. En el peor de los casos, se perderán todos los datos almacenados en el disco duro.

Mitos sobre SWAP

Igual que ocurre con la paginación de Windows, hay muchos mitos y muchas leyendas que giran en torno al intercambio de Linux. Y uno de los más graves es, desde luego, gira en torno a su funcionamiento. Generalmente se asocia el intercambio a que, cuando se alcanza cierto umbral, se deja de usar la RAM y se empieza a usar este SWAP. Es un concepto muy erróneo que, como se ha extendido a lo largo de los años, se cree que es verdad. Pero está muy lejos de la realidad. Y menos si configuramos correctamente el Swapiness.

Esto quiere decir que no deberíamos tener miedo de usar esta función de la que os hemos estado hablando, especialmente en equipos que estén limitados en la memoria RAM disponible. La mayoría de las ocasiones ganaremos en rendimiento al tiempo que evitamos posibles bloqueos en el funcionamiento de algunos programas.

Otro mito (a medias) es que perdemos rendimiento en el PC. Este, en el pasado, era cierto, ya que escribir en los discos duros era infinitamente más lento que hacerlo en la RAM. A día de hoy, con los SSD NVMe PCIe 4.0, aunque puede haber pérdida de rendimiento, esta es casi inapreciable.

¿Y qué pasa con que se rompen los discos duros y, sobre todo, los SSD? Los discos duros jamás se han roto por usar el intercambio. Las primeras unidades de SSD tenían ciclos de escritura muy cortos y limitados, y las escrituras del SWAP es cierto que podían llegar a resentirlos. Hoy en día, los ciclos de escritura y lectura los hacen casi eternos, por lo que no va a haber el menor problema en ese sentido.

Esto quiere decir que nos podemos olvidar de esos mitos y leyendas que afirman que el uso de esta funcionalidad en Linux podría dañar las unidades de disco sólido que tengamos instaladas en el equipo.