{"id":1635,"date":"2025-02-04T07:02:56","date_gmt":"2025-02-04T07:02:56","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/02\/04\/how-to-find-seasonality-patterns-in-time-series-c3b9f11e89c6\/"},"modified":"2025-02-04T07:02:56","modified_gmt":"2025-02-04T07:02:56","slug":"how-to-find-seasonality-patterns-in-time-series-c3b9f11e89c6","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/02\/04\/how-to-find-seasonality-patterns-in-time-series-c3b9f11e89c6\/","title":{"rendered":"How to Find Seasonality Patterns in Time Series"},"content":{"rendered":"<p>    How to Find Seasonality Patterns in Time Series<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<h3 class=\"wp-block-heading\">Using Fourier Transforms to detect seasonal components<\/h3>\n<p class=\"wp-block-paragraph\">In my professional life as a data scientist, I have encountered time series multiple times. Most of my knowledge comes from my academic experience, specifically my courses in Econometrics (I have a degree in Economics), where we studied statistical properties and models of time series.<\/p>\n<p class=\"wp-block-paragraph\">Among the models I studied was <strong>SARIMA<\/strong>, which acknowledges the <strong>seasonality<\/strong> of a time series, however, we have never studied how to intercept and recognize seasonality patterns.<\/p>\n<p class=\"wp-block-paragraph\">Most of the time I had to find seasonal patterns I simply relied on <strong>visual<\/strong> <strong>inspections<\/strong> of data. This was until I stumbled on <a href=\"https:\/\/www.youtube.com\/watch?v=spUNpyF58BY&amp;t=1s\">this YouTube video<\/a> on <strong>Fourier transforms<\/strong> and eventually found out what a <strong>periodogram<\/strong> is.<\/p>\n<p class=\"wp-block-paragraph\">In this blog post, I will explain and apply simple concepts that will turn into useful tools that every DS who\u2019s studying time series should know.<\/p>\n<p class=\"wp-block-paragraph\"><strong>Table of Contents<\/strong><\/p>\n<ol class=\"wp-block-list\">\n<li>What is a Fourier Transform?<\/li>\n<li>Fourier Transform in Python<\/li>\n<li>Periodogram<\/li>\n<\/ol>\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n<p class=\"wp-block-paragraph\">Let\u2019s assume I have the following dataset (<a href=\"https:\/\/www.kaggle.com\/datasets\/robikscube\/hourly-energy-consumption\">AEP energy consumption<\/a>, CC0 license):<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">import pandas as pd\nimport matplotlib.pyplot as plt\n\ndf = pd.read_csv(\"data\/AEP_hourly.csv\", index_col=0) \ndf.index = pd.to_datetime(df.index)\ndf.sort_index(inplace=True)\n\nfig, ax = plt.subplots(figsize=(20,4))\ndf.plot(ax=ax)\nplt.tight_layout()\nplt.show()<\/code><\/pre>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"add5e2\" data-has-transparency=\"true\" style=\"--dominant-color: #add5e2;\" loading=\"lazy\" decoding=\"async\" width=\"2000\" height=\"400\" class=\"wp-image-597230 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw.png?resize=2000%2C400&#038;ssl=1\" alt=\"AEP hourly energy consumption | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw.png 2000w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw-300x60.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw-1024x205.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw-768x154.png 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1m9_pvwCDzl19iz3U3UbBFw-1536x307.png 1536w\" sizes=\"auto, (max-width: 2000px) 100vw, 2000px\"><figcaption class=\"wp-element-caption\">AEP hourly energy consumption | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">It is very clear, just from a visual inspection, that <strong>seasonal patterns are playing a role<\/strong>, however it might be trivial to intercept them all.<\/p>\n<p class=\"wp-block-paragraph\">As explained before, the discovery process I used to perform was mainly <strong>manual<\/strong>, and it could have looked something as follows:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">fig, ax = plt.subplots(3, 1, figsize=(20,9))\n\ndf_3y = df[(df.index &gt;= '2006\u201301\u201301') &amp;amp; (df.index &lt; '2010\u201301\u201301')]\ndf_3M = df[(df.index &gt;= '2006\u201301\u201301') &amp;amp; (df.index &lt; '2006\u201304\u201301')]\ndf_7d = df[(df.index &gt;= '2006\u201301\u201301') &amp;amp; (df.index &lt; '2006\u201301\u201308')]\n\nax[0].set_title('AEP energy consumption 3Y')\ndf_3y[['AEP_MW']].groupby(pd.Grouper(freq = 'D')).sum().plot(ax=ax[0])\nfor date in df_3y[[True if x % (24 * 365.25 \/ 2) == 0 else False for x in range(len(df_3y))]].index.tolist():\n ax[0].axvline(date, color = 'r', alpha = 0.5)\n\nax[1].set_title('AEP energy consumption 3M')\ndf_3M[['AEP_MW']].plot(ax=ax[1])\nfor date in df_3M[[True if x % (24 * 7) == 0 else False for x in range(len(df_3M))]].index.tolist():\n ax[1].axvline(date, color = 'r', alpha = 0.5)\n\nax[2].set_title('AEP energy consumption 7D')\ndf_7d[['AEP_MW']].plot(ax=ax[2])\nfor date in df_7d[[True if x % 24 == 0 else False for x in range(len(df_7d))]].index.tolist():\n ax[2].axvline(date, color = 'r', alpha = 0.5)\n\nplt.tight_layout()\nplt.show()<\/code><\/pre>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"f2f7f8\" data-has-transparency=\"true\" style=\"--dominant-color: #f2f7f8;\" loading=\"lazy\" decoding=\"async\" width=\"2000\" height=\"900\" class=\"wp-image-597231 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg.png?resize=2000%2C900&#038;ssl=1\" alt=\"AEP hourly energy consumption, smaller timeframe | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg.png 2000w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg-300x135.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg-1024x461.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg-768x346.png 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1yy3iDzcFMmnx8_hj_q8qFg-1536x691.png 1536w\" sizes=\"auto, (max-width: 2000px) 100vw, 2000px\"><figcaption class=\"wp-element-caption\">AEP hourly energy consumption, smaller timeframe | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">This is a more in-depth visualization of this time series. As we can see the following patterns are influencing the data:<br \/>\n**- a 6 month cycle,<\/p>\n<ul class=\"wp-block-list\">\n<li>a weekly cycle,<\/li>\n<li>and a daily cycle.**<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">This dataset shows energy consumption, so these seasonal patterns are easily inferable just from domain knowledge. However, by relying only on a manual inspection we could <strong>miss important informations.<\/strong> These could be some of the main <strong>drawbacks<\/strong>:<\/p>\n<ul class=\"wp-block-list\">\n<li>\n<strong>Subjectivity:<\/strong> We might miss less obvious patterns.<\/li>\n<li>\n<strong>Time-consuming<\/strong> : We need to test different timeframes one by one.<\/li>\n<li>\n<strong>Scalability issues:<\/strong> Works well for a few datasets, but inefficient for large-scale analysis.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">As a Data Scientist it would be useful to have a tool that gives us <strong>immediate feedback<\/strong> on the most important frequencies that compose the time series. This is where the <strong>Fourier Transforms<\/strong> come to help.<\/p>\n<h2 class=\"wp-block-heading\">1. What is a Fourier Transform<\/h2>\n<p class=\"wp-block-paragraph\">The Fourier Transform is a mathematical tool that allows us to &#8220;switch domain&#8221;.<\/p>\n<p class=\"wp-block-paragraph\">Usually, we visualize our data in the <strong>time domain<\/strong>. However, using a Fourier Transform, we can switch to the <strong>frequency domain<\/strong>, which shows the frequencies that are present in the signal and their relative contribution to the original time series.<\/p>\n<h3 class=\"wp-block-heading\">Intuition<\/h3>\n<p class=\"wp-block-paragraph\">Any well-behaved function f(x) can be written as a sum of sinusoids with different frequencies, amplitudes and phases. In simple terms, <strong>every signal<\/strong> (time series) is just a <strong>combination<\/strong> of <strong>simple waveforms<\/strong>.<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"000000\" data-has-transparency=\"true\" style=\"--dominant-color: #000000;\" loading=\"lazy\" decoding=\"async\" width=\"968\" height=\"117\" class=\"wp-image-597232 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1QzhfdRwvBcYDkZZL73G09g.png?resize=968%2C117&#038;ssl=1\" alt=\"Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1QzhfdRwvBcYDkZZL73G09g.png 968w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1QzhfdRwvBcYDkZZL73G09g-300x36.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1QzhfdRwvBcYDkZZL73G09g-768x93.png 768w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\"><figcaption class=\"wp-element-caption\">Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">Where:<\/p>\n<ul class=\"wp-block-list\">\n<li>F(f) represents the function in the <strong>frequency domain<\/strong>.<\/li>\n<li>f(x) is the original function in the <strong>time domain<\/strong>.<\/li>\n<li>exp(\u2212i2\u03c0f(x)) is a complex exponential that acts as a &#8220;frequency filter&#8221;.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Thus, <strong>F(f)<\/strong> tells us <strong>how much<\/strong> frequency <strong>f<\/strong> is <strong>present<\/strong> in the original function.<\/p>\n<h3 class=\"wp-block-heading\"><strong>Example<\/strong><\/h3>\n<p class=\"wp-block-paragraph\">Let\u2019s consider a signal composed of three sine waves with frequencies 2 Hz, 3 Hz, and 5 Hz:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"f7fafb\" data-has-transparency=\"true\" style=\"--dominant-color: #f7fafb;\" loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"300\" class=\"wp-image-597233 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1kntNLgrCK9BMvriI7W_CYA.png?resize=1000%2C300&#038;ssl=1\" alt=\"A Simple Signal in time domain | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1kntNLgrCK9BMvriI7W_CYA.png 1000w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1kntNLgrCK9BMvriI7W_CYA-300x90.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1kntNLgrCK9BMvriI7W_CYA-768x230.png 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\"><figcaption class=\"wp-element-caption\">A Simple Signal in time domain | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">Now, let\u2019s apply a Fourier Transform to extract these frequencies from the signal:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"f9faf9\" data-has-transparency=\"true\" style=\"--dominant-color: #f9faf9;\" loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"300\" class=\"wp-image-597234 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1OjZ68_7S-mIi2pIjtoUtWA.png?resize=1000%2C300&#038;ssl=1\" alt=\"A Simple Signal in the frequency domain | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1OjZ68_7S-mIi2pIjtoUtWA.png 1000w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1OjZ68_7S-mIi2pIjtoUtWA-300x90.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1OjZ68_7S-mIi2pIjtoUtWA-768x230.png 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\"><figcaption class=\"wp-element-caption\">A Simple Signal in the frequency domain | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">The graph above represents our signal expressed in the frequency domain instead of the classic time domain. From the resulting plot, we can see that our signal is decomposed in 3 elements of frequency 2 Hz, 3 Hz and 5 Hz as expected from the starting signal.<\/p>\n<p class=\"wp-block-paragraph\">As said before, any well-behaved function can be written as a sum of sinusoids. With the information we have so far it is possible to decompose our signal into three sinusoids:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"eff0f1\" data-has-transparency=\"true\" style=\"--dominant-color: #eff0f1;\" loading=\"lazy\" decoding=\"async\" width=\"1274\" height=\"725\" class=\"wp-image-597229 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1N7ZkfEKZFym8tsAfv17E4w.png?resize=1274%2C725&#038;ssl=1\" alt=\"A Simple Signal decomposition in its basic wavelength | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1N7ZkfEKZFym8tsAfv17E4w.png 1274w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1N7ZkfEKZFym8tsAfv17E4w-300x171.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1N7ZkfEKZFym8tsAfv17E4w-1024x583.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1N7ZkfEKZFym8tsAfv17E4w-768x437.png 768w\" sizes=\"auto, (max-width: 1274px) 100vw, 1274px\"><figcaption class=\"wp-element-caption\">A Simple Signal decomposition in its basic wavelength | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">The original signal (in blue) can be obtained by summing the three waves (in red). This process can easily be applied in any time series to evaluate the main frequencies that compose the time series.<\/p>\n<h2 class=\"wp-block-heading\">2 Fourier Transform in Python<\/h2>\n<p class=\"wp-block-paragraph\">Given that it is quite easy to switch between the time domain and the frequency domain, let\u2019s have a look at the AEP energy consumption time series we started studying at the beginning of the article.<\/p>\n<p class=\"wp-block-paragraph\">Python provides the &#8220;numpy.fft&#8221; library to compute the Fourier Transform for discrete signals. FFT stands for Fast Fourier Transform which is an algorithm used to decompose a discrete signal into its frequency components:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from numpy import fft\n\nX = fft.fft(df['AEP_MW'])\nN = len(X)\nfrequencies = fft.fftfreq(N, 1)\nperiods = 1 \/ frequencies\nfft_magnitude = np.abs(X) \/ N\n\nmask = frequencies &gt;= 0\n\n# Plot the Fourier Transform\nfig, ax = plt.subplots(figsize=(20, 3))\nax.step(periods[mask], fft_magnitude[mask]) # Only plot positive frequencies\nax.set_xscale('log')\nax.xaxis.set_major_formatter('{x:,.0f}')\nax.set_title('AEP energy consumption - Frequency-Domain')\nax.set_xlabel('Frequency (Hz)')\nax.set_ylabel('Magnitude')\nplt.show()<\/code><\/pre>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"f7fafb\" data-has-transparency=\"true\" style=\"--dominant-color: #f7fafb;\" loading=\"lazy\" decoding=\"async\" width=\"1635\" height=\"400\" class=\"wp-image-597235 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ.png?resize=1635%2C400&#038;ssl=1\" alt=\"AEP hourly energy consumption in frequency domain | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ.png 1635w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ-300x73.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ-1024x251.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ-768x188.png 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1Uj5W-kGdj3rHykE2f-RowQ-1536x376.png 1536w\" sizes=\"auto, (max-width: 1635px) 100vw, 1635px\"><figcaption class=\"wp-element-caption\">AEP hourly energy consumption in frequency domain | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">This is the frequency domain visualization of the AEP_MW energy consumption. When we analyze the graph we can already see that at certain frequencies we have a higher magnitude, implying higher importance of such frequencies.<\/p>\n<p class=\"wp-block-paragraph\">However, before doing so we add one more piece of theory that will allow us to build a <strong>periodogram<\/strong>, that will give us a better view of the most important frequencies.<\/p>\n<h2 class=\"wp-block-heading\">3. Periodogram<\/h2>\n<p class=\"wp-block-paragraph\">The periodogram is a frequency-domain representation of the <strong>power spectral density<\/strong> (PSD) of a signal. While the Fourier Transform tells us which frequencies are present in a signal, the periodogram quantifies the power (or intensity) of those frequencies. This passage is usefull as it <strong>reduces the noise<\/strong> of less important frequencies.<\/p>\n<p class=\"wp-block-paragraph\">Mathematically, the periodogram is given by:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"000000\" data-has-transparency=\"true\" style=\"--dominant-color: #000000;\" loading=\"lazy\" decoding=\"async\" width=\"1444\" height=\"241\" class=\"wp-image-597236 has-transparency\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/12XZn6NkQG5KnyvOtYT3WMw.png?resize=1444%2C241&#038;ssl=1\" alt=\"Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/12XZn6NkQG5KnyvOtYT3WMw.png 1444w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/12XZn6NkQG5KnyvOtYT3WMw-300x50.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/12XZn6NkQG5KnyvOtYT3WMw-1024x171.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/12XZn6NkQG5KnyvOtYT3WMw-768x128.png 768w\" sizes=\"auto, (max-width: 1444px) 100vw, 1444px\"><figcaption class=\"wp-element-caption\">Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">Where:<\/p>\n<ul class=\"wp-block-list\">\n<li>P(f) is the power spectral density (PSD) at frequency f,<\/li>\n<li>X(f) is the Fourier Transform of the signal,<\/li>\n<li>N is the total number of samples.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">This can be achieved in Python as follows:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">power_spectrum = np.abs(X)**2 \/ N # Power at each frequency\n\nfig, ax = plt.subplots(figsize=(20, 3))\nax.step(periods[mask], power_spectrum[mask])\nax.set_title('AEP energy consumption Periodogram')\nax.set_xscale('log')\nax.xaxis.set_major_formatter('{x:,.0f}')\nplt.xlabel('Frequency (Hz)')\nplt.ylabel('Power')\nplt.show()<\/code><\/pre>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"f9fbfb\" data-has-transparency=\"false\" style=\"--dominant-color: #f9fbfb;\" loading=\"lazy\" decoding=\"async\" width=\"2000\" height=\"400\" class=\"wp-image-597237 not-transparent\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A.png?resize=2000%2C400&#038;ssl=1\" alt=\"AEP hourly energy consumption Periodogram | Image by Author\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A.png 2000w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A-300x60.png 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A-1024x205.png 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A-768x154.png 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1F0Xperu_4ufe4kbQhZ1y8A-1536x307.png 1536w\" sizes=\"auto, (max-width: 2000px) 100vw, 2000px\"><figcaption class=\"wp-element-caption\">AEP hourly energy consumption Periodogram | Image by Author<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">From this periodogram, it is now possible to <strong>draw conclusions<\/strong>. As we can see the most powerful frequencies sit at:<\/p>\n<ul class=\"wp-block-list\">\n<li>24 Hz, corresponding to 24h,<\/li>\n<li>4.380 Hz, corresponding to 6 months,<\/li>\n<li>and at 168 Hz, corresponding to the weekly cycle.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">These three are the same <a href=\"https:\/\/towardsdatascience.com\/tag\/seasonality\/\" title=\"Seasonality\">Seasonality<\/a> components we found in the manual exercise done in the visual inspection. However, using this visualization, we can see <strong>three other cycles<\/strong>, weaker in power, but present:<\/p>\n<ul class=\"wp-block-list\">\n<li>a 12 Hz cycle,<\/li>\n<li>an 84 Hz cycle, correspondint to half a week,<\/li>\n<li>an 8.760 Hz cycle, corresponding to a full year.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">It is also possible to use the function &#8220;periodogram&#8221; present in scipy to obtain the same result.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from scipy.signal import periodogram\n\nfrequencies, power_spectrum = periodogram(df['AEP_MW'], return_onesided=False)\nperiods = 1 \/ frequencies\n\nfig, ax = plt.subplots(figsize=(20, 3))\nax.step(periods, power_spectrum)\nax.set_title('Periodogram')\nax.set_xscale('log')\nax.xaxis.set_major_formatter('{x:,.0f}')\nplt.xlabel('Frequency (Hz)')\nplt.ylabel('Power')\nplt.show()<\/code><\/pre>\n<h2 class=\"wp-block-heading\">Conclusions<\/h2>\n<p class=\"wp-block-paragraph\">When we are dealing with time series one of the most important components to consider is seasonalities.<\/p>\n<p class=\"wp-block-paragraph\">In this blog post, we\u2019ve seen how to <strong>easily discover seasonalities<\/strong> within a time series using a periodogram. Providing us with a simple-to-implement tool that will become extremely useful in the exploratory process.<\/p>\n<p class=\"wp-block-paragraph\">However, this is just a starting point of the possible implementations of Fourier Transform that we could benefit from, as there are many more:<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>Spectrogram<\/strong><\/li>\n<li><strong>Feature encoding<\/strong><\/li>\n<li><strong>Time series decomposition<\/strong><\/li>\n<li>\u2026<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Please leave some claps if you enjoyed the article and feel free to comment, any suggestion and feedback is appreciated!<\/p>\n<p class=\"wp-block-paragraph\">_<a href=\"https:\/\/github.com\/lorenzomezzini\/MediumPosts\/blob\/main\/Fourier\/FTT_and_Periodogram.ipynb\">Here you can find a notebook with the code from this blog post.<\/a>_<\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/how-to-find-seasonality-patterns-in-time-series-c3b9f11e89c6\/\">How to Find Seasonality Patterns in Time Series<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Lorenzo Mezzini<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/how-to-find-seasonality-patterns-in-time-series-c3b9f11e89c6\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to Find Seasonality Patterns in Time Series Using Fourier Transforms to detect seasonal components In my professional life as a data scientist, I have encountered time series multiple times. Most of my knowledge comes from my academic experience, specifically my courses in Econometrics (I have a degree in Economics), where we studied statistical properties [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,83,1614,160,1615,1189],"tags":[1039,1616,1617],"class_list":["post-1635","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-data-science","category-fourier-transform","category-programming","category-seasonality","category-timeseries","tag-ax","tag-df","tag-index"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1635"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=1635"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1635\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=1635"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=1635"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=1635"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}