{"id":783,"date":"2022-04-27T10:40:25","date_gmt":"2022-04-27T10:40:25","guid":{"rendered":"https:\/\/ml-gis-service.com\/?p=783"},"modified":"2022-04-27T10:40:26","modified_gmt":"2022-04-27T10:40:26","slug":"data-science-moving-average-or-moving-median-for-data-filtering-time-series","status":"publish","type":"post","link":"https:\/\/ml-gis-service.com\/index.php\/2022\/04\/27\/data-science-moving-average-or-moving-median-for-data-filtering-time-series\/","title":{"rendered":"Data Science: Moving Average or Moving Median for Data Filtering &#8211; Time Series"},"content":{"rendered":"\n<p>Time series are fantastic beasts that are hard to tame. Their nature may be unexpected, and we cannot use any<strong> universal method <\/strong>to work with them. Even filtering, the operation that should be plain and simple, plain and simple is not.<\/p>\n\n\n\n<p>Imagine the scenario when we&#8217;re asked to build a filtering pipeline that will help us to make signals prone to anomalies. We work with the sensor placed in orbit; it measures the flow of high-energy particles from outer space. (The same scenario may be applied to different data: from the financial market or the e-commerce user behavior streams). We expect two kinds of strange behavior:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/radhome.gsfc.nasa.gov\/radhome\/see.htm\">short bursts of a very high energy induced by the heavy particles (SEE)<\/a>,<\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"885\" height=\"387\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal0-sse.png\" alt=\"\" class=\"wp-image-796\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal0-sse.png 885w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal0-sse-300x131.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal0-sse-768x336.png 768w\" sizes=\"auto, (max-width: 885px) 100vw, 885px\" \/><figcaption>Figure 1. Artificial signal simulating SEE.<\/figcaption><\/figure><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>Periods of artificial oscillations caused by our spacecraft&#8217;s abrupt change of temperature (for example, the transition from the Earth&#8217;s shadow into a full Sun illumination),<\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"888\" height=\"387\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal1-freq_var.png\" alt=\"\" class=\"wp-image-797\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal1-freq_var.png 888w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal1-freq_var-300x131.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/signal1-freq_var-768x335.png 768w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><figcaption>Figure 2. Artificial signal simulating sensor&#8217;s work.<\/figcaption><\/figure><\/div>\n\n\n\n<p>How do we build our signal filtering algorithm for those two kinds of anomalies?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Background<\/h2>\n\n\n\n<p>Time series are different than dimensionless data sources. Two main differences are:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Values are correlated, and we can assume that the observation at time T1 will be similar to the observation at time T0. Without a weather forecasting app, we can guess that the temperature tomorrow will be within a range of today&#8217;s temperature +\/- 1 degree.<\/li><li>We often don&#8217;t know the future, especially in the long-range. The safe assumption is to think that time series are evolving in time and they show chaotic behavior. Just think about the stock. If we say that cryptocurrencies tend to be volatile, their price is chaotic and hard to predict.<\/li><\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Why do we filter data in those contexts?<\/h3>\n\n\n\n<p>The reason is to make data less noisy and easier to model. Look at Figure 1. We can distinguish there flattened sine wave. It can be modeled as a sine function if we find an appropriate frequency, phase, multiplicative factor, and additive factor. But how do we incorporate outliers in our model? The answer is: we cannot, and we shouldn&#8217;.t Outliers must be filtered out because they are not representative of our time series. The dopest thing is that if we create an optimal model that follows the usual pattern, we can detect and filter outliers quickly! We just set a threshold of difference from our forecast, and we will know about anomalies instantly, without delay.<\/p>\n\n\n\n<p>The case in Figure 2 is more about evolution and chaotic, abrupt changes than a correlation. We see that after step 500, the frequency and amplitude of readings suddenly change, but it seems to last into the future, so we can assume that it is a &#8220;new normal.&#8221; It is not a singular event but a full-scale change of behavior. Moreover, values are changing fast, and we don&#8217;t see any outliers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">We talk about filtering, but how do we do it?<\/h3>\n\n\n\n<p>In an exploratory analysis of time-depended data, the rule of thumb is not to trust a single observation but rather to take an average (or median) of n consecutive measurements. A single value may be misleading. I don&#8217;t know how it was reported in your country, but we have seen a decreasing number of COVID deaths and cases on Sunday and a peak around Tuesday in Poland. It was related to the reporting schema and the hospital&#8217;s capacity and a data analyst should be aware of this effect. We must filter out this kind of behavior. We can group observations from a week back and use the average value to analyze the overall trend and risk associated with the disease. The same for financial data, which could be volatile, but if we aggregate it over long periods, we start to see a pattern within and without the noise.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter-1024x576.jpg\" alt=\"\" class=\"wp-image-804\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter-1024x576.jpg 1024w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter-300x169.jpg 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter-768x432.jpg 768w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter-1536x864.jpg 1536w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig3meanmedianfilter.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Figure 3. The day-by-day pattern can be misleading.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Aggregation (filtering) leads to the loss of variance, but in return, higher-order hierarchical structures present patterns not visible at a finer scale. Decisions can be made!<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter-1024x576.jpg\" alt=\"\" class=\"wp-image-805\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter-1024x576.jpg 1024w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter-300x169.jpg 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter-768x432.jpg 768w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter-1536x864.jpg 1536w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/fig4meanmedianfilter.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Figure 4. Moving Average clearly shows a rising trend, and there is no place for misinterpretation.<\/figcaption><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Sliding Window(s) algorithms in Python<\/h2>\n\n\n\n<p>From now on, we will refer to moving windows as sliding windows. Yes, there is a different kind of moving window named expanding window, but it is not relevant for our case. Figure 5 shows the sliding window concept, and Figure 6 shows an expanding window.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4-1024x576.jpg\" alt=\"\" class=\"wp-image-816\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4-1024x576.jpg 1024w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4-300x169.jpg 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4-768x432.jpg 768w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4-1536x864.jpg 1536w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/4.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Figure 5. The sliding window algorithm. We pass the window of the fixed size through time and aggregate values to create a new time series.<\/figcaption><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5-1024x576.jpg\" alt=\"\" class=\"wp-image-817\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5-1024x576.jpg 1024w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5-300x169.jpg 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5-768x432.jpg 768w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5-1536x864.jpg 1536w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/5.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Figure 6. The expanding window. We build a window that aggregates values from the fixed date. The window is wider in each step and takes into account more values.<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">How does the algorithm work?<\/h3>\n\n\n\n<p>It is straightforward: it goes through the time series from the time <strong>T + window size<\/strong> and aggregates values from the current time <strong>T<\/strong> and <strong>window size &#8211; 1<\/strong> steps back. Usually, we take an average from this window as the expected value (thus, we have the Moving Average algorithm). But we can use other aggregation methods, median, standard deviation, or even exotic ones such as skewness and kurtosis. <\/p>\n\n\n\n<p>Do you know <code>pandas<\/code> package from <code>Python<\/code>? It has many functionalities dedicated to the <a href=\"https:\/\/pandas.pydata.org\/pandas-docs\/stable\/user_guide\/timeseries.html\">time series processing<\/a>, and we use the method <code><a href=\"https:\/\/pandas.pydata.org\/docs\/reference\/api\/pandas.DataFrame.rolling.html\">.rolling()<\/a><\/code> to create a sliding-window transformer. The syntax is:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dataseries.rolling(window_size)<\/pre>\n\n\n\n<p>The <code>.rolling()<\/code> method can be applied to the pandas DataFrame and pandas Series objects with the DateTime index.<\/p>\n\n\n\n<p>It is not the end of syntax, with only <code>.rolling()<\/code> we create an object which must be parsed with an appropriate function. It is similar to the .groupby() method. We need to pass a function over which data is aggregated\/grouped. For example, if we want to calculate the moving average:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dataseries.rolling(window_size).mean()<\/pre>\n\n\n\n<p>and the moving median:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dataseries.rolling(window_size).median()<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Median Filter<\/h2>\n\n\n\n<p>We can go back to our cases. The first case is a relatively stable signal with single bursts of high-energy events, and we assume that those events occur randomly. The better filter, in this case, is <strong>the median filter<\/strong>. Its main advantage over the classic average is its resistance to outliers. If they are rare then their amplitude doesn&#8217;t matter for the median, even if the window size is small.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"871\" height=\"357\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median.png\" alt=\"\" class=\"wp-image-808\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median.png 871w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median-300x123.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median-768x315.png 768w\" sizes=\"auto, (max-width: 871px) 100vw, 871px\" \/><figcaption>Figure 7. Comparison of the mean and median filter for processing SEE.<\/figcaption><\/figure><\/div>\n\n\n\n<p>In our case, we compare the mean and median filters of window size 5 (Figure 7). We may observe that of the most time their output is similar but when it comes to outliers, the mean filter tends to follow the unexpected values. We can observe this pattern &#8220;in reality&#8221; in images with oversaturated pixels, for example <a href=\"https:\/\/www.eso.org\/~ohainaut\/ccd\/CCD_artifacts.html\">from the cosmic rays<\/a>. The median filter works with images and data with more dimensions too and its role is always the same: to <strong>get rid of rare artifacts<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Mean Filter<\/h2>\n\n\n\n<p>But there are cases when the median filter will fail us. Let&#8217;s take a look at how it works in comparison to the moving average from the second scenario:<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"874\" height=\"357\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile.png\" alt=\"\" class=\"wp-image-810\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile.png 874w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile-300x123.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile-768x314.png 768w\" sizes=\"auto, (max-width: 874px) 100vw, 874px\" \/><figcaption>Figure 8. Comparison of the moving average and the moving median filters with the volatile, high amplitude, and high-frequency dataset. Window size = 5.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Here we don&#8217;t have outliers but high-frequency and high-amplitude signals. We have used the mean and the median filters with the window size 5. The median filter seems to be very sensitive to the volatile changes and, at the same time, the mean filter works reasonably well. Forecasts based on the median filter may quickly lose a trend in data. If this signal is responsible for the control of some device, and we use the median filter, we may damage an actuator. For data with high variance \/ high frequency, it is better to use the moving average. Of course, we can make the window wider, for example with a size of 15 steps, and then the mean and the median filter outputs will be similar but it is a trade-off &#8211; with larger windows, we lose more information and at some point, we may be not able to retrieve the basic properties, for example, the frequency.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"874\" height=\"357\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile_2.png\" alt=\"\" class=\"wp-image-812\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile_2.png 874w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile_2-300x123.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/mean_median_volatile_2-768x314.png 768w\" sizes=\"auto, (max-width: 874px) 100vw, 874px\" \/><figcaption>Figure 9. Comparison of the moving average and the moving median filters with the volatile, high amplitude, and high-frequency dataset. Window size = 15.<\/figcaption><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Mean or Median?<\/h2>\n\n\n\n<p>We can wrap up information from this article in two sentences:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Median filtering is better if we are removing rare events, outliers, from our dataset.<\/li><li>Average filter is better for volatile and high-frequency datasets.<\/li><\/ol>\n\n\n\n<p>What if our data has both properties? We know that time series may evolve in an unexpected direction&#8230; Unfortunately, there are no solutions that fit all the scenarios. The best idea is to build a robust system from multiple models \/ filters and observe how the basic properties of our time series are changing. Sometimes, it even doesn&#8217;t matter which algorithm you use because both will work fine! We close this article with temperature readings from Poland, you may observe, that outputs from both filters are reasonably well:<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"891\" height=\"496\" src=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/temperatures-1.png\" alt=\"\" class=\"wp-image-814\" srcset=\"https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/temperatures-1.png 891w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/temperatures-1-300x167.png 300w, https:\/\/ml-gis-service.com\/wp-content\/uploads\/2022\/04\/temperatures-1-768x428.png 768w\" sizes=\"auto, (max-width: 891px) 100vw, 891px\" \/><figcaption>Figure 10. The comparison of mean and median filters on a real-world dataset.<\/figcaption><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Code<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import numpy as np\nimport pandas as pd\n\n\n# Alias functions\n\ndef mean_filter(dataseries: pd.Series, window_size=5):\n    \"\"\"\n    Function applies rolling mean into given dataseries.\n    \n    Parameters\n    ----------\n    dataseries : pandas Series\n                 Pandas Series with datetime index.\n                 \n    window_size : int\n                  The rolling window size, how many readings are included in a mean.\n                  \n    Returns\n    -------\n    filtered : pandas Series\n               The filtered time series.\n    \"\"\"\n    \n    filtered = dataseries.rolling(window_size).mean()\n    return filtered\n\n\ndef median_filter(dataseries: pd.Series, window_size=5):\n    \"\"\"\n    Function applies rolling mean into given dataseries.\n    \n    Parameters\n    ----------\n    dataseries : pandas Series\n                 Pandas Series with datetime index.\n                 \n    window_size : int\n                  The rolling window size, how many readings are included in a mean.\n                  \n    Returns\n    -------\n    filtered : pandas Series\n               The filtered time series.\n    \"\"\"\n    \n    filtered = dataseries.rolling(window_size).median()\n    return filtered\n\n\n# Create 1st dataset\n\nsse_data = []\n\nfor x in np.linspace(0, np.pi*2, 1000):\n    t = np.random.rand()\n    v = np.sin(x) + np.random.rand()\n    if t > 0.95:\n        v = v * np.random.randint(2, 10)\n    sse_data.append(np.abs(v))\n    \nsse_data = np.array(sse_data)\n\n# Create 2nd dataset\n\nvar_data = []\n\nrandom_noise = np.random.rand(500)\n\nvar_data.extend(random_noise)\n    \nfor x in np.linspace(0, np.pi*2, 500):\n    t = np.random.rand()\n    v = np.sin(x*220) + 0.5*t\n    var_data.append(v)\n    \nvar_data = (np.array(var_data)) + 1\n\n# Filter datasets : 1\n\nsse_avg = mean_filter(pd.Series(sse_data), 5)\nsse_med = median_filter(pd.Series(sse_data), 5)\n\n# Filter datasets : 2\n\nvar_avg = mean_filter(pd.Series(var_data), 5)\nvar_med = median_filter(pd.Series(var_data), 5)\n\n# Plot the 1st\n\nplt.figure(figsize=(15, 6))\nplt.scatter(np.arange(0, 1000), sse_data, alpha=0.2)\nplt.plot(sse_avg)\nplt.plot(sse_med)\nplt.legend(['real', 'mean', 'median'])\nplt.show()\n\n# Plot the 2nd\n\nplt.figure(figsize=(15, 6))\nplt.scatter(np.arange(0, 1000), var_data, alpha=0.2)\nplt.plot(var_avg)\nplt.plot(var_med)\nplt.legend(['real', 'mean', 'median'])\nplt.show()\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Moving Mean or Moving Median for Time Series filtering?<\/p>\n","protected":false},"author":1,"featured_media":828,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,79,3,180],"tags":[188,181,183,182,64,7,186,190,191,189,187,184,185],"class_list":["post-783","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-science","category-pandas","category-python","category-time-series","tag-filtering","tag-moving-average","tag-moving-mean","tag-moving-median","tag-pandas","tag-python","tag-rolling-window","tag-see","tag-seu","tag-signal","tag-sliding-window","tag-time-series","tag-time-series-2"],"_links":{"self":[{"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/posts\/783","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/comments?post=783"}],"version-history":[{"count":26,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/posts\/783\/revisions"}],"predecessor-version":[{"id":829,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/posts\/783\/revisions\/829"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/media\/828"}],"wp:attachment":[{"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/media?parent=783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/categories?post=783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ml-gis-service.com\/index.php\/wp-json\/wp\/v2\/tags?post=783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}