In this article, I will show how to use Recurrence Plots to describe different types of time series. We will look at various simulated time series with 500 data points. We can visually characterize a time series by visualizing its recurrence plot and comparing it to other known recurrence plots of different time series.
Recursive graph
Recurrence Plots (RP) is a method for visualizing and analyzing time series or dynamic systems. It converts time series into graphical representations in order to analyze recurring patterns and structures in time series. Recurrence Plots are useful especially when there are periodicity, recurring events, or correlation structures in the time series data.
The basic principle of Recurrence Plots is to measure the similarity between points in a time series. If the distance between two time points is less than a given threshold, a point is plotted in the Recurrence Plot indicating that there is recurrence between the two time points. These points form an image on a two-dimensional plane.
import numpy as np import matplotlib.pyplot as plt def recurrence_plot(data, threshold=0.1): """ Generate a recurrence plot from a time series. :param data: Time series data :param threshold: Threshold to determine recurrence :return: Recurrence plot """ # Calculate the distance matrix N = len(data) distance_matrix = np.zeros((N, N)) for i in range(N): for j in range(N): distance_matrix[i, j] = np.abs(data[i] - data[j]) # Create the recurrence plot recurrence_plot = np.where(distance_matrix <= threshold, 1, 0) return recurrence_plot
The code above creates a binary distance matrix that takes the value of time series i and j to be 1 if their values are within 0.1 (threshold), and 0 otherwise. The resulting matrix can be viewed as an image.
White noise
Next we will visualize white noise. First, we need to create a series of simulated white noise:
# Set a seed for reproducibility np.random.seed(0) # Generate 500 data points of white noise white_noise = np.random.normal(size=500) # Plot the white noise time series plt.figure(figsize=(10, 6)) plt.plot(white_noise, label='White Noise') plt.title('White Noise Time Series') plt.xlabel('Time') plt.ylabel('Value') plt.legend() plt.grid(True) plt.show()
Recursion plots provide an interesting visualization of this white noise. For any kind of white noise, the plot looks the same:
# Generate and plot the recurrence plot recurrence = recurrence_plot(white_noise, threshold=0.1) plt.figure(figsize=(8, 8)) plt.imshow(recurrence, cmap='binary', origin='lower') plt.title('Recurrence Plot') plt.xlabel('Time') plt.ylabel('Time') plt.colorbar(label='Recurrence') plt.show()
A noisy process can be visualized. You can see that the diagonal lines in the picture are always black.
Random walk
Next let’s see what a Random Walk looks like:
# Generate 500 data points of a random walk steps = np.random.choice([-1, 1], size=500) # Generate random steps: -1 or 1 random_walk = np.cumsum(steps) # Cumulative sum to generate the random walk # Plot the random walk time series plt.figure(figsize=(10, 6)) plt.plot(random_walk, label='Random Walk') plt.title('Random Walk Time Series') plt.xlabel('Time') plt.ylabel('Value') plt.legend() plt.grid(True) plt.show()
# Generate and plot the recurrence plot recurrence = recurrence_plot(random_walk, threshold=0.1) plt.figure(figsize=(8, 8)) plt.imshow(recurrence, cmap='binary', origin='lower') plt.title('Recurrence Plot') plt.xlabel('Time') plt.ylabel('Time') plt.colorbar(label='Recurrence') plt.show()
SARIMA
Simulated data of SARIMA(4,1,4)(1,0,0,12)
from statsmodels.tsa.statespace.sarimax import SARIMAX #Define SARIMA parameters p, d, q = 4, 1, 4 # Non-seasonal order P, D, Q, s = 1, 0, 0, 12 # Seasonal order # Simulate data model = SARIMAX(np.random.randn(100), order=(p, d, q), seasonal_order=(P, D, Q, s), trend='ct') fit = model.fit(disp=False) # Fit the model to random data to get parameters simulated_data = fit.simulate(nsimulations=500) # Plot the simulated time series plt.figure(figsize=(10, 6)) plt.plot(simulated_data, label=f'SARIMA({p},{d},{q})({P},{D},{Q},{s})') plt.title('Simulated Time Series from SARIMA Model') plt.xlabel('Time') plt.ylabel('Value') plt.legend() plt.grid(True) plt.show()
recurrence = recurrence_plot(simulated_data, threshold=0.1) plt.figure(figsize=(8, 8)) plt.imshow(recurrence, cmap='binary', origin='lower') plt.title('Recurrence Plot') plt.xlabel('Time') plt.ylabel('Time') plt.colorbar(label='Recurrence') plt.show()
Chaotic data
def logistic_map(x, r): """Logistic map function.""" return r * x * (1 - x) #Initialize parameters N = 500 #Number of data points r = 3.9 # Parameter r, set to a value that causes chaotic behavior x0 = np.random.rand() # Initial value # Generate chaotic time series data chaotic_data = [x0] for _ in range(1, N): x_next = logistic_map(chaotic_data[-1], r) chaotic_data.append(x_next) # Plot the chaotic time series plt.figure(figsize=(10, 6)) plt.plot(chaotic_data, label=f'Logistic Map (r={r})') plt.title('Chaotic Time Series') plt.xlabel('Time') plt.ylabel('Value') plt.legend() plt.grid(True) plt.show()
recurrence = recurrence_plot(chaotic_data, threshold=0.1) plt.figure(figsize=(8, 8)) plt.imshow(recurrence, cmap='binary', origin='lower') plt.title('Recurrence Plot') plt.xlabel('Time') plt.ylabel('Time') plt.colorbar(label='Recurrence') plt.show()
S&P 500 Index
As a final example, let’s look at real S&P 500 data from October 28, 2013, to October 27, 2023:
import pandas as pd df = pd.read_csv('standard_and_poors_500_idx.csv', parse_dates=True) df['Date'] = pd.to_datetime(df['Date']) df.set_index('Date', inplace = True) df.drop(columns = ['Open', 'High', 'Low'], inplace = True) df.plot() plt.title('S &P 500 Index - 10/28/2013 to 10/27/2023') plt.ylabel('S &P 500 Index') plt.xlabel('Date');
recurrence = recurrence_plot(df['Close/Last'], threshold=10) plt.figure(figsize=(8, 8)) plt.imshow(recurrence, cmap='binary', origin='lower') plt.title('Recurrence Plot') plt.xlabel('Time') plt.ylabel('Time') plt.colorbar(label='Recurrence') plt.show()
Choosing an appropriate similarity threshold is a key step in recursive graph analysis. A smaller threshold will result in more repeating patterns, while a larger threshold will result in fewer repeating patterns. The choice of threshold usually needs to be adjusted based on the characteristics of the data and the analysis goals.
Here we had to adjust the threshold, and we ended up with a value of 10, which allowed for greater contrast. The recurrence graph above looks a lot like a mix of a random walk recurrence graph and random chaotic data.
Summary
In this article, we introduced recursive graphs and how to create them using Python. Recursion graphs give us a way to visually represent time series graphs. Recurrence plots are a powerful tool for revealing structure and patterns in time series, especially those with periodic, repetitive, or complex structure. Through visualization and feature extraction, researchers can better understand time series data and perform further analysis.
Various features can be extracted from the recurrence graph for further analysis. These features can include the distribution of repeating points, Lempel-Ziv complexity, longest diagonal length, etc.
Recursive graphs are widely used in many fields, including time series analysis, vibration analysis, seismology, ecology, financial analysis, biomedicine, etc. It can be used to detect periodicity, abnormal events, phase synchronization, etc.
https://avoid.overfit.cn/post/6b385fd6e8d64f2cb62d9caafd05389b
Author: Sam Erickson