{"id":1515,"date":"2016-05-01T10:12:13","date_gmt":"2016-05-01T18:12:13","guid":{"rendered":"https:\/\/oroup.com\/blog\/?p=1515"},"modified":"2016-05-08T09:01:09","modified_gmt":"2016-05-08T17:01:09","slug":"unevenly-spaced-time-series-analysis","status":"publish","type":"post","link":"https:\/\/oroup.com\/blog\/2016\/05\/unevenly-spaced-time-series-analysis\/","title":{"rendered":"Unevenly spaced time series analysis"},"content":{"rendered":"<p>A number of years ago I bought a <a href=\"http:\/\/www.withings.com\/us\/en\/products\/smart-body-analyzer\">Withings connected scale<\/a>. It&#8217;s great. You get on every morning, it logs your weight and uploads it to the internet where it can be analyzed.<\/p>\n<p>Recently, I decided to use the <a href=\"http:\/\/oauth.withings.com\/api\">Withing API<\/a> to build a plot that I could include on my homepage to keep an eye on my weight. Something every weight-watcher knows though is your weight fluctuates and so it&#8217;s great to keep an average. Unfortunately, unevenly spaced time series do not interact well with simple moving averages, so I found <a href=\"http:\/\/eckner.com\/papers\/unevenly_spaced_time_series_analysis.pdf\">this paper<\/a>\u00a0and converted <a href=\"http:\/\/eckner.com\/papers\/ts_alg.zip\">the code<\/a> into javascript:<\/p>\n<pre style=\"background:#f8f9fa;border:1px solid #e0e0e0;border-radius:4px;padding:1em;overflow-x:auto;font-family:Monaco,Consolas,monospace;font-size:0.85em;line-height:1.5\"><code>\r\n<span style=\"color:#800080\">var<\/span> tau = <span style=\"color:#ce0000\">1000<\/span> * <span style=\"color:#ce0000\">60<\/span> * <span style=\"color:#ce0000\">60<\/span> * <span style=\"color:#ce0000\">24<\/span> * <span style=\"color:#ce0000\">3.5<\/span> <span style=\"color:#ff8000\">\/\/ 1\/2 week<\/span>\n<span style=\"color:#800080\">var<\/span> i = <span style=\"color:#ce0000\">0<\/span>\n<span style=\"color:#800080\">var<\/span> left = <span style=\"color:#ce0000\">0<\/span>\n<span style=\"color:#800080\">var<\/span> t_left_new, roll_area, left_area, width, weight, y2\n<span style=\"color:#800080\">var<\/span> avg_data = [[json_data[<span style=\"color:#ce0000\">0<\/span>][<span style=\"color:#ce0000\">0<\/span>], json_data[<span style=\"color:#ce0000\">0<\/span>][<span style=\"color:#ce0000\">1<\/span>]]]\nroll_area = left_area = json_data[<span style=\"color:#ce0000\">0<\/span>][<span style=\"color:#ce0000\">1<\/span>] * tau\n<span style=\"color:#800080\">for<\/span> (i = <span style=\"color:#ce0000\">1<\/span>; i &lt; json_data.length; i++) {\n  roll_area += (json_data[i][<span style=\"color:#ce0000\">1<\/span>] + json_data[i - <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">1<\/span>]) \/ <span style=\"color:#ce0000\">2<\/span> * (json_data[i][<span style=\"color:#ce0000\">0<\/span>] - json_data[i - <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">0<\/span>])\n  roll_area = roll_area - left_area\n  t_left_new = json_data[i][<span style=\"color:#ce0000\">0<\/span>] - tau\n  <span style=\"color:#800080\">while<\/span> (json_data[left][<span style=\"color:#ce0000\">0<\/span>] &lt; t_left_new) {\n    roll_area = roll_area - (json_data[left][<span style=\"color:#ce0000\">1<\/span>] + json_data[left + <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">1<\/span>]) \/ <span style=\"color:#ce0000\">2<\/span> * (json_data[left + <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">0<\/span>] - json_data[left][<span style=\"color:#ce0000\">0<\/span>])\n    left++\n  }\n  width = json_data[left][<span style=\"color:#ce0000\">0<\/span>] - t_left_new\n  <span style=\"color:#800080\">if<\/span> ((left === <span style=\"color:#ce0000\">0<\/span>) || (width === <span style=\"color:#ce0000\">0<\/span>)) {\n    left_area = width * json_data[<span style=\"color:#ce0000\">0<\/span>][<span style=\"color:#ce0000\">1<\/span>]\n  } <span style=\"color:#800080\">else<\/span> {\n    weight = width \/ (json_data[left][<span style=\"color:#ce0000\">0<\/span>] - json_data[left - <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">0<\/span>])\n    y2 = json_data[left - <span style=\"color:#ce0000\">1<\/span>][<span style=\"color:#ce0000\">1<\/span>] * weight + json_data[left][<span style=\"color:#ce0000\">1<\/span>] * (<span style=\"color:#ce0000\">1<\/span> - weight)\n    left_area = width * (y2 + json_data[left][<span style=\"color:#ce0000\">1<\/span>]) \/ <span style=\"color:#ce0000\">2<\/span>\n  }\n  roll_area = roll_area + left_area\n  avg_data[i] = [json_data[i][<span style=\"color:#ce0000\">0<\/span>], roll_area \/ tau]\n}\n<\/code><\/pre>\n<p>The algorithm expects data in a variable called json_data. It&#8217;s an array of arrays, each of which is a timestamp and data pair. (This is the form that Highcharts expects.) The key factor is tau, the length of the time window over which you&#8217;re averaging. I have it set to a week here which I think is about right for looking at human weight. (3.5 days works quite well too.)<\/p>\n<p>The results are here:<\/p>\n<div id=\"weight-graph\"><\/div>\n<p><script src=\"\/js\/jquery.min.js\"><\/script><br \/>\n<script src=\"\/js\/highcharts.js\"><\/script><br \/>\n<script src=\"\/pub\/weight\/graph.js\"><\/script><\/p>\n<p>The <a href=\"https:\/\/oroup.com\/pub\/weight\/graph.js\">code in context<\/a>\u00a0and\u00a0a <a href=\"https:\/\/oroup.com\/pub\/weight\/\">cleaner running example<\/a>\u00a0are both available. You can also see the <a href=\"https:\/\/oroup.com\/pub\/weight\/data\/\">raw data<\/a> I use to fuel the graph.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A number of years ago I bought a Withings connected scale. It&#8217;s great. You get on every morning, it logs your weight and uploads it to the internet where it can be analyzed. Recently, I decided to use the Withing&hellip;<\/p>\n","protected":false},"author":1,"featured_media":1522,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-1515","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code"],"_links":{"self":[{"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/posts\/1515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/comments?post=1515"}],"version-history":[{"count":16,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions"}],"predecessor-version":[{"id":1532,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions\/1532"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/media\/1522"}],"wp:attachment":[{"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/media?parent=1515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/categories?post=1515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oroup.com\/blog\/wp-json\/wp\/v2\/tags?post=1515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}