1&&\"string\"==typeof v&&!d.checkClone&&De.test(v))return e.each((function(i){var o=e.eq(i);y&&(t[0]=v.call(this,i,o.html())),Re(o,t,n,r)}));if(p&&(a=(i=xe(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=a),a||r)){for(u=(s=b.map(ve(i,\"script\"),Le)).length;f0&&ye(a,!u&&ve(e,\"script\")),s},cleanData:function(e){for(var t,n,r,i=b.event.special,o=0;void 0!==(n=e[o]);o++)if(X(n)){if(t=n[G.expando]){if(t.events)for(r in t.events)i[r]?b.event.remove(n,r):b.removeEvent(n,r,t.handle);n[G.expando]=void 0}n[Y.expando]&&(n[Y.expando]=void 0)}}}),b.fn.extend({detach:function(e){return Me(this,e,!0)},remove:function(e){return Me(this,e)},text:function(e){return B(this,(function(e){return void 0===e?b.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Re(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)}))},prepend:function(){return Re(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Re(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Re(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(b.cleanData(ve(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return b.clone(this,e,t)}))},html:function(e){return B(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=b.htmlPrefilter(e);try{for(;n3,ne.removeChild(t)),s}}))}();var ze=[\"Webkit\",\"Moz\",\"ms\"],Ue=v.createElement(\"div\").style,Xe={};function Ve(e){var t=b.cssProps[e]||Xe[e];return t||(e in Ue?e:Xe[e]=function(e){for(var t=e[0].toUpperCase()+e.slice(1),n=ze.length;n--;)if((e=ze[n]+t)in Ue)return e}(e)||e)}var Ge=/^(none|table(?!-c[ea]).+)/,Ye=/^--/,Qe={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Je={letterSpacing:\"0\",fontWeight:\"400\"};function Ke(e,t,n){var r=ee.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ze(e,t,n,r,i,o){var a=\"width\"===t?1:0,s=0,u=0;if(n===(r?\"border\":\"content\"))return 0;for(;a<4;a+=2)\"margin\"===n&&(u+=b.css(e,n+te[a],!0,i)),r?(\"content\"===n&&(u-=b.css(e,\"padding\"+te[a],!0,i)),\"margin\"!==n&&(u-=b.css(e,\"border\"+te[a]+\"Width\",!0,i))):(u+=b.css(e,\"padding\"+te[a],!0,i),\"padding\"!==n?u+=b.css(e,\"border\"+te[a]+\"Width\",!0,i):s+=b.css(e,\"border\"+te[a]+\"Width\",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function et(e,t,n){var r=We(e),i=(!d.boxSizingReliable()||n)&&\"border-box\"===b.css(e,\"boxSizing\",!1,r),o=i,a=$e(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Ie.test(a)){if(!n)return a;a=\"auto\"}return(!d.boxSizingReliable()&&i||!d.reliableTrDimensions()&&k(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===b.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===b.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ze(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=$e(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=U(t),u=Ye.test(t),l=e.style;if(u||(t=Ve(s)),a=b.cssHooks[t]||b.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=ee.exec(n))&&i[1]&&(n=ae(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(b.cssNumber[s]?\"\":\"px\")),d.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=U(t);return Ye.test(t)||(t=Ve(s)),(a=b.cssHooks[t]||b.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=$e(e,t,r)),\"normal\"===i&&t in Je&&(i=Je[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),b.each([\"height\",\"width\"],(function(e,t){b.cssHooks[t]={get:function(e,n,r){if(n)return!Ge.test(b.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):Fe(e,Qe,(function(){return et(e,t,r)}))},set:function(e,n,r){var i,o=We(e),a=!d.scrollboxSize()&&\"absolute\"===o.position,s=(a||r)&&\"border-box\"===b.css(e,\"boxSizing\",!1,o),u=r?Ze(e,t,r,s,o):0;return s&&a&&(u-=Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,\"border\",!1,o)-.5)),u&&(i=ee.exec(n))&&\"px\"!==(i[3]||\"px\")&&(e.style[t]=n,n=b.css(e,t)),Ke(0,n,u)}}})),b.cssHooks.marginLeft=_e(d.reliableMarginLeft,(function(e,t){if(t)return(parseFloat($e(e,\"marginLeft\"))||e.getBoundingClientRect().left-Fe(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+\"px\"})),b.each({margin:\"\",padding:\"\",border:\"Width\"},(function(e,t){b.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o=\"string\"==typeof n?n.split(\" \"):[n];r<4;r++)i[e+te[r]+t]=o[r]||o[r-2]||o[0];return i}},\"margin\"!==e&&(b.cssHooks[e+t].set=Ke)})),b.fn.extend({css:function(e,t){return B(this,(function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=We(e),i=t.length;a1)}}),b.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||b.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?\"\":\"px\")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=b.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):1!==e.elem.nodeType||!b.cssHooks[e.prop]&&null==e.elem.style[Ve(e.prop)]?e.elem[e.prop]=e.now:b.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},b.fx=tt.prototype.init,b.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===v.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,b.fx.interval),b.fx.tick())}function st(){return e.setTimeout((function(){nt=void 0})),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=te[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(ct.tweeners[t]||[]).concat(ct.tweeners[\"*\"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each((function(){b.removeAttr(this,e)}))}}),b.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?b.prop(e,t,n):(1===o&&b.isXMLDoc(e)||(i=b.attrHooks[t.toLowerCase()]||(b.expr.match.bool.test(t)?ft:void 0)),void 0!==n?null===n?void b.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:null==(r=b.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!d.radioValue&&\"radio\"===t&&k(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(O);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),ft={set:function(e,t,n){return!1===t?b.removeAttr(e,n):e.setAttribute(n,n),n}},b.each(b.expr.match.bool.source.match(/\\w+/g),(function(e,t){var n=pt[t]||b.find.attr;pt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=pt[a],pt[a]=i,i=null!=n(e,t,r)?a:null,pt[a]=o),i}}));var dt=/^(?:input|select|textarea|button)$/i,ht=/^(?:a|area)$/i;function gt(e){return(e.match(O)||[]).join(\" \")}function vt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function yt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(O)||[]}b.fn.extend({prop:function(e,t){return B(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[b.propFix[e]||e]}))}}),b.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&b.isXMLDoc(e)||(t=b.propFix[t]||t,i=b.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=b.find.attr(e,\"tabindex\");return t?parseInt(t,10):dt.test(e.nodeName)||ht.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:\"htmlFor\",class:\"className\"}}),d.optSelected||(b.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),b.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],(function(){b.propFix[this.toLowerCase()]=this})),b.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(h(e))return this.each((function(t){b(this).addClass(e.call(this,t,vt(this)))}));if((t=yt(e)).length)for(;n=this[u++];)if(i=vt(n),r=1===n.nodeType&&\" \"+gt(i)+\" \"){for(a=0;o=t[a++];)r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=gt(r))&&n.setAttribute(\"class\",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(h(e))return this.each((function(t){b(this).removeClass(e.call(this,t,vt(this)))}));if(!arguments.length)return this.attr(\"class\",\"\");if((t=yt(e)).length)for(;n=this[u++];)if(i=vt(n),r=1===n.nodeType&&\" \"+gt(i)+\" \"){for(a=0;o=t[a++];)for(;r.indexOf(\" \"+o+\" \")>-1;)r=r.replace(\" \"+o+\" \",\" \");i!==(s=gt(r))&&n.setAttribute(\"class\",s)}return this},toggleClass:function(e,t){var n=typeof e,r=\"string\"===n||Array.isArray(e);return\"boolean\"==typeof t&&r?t?this.addClass(e):this.removeClass(e):h(e)?this.each((function(n){b(this).toggleClass(e.call(this,n,vt(this),t),t)})):this.each((function(){var t,i,o,a;if(r)for(i=0,o=b(this),a=yt(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&\"boolean\"!==n||((t=vt(this))&&G.set(this,\"__className__\",t),this.setAttribute&&this.setAttribute(\"class\",t||!1===e?\"\":G.get(this,\"__className__\")||\"\"))}))},hasClass:function(e){var t,n,r=0;for(t=\" \"+e+\" \";n=this[r++];)if(1===n.nodeType&&(\" \"+gt(vt(n))+\" \").indexOf(t)>-1)return!0;return!1}});var mt=/\\r/g;b.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=h(e),this.each((function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,b(this).val()):e)?i=\"\":\"number\"==typeof i?i+=\"\":Array.isArray(i)&&(i=b.map(i,(function(e){return null==e?\"\":e+\"\"}))),(t=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()])&&\"set\"in t&&void 0!==t.set(this,i,\"value\")||(this.value=i))}))):i?(t=b.valHooks[i.type]||b.valHooks[i.nodeName.toLowerCase()])&&\"get\"in t&&void 0!==(n=t.get(i,\"value\"))?n:\"string\"==typeof(n=i.value)?n.replace(mt,\"\"):null==n?\"\":n:void 0}}),b.extend({valHooks:{option:{get:function(e){var t=b.find.attr(e,\"value\");return null!=t?t:gt(b.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),b.each([\"radio\",\"checkbox\"],(function(){b.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=b.inArray(b(e).val(),t)>-1}},d.checkOn||(b.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})})),d.focusin=\"onfocusin\"in e;var xt=/^(?:focusinfocus|focusoutblur)$/,bt=function(e){e.stopPropagation()};b.extend(b.event,{trigger:function(t,n,r,i){var o,a,s,u,l,f,p,d,y=[r||v],m=c.call(t,\"type\")?t.type:t,x=c.call(t,\"namespace\")?t.namespace.split(\".\"):[];if(a=d=s=r=r||v,3!==r.nodeType&&8!==r.nodeType&&!xt.test(m+b.event.triggered)&&(m.indexOf(\".\")>-1&&(x=m.split(\".\"),m=x.shift(),x.sort()),l=m.indexOf(\":\")<0&&\"on\"+m,(t=t[b.expando]?t:new b.Event(m,\"object\"==typeof t&&t)).isTrigger=i?2:3,t.namespace=x.join(\".\"),t.rnamespace=t.namespace?new RegExp(\"(^|\\\\.)\"+x.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:b.makeArray(n,[t]),p=b.event.special[m]||{},i||!p.trigger||!1!==p.trigger.apply(r,n))){if(!i&&!p.noBubble&&!g(r)){for(u=p.delegateType||m,xt.test(u+m)||(a=a.parentNode);a;a=a.parentNode)y.push(a),s=a;s===(r.ownerDocument||v)&&y.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=y[o++])&&!t.isPropagationStopped();)d=a,t.type=o>1?u:p.bindType||m,(f=(G.get(a,\"events\")||Object.create(null))[t.type]&&G.get(a,\"handle\"))&&f.apply(a,n),(f=l&&a[l])&&f.apply&&X(a)&&(t.result=f.apply(a,n),!1===t.result&&t.preventDefault());return t.type=m,i||t.isDefaultPrevented()||p._default&&!1!==p._default.apply(y.pop(),n)||!X(r)||l&&h(r[m])&&!g(r)&&((s=r[l])&&(r[l]=null),b.event.triggered=m,t.isPropagationStopped()&&d.addEventListener(m,bt),r[m](),t.isPropagationStopped()&&d.removeEventListener(m,bt),b.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=b.extend(new b.Event,n,{type:e,isSimulated:!0});b.event.trigger(r,null,t)}}),b.fn.extend({trigger:function(e,t){return this.each((function(){b.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return b.event.trigger(e,t,n,!0)}}),d.focusin||b.each({focus:\"focusin\",blur:\"focusout\"},(function(e,t){var n=function(e){b.event.simulate(t,e.target,b.event.fix(e))};b.event.special[t]={setup:function(){var r=this.ownerDocument||this.document||this,i=G.access(r,t);i||r.addEventListener(e,n,!0),G.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this.document||this,i=G.access(r,t)-1;i?G.access(r,t,i):(r.removeEventListener(e,n,!0),G.remove(r,t))}}}));var wt=e.location,Tt={guid:Date.now()},Ct=/\\?/;b.parseXML=function(t){var n;if(!t||\"string\"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,\"text/xml\")}catch(e){n=void 0}return n&&!n.getElementsByTagName(\"parsererror\").length||b.error(\"Invalid XML: \"+t),n};var Et=/\\[\\]$/,St=/\\r?\\n/g,kt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function Nt(e,t,n,r){var i;if(Array.isArray(t))b.each(t,(function(t,i){n||Et.test(e)?r(e,i):Nt(e+\"[\"+(\"object\"==typeof i&&null!=i?t:\"\")+\"]\",i,n,r)}));else if(n||\"object\"!==x(t))r(e,t);else for(i in t)Nt(e+\"[\"+i+\"]\",t[i],n,r)}b.param=function(e,t){var n,r=[],i=function(e,t){var n=h(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,(function(){i(this.name,this.value)}));else for(n in e)Nt(n,e[n],t,i);return r.join(\"&\")},b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=b.prop(this,\"elements\");return e?b.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!b(this).is(\":disabled\")&&At.test(this.nodeName)&&!kt.test(e)&&(this.checked||!pe.test(e))})).map((function(e,t){var n=b(this).val();return null==n?null:Array.isArray(n)?b.map(n,(function(e){return{name:t.name,value:e.replace(St,\"\\r\\n\")}})):{name:t.name,value:n.replace(St,\"\\r\\n\")}})).get()}});var Dt=/%20/g,jt=/#.*$/,qt=/([?&])_=[^&]*/,Lt=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ht=/^(?:GET|HEAD)$/,Ot=/^\\/\\//,Pt={},Rt={},Mt=\"*/\".concat(\"*\"),It=v.createElement(\"a\");function Wt(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,i=0,o=t.toLowerCase().match(O)||[];if(h(n))for(;r=o[i++];)\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function Ft(e,t,n,r){var i={},o=e===Rt;function a(s){var u;return i[s]=!0,b.each(e[s]||[],(function(e,s){var l=s(t,n,r);return\"string\"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)})),u}return a(t.dataTypes[0])||!i[\"*\"]&&a(\"*\")}function Bt(e,t){var n,r,i=b.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&b.extend(!0,e,r),e}It.href=wt.href,b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:wt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(wt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":Mt,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Bt(Bt(e,b.ajaxSettings),t):Bt(b.ajaxSettings,e)},ajaxPrefilter:Wt(Pt),ajaxTransport:Wt(Rt),ajax:function(t,n){\"object\"==typeof t&&(n=t,t=void 0),n=n||{};var r,i,o,a,s,u,l,c,f,p,d=b.ajaxSetup({},n),h=d.context||d,g=d.context&&(h.nodeType||h.jquery)?b(h):b.event,y=b.Deferred(),m=b.Callbacks(\"once memory\"),x=d.statusCode||{},w={},T={},C=\"canceled\",E={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a)for(a={};t=Lt.exec(o);)a[t[1].toLowerCase()+\" \"]=(a[t[1].toLowerCase()+\" \"]||[]).concat(t[2]);t=a[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return l?o:null},setRequestHeader:function(e,t){return null==l&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,w[e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return r&&r.abort(t),S(0,t),this}};if(y.promise(E),d.url=((t||d.url||wt.href)+\"\").replace(Ot,wt.protocol+\"//\"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=(d.dataType||\"*\").toLowerCase().match(O)||[\"\"],null==d.crossDomain){u=v.createElement(\"a\");try{u.href=d.url,u.href=u.href,d.crossDomain=It.protocol+\"//\"+It.host!=u.protocol+\"//\"+u.host}catch(e){d.crossDomain=!0}}if(d.data&&d.processData&&\"string\"!=typeof d.data&&(d.data=b.param(d.data,d.traditional)),Ft(Pt,d,n,E),l)return E;for(f in(c=b.event&&d.global)&&0==b.active++&&b.event.trigger(\"ajaxStart\"),d.type=d.type.toUpperCase(),d.hasContent=!Ht.test(d.type),i=d.url.replace(jt,\"\"),d.hasContent?d.data&&d.processData&&0===(d.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(d.data=d.data.replace(Dt,\"+\")):(p=d.url.slice(i.length),d.data&&(d.processData||\"string\"==typeof d.data)&&(i+=(Ct.test(i)?\"&\":\"?\")+d.data,delete d.data),!1===d.cache&&(i=i.replace(qt,\"$1\"),p=(Ct.test(i)?\"&\":\"?\")+\"_=\"+Tt.guid+++p),d.url=i+p),d.ifModified&&(b.lastModified[i]&&E.setRequestHeader(\"If-Modified-Since\",b.lastModified[i]),b.etag[i]&&E.setRequestHeader(\"If-None-Match\",b.etag[i])),(d.data&&d.hasContent&&!1!==d.contentType||n.contentType)&&E.setRequestHeader(\"Content-Type\",d.contentType),E.setRequestHeader(\"Accept\",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(\"*\"!==d.dataTypes[0]?\", \"+Mt+\"; q=0.01\":\"\"):d.accepts[\"*\"]),d.headers)E.setRequestHeader(f,d.headers[f]);if(d.beforeSend&&(!1===d.beforeSend.call(h,E,d)||l))return E.abort();if(C=\"abort\",m.add(d.complete),E.done(d.success),E.fail(d.error),r=Ft(Rt,d,n,E)){if(E.readyState=1,c&&g.trigger(\"ajaxSend\",[E,d]),l)return E;d.async&&d.timeout>0&&(s=e.setTimeout((function(){E.abort(\"timeout\")}),d.timeout));try{l=!1,r.send(w,S)}catch(e){if(l)throw e;S(-1,e)}}else S(-1,\"No Transport\");function S(t,n,a,u){var f,p,v,w,T,C=n;l||(l=!0,s&&e.clearTimeout(s),r=void 0,o=u||\"\",E.readyState=t>0?4:0,f=t>=200&&t<300||304===t,a&&(w=function(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;\"*\"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(d,E,a)),!f&&b.inArray(\"script\",d.dataTypes)>-1&&(d.converters[\"text script\"]=function(){}),w=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(d,w,E,f),f?(d.ifModified&&((T=E.getResponseHeader(\"Last-Modified\"))&&(b.lastModified[i]=T),(T=E.getResponseHeader(\"etag\"))&&(b.etag[i]=T)),204===t||\"HEAD\"===d.type?C=\"nocontent\":304===t?C=\"notmodified\":(C=w.state,p=w.data,f=!(v=w.error))):(v=C,!t&&C||(C=\"error\",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+\"\",f?y.resolveWith(h,[p,C,E]):y.rejectWith(h,[E,C,v]),E.statusCode(x),x=void 0,c&&g.trigger(f?\"ajaxSuccess\":\"ajaxError\",[E,d,f?p:v]),m.fireWith(h,[E,C]),c&&(g.trigger(\"ajaxComplete\",[E,d]),--b.active||b.event.trigger(\"ajaxStop\")))}return E},getJSON:function(e,t,n){return b.get(e,t,n,\"json\")},getScript:function(e,t){return b.get(e,void 0,t,\"script\")}}),b.each([\"get\",\"post\"],(function(e,t){b[t]=function(e,n,r,i){return h(n)&&(i=i||r,r=n,n=void 0),b.ajax(b.extend({url:e,type:t,dataType:i,data:n,success:r},b.isPlainObject(e)&&e))}})),b.ajaxPrefilter((function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")})),b._evalUrl=function(e,t,n){return b.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){b.globalEval(e,t,n)}})},b.fn.extend({wrapAll:function(e){var t;return this[0]&&(h(e)&&(e=e.call(this[0])),t=b(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return h(e)?this.each((function(t){b(this).wrapInner(e.call(this,t))})):this.each((function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=h(e);return this.each((function(n){b(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not(\"body\").each((function(){b(this).replaceWith(this.childNodes)})),this}}),b.expr.pseudos.hidden=function(e){return!b.expr.pseudos.visible(e)},b.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},b.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var $t={0:200,1223:204},_t=b.ajaxSettings.xhr();d.cors=!!_t&&\"withCredentials\"in _t,d.ajax=_t=!!_t,b.ajaxTransport((function(t){var n,r;if(d.cors||_t&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];for(a in t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i[\"X-Requested-With\"]||(i[\"X-Requested-With\"]=\"XMLHttpRequest\"),i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,\"abort\"===e?s.abort():\"error\"===e?\"number\"!=typeof s.status?o(0,\"error\"):o(s.status,s.statusText):o($t[s.status]||s.status,s.statusText,\"text\"!==(s.responseType||\"text\")||\"string\"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n(\"error\"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout((function(){n&&r()}))},n=n(\"abort\");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}})),b.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),b.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter(\"script\",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")})),b.ajaxTransport(\"script\",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=b(\""
+ ],
+ "text/plain": [
+ ":DynamicMap [Name]\n",
+ " :RGB [Date,energy(kWh/hh)] (R,G,B,A)"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {
+ "application/vnd.holoviews_exec.v0+json": {
+ "id": "1001"
+ }
+ },
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import holoviews as hv\n",
+ "from holoviews import opts\n",
+ "\n",
+ "from holoviews.plotting.links import RangeToolLink\n",
+ "\n",
+ "import datashader as ds\n",
+ "\n",
+ "from holoviews.operation.datashader import datashade, shade, dynspread, rasterize\n",
+ "from holoviews.operation import decimate\n",
+ "\n",
+ "hv.extension('bokeh')\n",
+ "\n",
+ "\n",
+ "def house_curve(Name=None):\n",
+ " if isinstance(Name, int):\n",
+ " name = df.LCLid.unique()[Name]\n",
+ " d = df[df.LCLid == Name]\n",
+ " d_curve = hv.Curve(d, 'Date', 'energy(kWh/hh)', label=Name)\n",
+ " return d_curve\n",
+ "\n",
+ "\n",
+ "dmap = hv.DynamicMap(house_curve, kdims=['Name'])\n",
+ "dmap = dmap.redim.values(Name=list(df.LCLid.unique()))\n",
+ "dynspread(datashade(dmap).opts(width=800,\n",
+ " height=300,\n",
+ " tools=['xwheel_zoom', 'pan'],\n",
+ " active_tools=['xwheel_zoom', 'pan'],\n",
+ " default_tools=['reset', 'save', 'hover']\n",
+ " ))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:33:17.798444Z",
+ "start_time": "2020-10-19T05:33:17.707112Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Profiling"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:15.418443Z",
+ "start_time": "2020-10-19T05:40:15.354154Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# from pandas_profiling import ProfileReport\n",
+ "# profile = ProfileReport(df, title=\"Pandas Profiling Report\", minimal=True)\n",
+ "# profile"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Norm"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:15.654837Z",
+ "start_time": "2020-10-19T05:40:15.422503Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " energy(kWh/hh) | \n",
+ " month | \n",
+ " day | \n",
+ " week | \n",
+ " hour | \n",
+ " minute | \n",
+ " dayofweek | \n",
+ " visibility | \n",
+ " windBearing | \n",
+ " temperature | \n",
+ " dewPoint | \n",
+ " pressure | \n",
+ " apparentTemperature | \n",
+ " windSpeed | \n",
+ " humidity | \n",
+ " holiday | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | count | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.00000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.00000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ " 200000.000000 | \n",
+ "
\n",
+ " \n",
+ " | mean | \n",
+ " 0.680330 | \n",
+ " 6.750390 | \n",
+ " 15.802345 | \n",
+ " 27.45751 | \n",
+ " 11.504260 | \n",
+ " 15.000000 | \n",
+ " 2.994710 | \n",
+ " 11.352912 | \n",
+ " 194.297550 | \n",
+ " 11.36635 | \n",
+ " 7.243470 | \n",
+ " 1012.911028 | \n",
+ " 10.263340 | \n",
+ " 3.901174 | \n",
+ " 0.775153 | \n",
+ " 0.022905 | \n",
+ "
\n",
+ " \n",
+ " | std | \n",
+ " 0.837963 | \n",
+ " 3.422923 | \n",
+ " 8.811981 | \n",
+ " 14.88508 | \n",
+ " 6.922627 | \n",
+ " 15.000038 | \n",
+ " 1.999133 | \n",
+ " 2.935302 | \n",
+ " 90.607297 | \n",
+ " 6.07569 | \n",
+ " 5.187453 | \n",
+ " 10.744543 | \n",
+ " 7.256847 | \n",
+ " 2.023303 | \n",
+ " 0.144399 | \n",
+ " 0.149601 | \n",
+ "
\n",
+ " \n",
+ " | min | \n",
+ " 0.001000 | \n",
+ " 1.000000 | \n",
+ " 1.000000 | \n",
+ " 1.00000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.270000 | \n",
+ " 0.000000 | \n",
+ " -3.86000 | \n",
+ " -8.920000 | \n",
+ " 975.740000 | \n",
+ " -8.880000 | \n",
+ " 0.040000 | \n",
+ " 0.230000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " | 25% | \n",
+ " 0.138000 | \n",
+ " 4.000000 | \n",
+ " 8.000000 | \n",
+ " 15.00000 | \n",
+ " 6.000000 | \n",
+ " 0.000000 | \n",
+ " 1.000000 | \n",
+ " 10.460000 | \n",
+ " 121.000000 | \n",
+ " 6.98000 | \n",
+ " 3.370000 | \n",
+ " 1006.910000 | \n",
+ " 4.430000 | \n",
+ " 2.420000 | \n",
+ " 0.690000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " | 50% | \n",
+ " 0.307000 | \n",
+ " 7.000000 | \n",
+ " 16.000000 | \n",
+ " 28.00000 | \n",
+ " 12.000000 | \n",
+ " 15.000000 | \n",
+ " 3.000000 | \n",
+ " 12.260000 | \n",
+ " 215.000000 | \n",
+ " 11.14000 | \n",
+ " 7.470000 | \n",
+ " 1013.730000 | \n",
+ " 11.140000 | \n",
+ " 3.670000 | \n",
+ " 0.810000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " | 75% | \n",
+ " 0.872000 | \n",
+ " 10.000000 | \n",
+ " 23.000000 | \n",
+ " 40.00000 | \n",
+ " 18.000000 | \n",
+ " 30.000000 | \n",
+ " 5.000000 | \n",
+ " 13.100000 | \n",
+ " 254.000000 | \n",
+ " 15.76000 | \n",
+ " 11.380000 | \n",
+ " 1020.130000 | \n",
+ " 15.760000 | \n",
+ " 5.070000 | \n",
+ " 0.890000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " | max | \n",
+ " 8.170999 | \n",
+ " 12.000000 | \n",
+ " 31.000000 | \n",
+ " 52.00000 | \n",
+ " 23.000000 | \n",
+ " 30.000000 | \n",
+ " 6.000000 | \n",
+ " 16.090000 | \n",
+ " 359.000000 | \n",
+ " 32.40000 | \n",
+ " 19.880000 | \n",
+ " 1040.130000 | \n",
+ " 32.420000 | \n",
+ " 14.800000 | \n",
+ " 1.000000 | \n",
+ " 1.000000 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " energy(kWh/hh) month day week \\\n",
+ "count 200000.000000 200000.000000 200000.000000 200000.00000 \n",
+ "mean 0.680330 6.750390 15.802345 27.45751 \n",
+ "std 0.837963 3.422923 8.811981 14.88508 \n",
+ "min 0.001000 1.000000 1.000000 1.00000 \n",
+ "25% 0.138000 4.000000 8.000000 15.00000 \n",
+ "50% 0.307000 7.000000 16.000000 28.00000 \n",
+ "75% 0.872000 10.000000 23.000000 40.00000 \n",
+ "max 8.170999 12.000000 31.000000 52.00000 \n",
+ "\n",
+ " hour minute dayofweek visibility \\\n",
+ "count 200000.000000 200000.000000 200000.000000 200000.000000 \n",
+ "mean 11.504260 15.000000 2.994710 11.352912 \n",
+ "std 6.922627 15.000038 1.999133 2.935302 \n",
+ "min 0.000000 0.000000 0.000000 0.270000 \n",
+ "25% 6.000000 0.000000 1.000000 10.460000 \n",
+ "50% 12.000000 15.000000 3.000000 12.260000 \n",
+ "75% 18.000000 30.000000 5.000000 13.100000 \n",
+ "max 23.000000 30.000000 6.000000 16.090000 \n",
+ "\n",
+ " windBearing temperature dewPoint pressure \\\n",
+ "count 200000.000000 200000.00000 200000.000000 200000.000000 \n",
+ "mean 194.297550 11.36635 7.243470 1012.911028 \n",
+ "std 90.607297 6.07569 5.187453 10.744543 \n",
+ "min 0.000000 -3.86000 -8.920000 975.740000 \n",
+ "25% 121.000000 6.98000 3.370000 1006.910000 \n",
+ "50% 215.000000 11.14000 7.470000 1013.730000 \n",
+ "75% 254.000000 15.76000 11.380000 1020.130000 \n",
+ "max 359.000000 32.40000 19.880000 1040.130000 \n",
+ "\n",
+ " apparentTemperature windSpeed humidity holiday \n",
+ "count 200000.000000 200000.000000 200000.000000 200000.000000 \n",
+ "mean 10.263340 3.901174 0.775153 0.022905 \n",
+ "std 7.256847 2.023303 0.144399 0.149601 \n",
+ "min -8.880000 0.040000 0.230000 0.000000 \n",
+ "25% 4.430000 2.420000 0.690000 0.000000 \n",
+ "50% 11.140000 3.670000 0.810000 0.000000 \n",
+ "75% 15.760000 5.070000 0.890000 0.000000 \n",
+ "max 32.420000 14.800000 1.000000 1.000000 "
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df.describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:16.408599Z",
+ "start_time": "2020-10-19T05:40:15.658453Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " energy(kWh/hh) | \n",
+ " month | \n",
+ " day | \n",
+ " week | \n",
+ " hour | \n",
+ " minute | \n",
+ " dayofweek | \n",
+ " visibility | \n",
+ " windBearing | \n",
+ " temperature | \n",
+ " dewPoint | \n",
+ " pressure | \n",
+ " apparentTemperature | \n",
+ " windSpeed | \n",
+ " humidity | \n",
+ " holiday | \n",
+ " Acorn_grouped | \n",
+ " LCLid | \n",
+ " stdorToU | \n",
+ "
\n",
+ " \n",
+ " | Date | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2013-01-01 12:30:00 | \n",
+ " -0.612594 | \n",
+ " -1.679969 | \n",
+ " -1.679802 | \n",
+ " -1.777456 | \n",
+ " 0.071612 | \n",
+ " 1.0 | \n",
+ " -0.997790 | \n",
+ " 0.639489 | \n",
+ " 0.680989 | \n",
+ " -1.210787 | \n",
+ " -0.995379 | \n",
+ " 0.452228 | \n",
+ " -1.338510 | \n",
+ " 0.122981 | \n",
+ " 0.656841 | \n",
+ " 6.531354 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-01-01 13:00:00 | \n",
+ " -0.591113 | \n",
+ " -1.679969 | \n",
+ " -1.679802 | \n",
+ " -1.777456 | \n",
+ " 0.216066 | \n",
+ " -1.0 | \n",
+ " -0.997790 | \n",
+ " 0.615641 | \n",
+ " 0.625806 | \n",
+ " -1.222309 | \n",
+ " -0.985741 | \n",
+ " 0.508071 | \n",
+ " -1.320596 | \n",
+ " -0.050004 | \n",
+ " 0.726094 | \n",
+ " 6.531354 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-01-01 13:30:00 | \n",
+ " -0.597080 | \n",
+ " -1.679969 | \n",
+ " -1.679802 | \n",
+ " -1.777456 | \n",
+ " 0.216066 | \n",
+ " 1.0 | \n",
+ " -0.997790 | \n",
+ " 0.615641 | \n",
+ " 0.625806 | \n",
+ " -1.222309 | \n",
+ " -0.985741 | \n",
+ " 0.508071 | \n",
+ " -1.320596 | \n",
+ " -0.050004 | \n",
+ " 0.726094 | \n",
+ " 6.531354 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-01-01 14:00:00 | \n",
+ " -0.623334 | \n",
+ " -1.679969 | \n",
+ " -1.679802 | \n",
+ " -1.777456 | \n",
+ " 0.360520 | \n",
+ " -1.0 | \n",
+ " -0.997790 | \n",
+ " 0.639489 | \n",
+ " 0.570623 | \n",
+ " -1.316125 | \n",
+ " -1.005018 | \n",
+ " 0.579735 | \n",
+ " -1.364693 | \n",
+ " -0.321838 | \n",
+ " 0.933852 | \n",
+ " 6.531354 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-01-01 14:30:00 | \n",
+ " -0.573213 | \n",
+ " -1.679969 | \n",
+ " -1.679802 | \n",
+ " -1.777456 | \n",
+ " 0.360520 | \n",
+ " 1.0 | \n",
+ " -0.997790 | \n",
+ " 0.639489 | \n",
+ " 0.570623 | \n",
+ " -1.316125 | \n",
+ " -1.005018 | \n",
+ " 0.579735 | \n",
+ " -1.364693 | \n",
+ " -0.321838 | \n",
+ " 0.933852 | \n",
+ " 6.531354 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 2014-02-27 22:00:00 | \n",
+ " -0.594693 | \n",
+ " -1.387820 | \n",
+ " 1.270734 | \n",
+ " -1.240004 | \n",
+ " 1.516154 | \n",
+ " -1.0 | \n",
+ " 0.002646 | \n",
+ " 0.901813 | \n",
+ " 0.239523 | \n",
+ " -1.195974 | \n",
+ " -1.080200 | \n",
+ " -0.673928 | \n",
+ " -1.220001 | \n",
+ " -0.435514 | \n",
+ " 0.449083 | \n",
+ " -0.153108 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2014-02-27 22:30:00 | \n",
+ " -0.666296 | \n",
+ " -1.387820 | \n",
+ " 1.270734 | \n",
+ " -1.240004 | \n",
+ " 1.516154 | \n",
+ " 1.0 | \n",
+ " 0.002646 | \n",
+ " 0.901813 | \n",
+ " 0.239523 | \n",
+ " -1.195974 | \n",
+ " -1.080200 | \n",
+ " -0.673928 | \n",
+ " -1.220001 | \n",
+ " -0.435514 | \n",
+ " 0.449083 | \n",
+ " -0.153108 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2014-02-27 23:00:00 | \n",
+ " -0.644815 | \n",
+ " -1.387820 | \n",
+ " 1.270734 | \n",
+ " -1.240004 | \n",
+ " 1.660608 | \n",
+ " -1.0 | \n",
+ " 0.002646 | \n",
+ " 0.912034 | \n",
+ " 0.062936 | \n",
+ " -1.223955 | \n",
+ " -1.085983 | \n",
+ " -0.771652 | \n",
+ " -1.218623 | \n",
+ " -0.568959 | \n",
+ " 0.518336 | \n",
+ " -0.153108 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2014-02-27 23:30:00 | \n",
+ " -0.582760 | \n",
+ " -1.387820 | \n",
+ " 1.270734 | \n",
+ " -1.240004 | \n",
+ " 1.660608 | \n",
+ " 1.0 | \n",
+ " 0.002646 | \n",
+ " 0.912034 | \n",
+ " 0.062936 | \n",
+ " -1.223955 | \n",
+ " -1.085983 | \n",
+ " -0.771652 | \n",
+ " -1.218623 | \n",
+ " -0.568959 | \n",
+ " 0.518336 | \n",
+ " -0.153108 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2014-02-28 00:00:00 | \n",
+ " -0.706870 | \n",
+ " -1.387820 | \n",
+ " 1.384216 | \n",
+ " -1.240004 | \n",
+ " -1.661838 | \n",
+ " -1.0 | \n",
+ " 0.502864 | \n",
+ " 0.435080 | \n",
+ " -0.047431 | \n",
+ " -1.243706 | \n",
+ " -1.101405 | \n",
+ " -0.869376 | \n",
+ " -1.211733 | \n",
+ " -0.677692 | \n",
+ " 0.518336 | \n",
+ " -0.153108 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
200000 rows × 19 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " energy(kWh/hh) month day week hour \\\n",
+ "Date \n",
+ "2013-01-01 12:30:00 -0.612594 -1.679969 -1.679802 -1.777456 0.071612 \n",
+ "2013-01-01 13:00:00 -0.591113 -1.679969 -1.679802 -1.777456 0.216066 \n",
+ "2013-01-01 13:30:00 -0.597080 -1.679969 -1.679802 -1.777456 0.216066 \n",
+ "2013-01-01 14:00:00 -0.623334 -1.679969 -1.679802 -1.777456 0.360520 \n",
+ "2013-01-01 14:30:00 -0.573213 -1.679969 -1.679802 -1.777456 0.360520 \n",
+ "... ... ... ... ... ... \n",
+ "2014-02-27 22:00:00 -0.594693 -1.387820 1.270734 -1.240004 1.516154 \n",
+ "2014-02-27 22:30:00 -0.666296 -1.387820 1.270734 -1.240004 1.516154 \n",
+ "2014-02-27 23:00:00 -0.644815 -1.387820 1.270734 -1.240004 1.660608 \n",
+ "2014-02-27 23:30:00 -0.582760 -1.387820 1.270734 -1.240004 1.660608 \n",
+ "2014-02-28 00:00:00 -0.706870 -1.387820 1.384216 -1.240004 -1.661838 \n",
+ "\n",
+ " minute dayofweek visibility windBearing temperature \\\n",
+ "Date \n",
+ "2013-01-01 12:30:00 1.0 -0.997790 0.639489 0.680989 -1.210787 \n",
+ "2013-01-01 13:00:00 -1.0 -0.997790 0.615641 0.625806 -1.222309 \n",
+ "2013-01-01 13:30:00 1.0 -0.997790 0.615641 0.625806 -1.222309 \n",
+ "2013-01-01 14:00:00 -1.0 -0.997790 0.639489 0.570623 -1.316125 \n",
+ "2013-01-01 14:30:00 1.0 -0.997790 0.639489 0.570623 -1.316125 \n",
+ "... ... ... ... ... ... \n",
+ "2014-02-27 22:00:00 -1.0 0.002646 0.901813 0.239523 -1.195974 \n",
+ "2014-02-27 22:30:00 1.0 0.002646 0.901813 0.239523 -1.195974 \n",
+ "2014-02-27 23:00:00 -1.0 0.002646 0.912034 0.062936 -1.223955 \n",
+ "2014-02-27 23:30:00 1.0 0.002646 0.912034 0.062936 -1.223955 \n",
+ "2014-02-28 00:00:00 -1.0 0.502864 0.435080 -0.047431 -1.243706 \n",
+ "\n",
+ " dewPoint pressure apparentTemperature windSpeed \\\n",
+ "Date \n",
+ "2013-01-01 12:30:00 -0.995379 0.452228 -1.338510 0.122981 \n",
+ "2013-01-01 13:00:00 -0.985741 0.508071 -1.320596 -0.050004 \n",
+ "2013-01-01 13:30:00 -0.985741 0.508071 -1.320596 -0.050004 \n",
+ "2013-01-01 14:00:00 -1.005018 0.579735 -1.364693 -0.321838 \n",
+ "2013-01-01 14:30:00 -1.005018 0.579735 -1.364693 -0.321838 \n",
+ "... ... ... ... ... \n",
+ "2014-02-27 22:00:00 -1.080200 -0.673928 -1.220001 -0.435514 \n",
+ "2014-02-27 22:30:00 -1.080200 -0.673928 -1.220001 -0.435514 \n",
+ "2014-02-27 23:00:00 -1.085983 -0.771652 -1.218623 -0.568959 \n",
+ "2014-02-27 23:30:00 -1.085983 -0.771652 -1.218623 -0.568959 \n",
+ "2014-02-28 00:00:00 -1.101405 -0.869376 -1.211733 -0.677692 \n",
+ "\n",
+ " humidity holiday Acorn_grouped LCLid stdorToU \n",
+ "Date \n",
+ "2013-01-01 12:30:00 0.656841 6.531354 1.0 0.0 0.0 \n",
+ "2013-01-01 13:00:00 0.726094 6.531354 1.0 0.0 0.0 \n",
+ "2013-01-01 13:30:00 0.726094 6.531354 1.0 0.0 0.0 \n",
+ "2013-01-01 14:00:00 0.933852 6.531354 1.0 0.0 0.0 \n",
+ "2013-01-01 14:30:00 0.933852 6.531354 1.0 0.0 0.0 \n",
+ "... ... ... ... ... ... \n",
+ "2014-02-27 22:00:00 0.449083 -0.153108 0.0 6.0 1.0 \n",
+ "2014-02-27 22:30:00 0.449083 -0.153108 0.0 6.0 1.0 \n",
+ "2014-02-27 23:00:00 0.518336 -0.153108 0.0 6.0 1.0 \n",
+ "2014-02-27 23:30:00 0.518336 -0.153108 0.0 6.0 1.0 \n",
+ "2014-02-28 00:00:00 0.518336 -0.153108 0.0 6.0 1.0 \n",
+ "\n",
+ "[200000 rows x 19 columns]"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import sklearn\n",
+ "from sklearn.preprocessing import StandardScaler, OrdinalEncoder\n",
+ "from sklearn_pandas import DataFrameMapper\n",
+ "\n",
+ "columns_input_numeric = list(df.drop(columns=columns_target)._get_numeric_data().columns)\n",
+ "columns_categorical = list(set(df.columns)-set(columns_input_numeric)-set(columns_target))\n",
+ "\n",
+ "output_scalers = [([n], StandardScaler()) for n in columns_target]\n",
+ "transformers=output_scalers + \\\n",
+ "[([n], StandardScaler()) for n in columns_input_numeric] + \\\n",
+ "[([n], OrdinalEncoder()) for n in columns_categorical]\n",
+ "scaler = DataFrameMapper(transformers, df_out=True)\n",
+ "df_norm = scaler.fit_transform(df)\n",
+ "df_norm"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:16.481582Z",
+ "start_time": "2020-10-19T05:40:16.413550Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "StandardScaler()"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "output_scaler = next(filter(lambda r:r[0][0] in columns_target, scaler.features))[-1]\n",
+ "output_scaler"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:16.692901Z",
+ "start_time": "2020-10-19T05:40:16.485394Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2013-10-08 00:00:00\n"
+ ]
+ }
+ ],
+ "source": [
+ "# split data, with the test in the future\n",
+ "\n",
+ "d0 =df_norm.index.min()\n",
+ "d1 = df_norm.index.max()\n",
+ "split_time = d0+(d1-d0)*0.8\n",
+ "split_time = split_time.round('1D')\n",
+ "print(split_time)\n",
+ "df_train = df_norm.groupby('LCLid').apply(lambda d:d.loc[:split_time]).reset_index(level=0, drop=True)\n",
+ "df_test = df_norm.groupby('LCLid').apply(lambda d:d.loc[split_time:]).reset_index(level=0, drop=True)\n",
+ "# df_test"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.037348Z",
+ "start_time": "2020-10-19T05:40:16.696237Z"
+ },
+ "lines_to_next_cell": 0
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ ", , , , , , ])>\n",
+ ", , , , , ])>\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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LGAu5jOZTIiLi4Msy+jTpw9KSkosTe5BQUF4+umn8fzzzyMxMdEyBM2AAQNw6dIlfPDBBygrK8M777yDG2+80ZdPw6uYM+2YO23clbf27dujffv2vnwqXsOcacO8acO8aeeu3FVNQNUQ1IecBcQrlZiYiIkTJ6K4uBitWrVCXFwcgoODceLECcvFUZIk4ZFHHsGKFSss6/36669YvXo1evTogenTpzeoK3CZM+2YO22YN/WYM22YN22YN+2YO/XqS84Coo9yUlISsrKyMGjQIADA119/jfbt2yM0NBTx8fH4+OOPIcsyCgsLsWjRIowYMQIxMTFISkoCAHTr1s2X4fsEc6Ydc6cN86Yec6YN86YN86Ydc6defclZQLQod+zYEbfddpulf8qNN96I7OxsDB48GLIsIz4+HjqdDjk5OdDpdIiJiQFgTrK/JNrbmDPtmDttmDf1mDNtmDdtmDftmDv16kvOAqKPcs0BpP/44w/LhVFjx47Fli1bMHXqVKSlpSE2NtYXIfod5kw75k4b5k095kwb5k0b5k075k69+pKzgCiUq1T9KikoKEC/fv0AAOHh4Xjsscdw8eJFxMTEIDo62pch+h3mTDvmThvmTT3mTBvmTRvmTTvmTr1Az1lAFcqSJMFoNOKaa65BSkoKlixZgoiICIwePRpdu3b1dXh+iTnTjrnThnlTjznThnnThnnTjrlTL9BzFnCF8vnz57F7925kZmbizjvvxJAhQ3wdll9jzrRj7rRh3tRjzrRh3rRh3rRj7tQL9JwFxKgX1nJycrBz507cd999lkGoyTnmTDvmThvmTT3mTBvmTRvmTTvmTr1AzlnAFcpERERERN4QEMPDERERERF5GwtlIiIiIiI7WCgTEREREdnBQpmIiIiIyA4WykREREREdrBQJiIiIiKyI6AmHCEiaijGjRuH/Px8BAUFQafToW3bthg0aBBiY2Oh0zlv48jMzMT48eOxbNkyBAUFeSliIqL6h4UyEZGfeuONN9CrVy+UlJQgMTERixcvRnJyMsaOHevr0IiIGgQWykREfq5x48bo168fIiMj8dZbb+G+++5DdnY2li9fjoyMDDRu3Bh33nknhg8fDgB45513AACjRo0CAEyePBldunTB1q1bsW7dOuTn56NTp0547rnn0KJFC189LSIiv8c+ykREAaJTp06Ijo7GyZMnERYWhvHjx2Px4sWYOHEiNm/ejP379wMA3n33XQDAkiVLsHTpUnTp0gX79+/H6tWr8corr+Drr79G165dMWvWLF8+HSIiv8dCmYgogERHR6OoqAg9evRA+/btodPpcN111+GOO+5AYmKiw/USEhLw0EMPoW3btggKCsJDDz2ECxcuICsry4vRExEFFna9ICIKILm5uYiIiMCZM2fw/fffIzU1FUajEUajEbfeeqvD9bKysrB48WJ8++23lvuEEMjNzWX3CyIiB1goExEFiOTkZOTm5qJr166YNm0a/va3v+HNN99EaGgolixZgsLCQgCAJEm11tXr9Rg6dCgGDhzo7bCJiAIWu14QEfm5kpIS/P7775g1axYGDhyI9u3bo7S0FBEREQgNDUVycjJ2795tWb5p06aQJAkZGRmW++666y6sWbMGFy9etGzz119/9fpzISIKJJIQQvg6CCIismU9jrIkSWjbti0GDhyIu+++GzqdDvv27cO3336LoqIidO/eHS1atEBxcTFeeuklAEBcXBw2bdoEk8mESZMmoUuXLti5cyfWrl2L7OxsNG7cGH/605841BwRkRMslImIiIiI7GDXCyIiIiIiO1goExERERHZwUKZiIiIiMgOFspERERERHawUCYiIiIisoOFMhERERGRHSyUiYiIiIjsYKFMRERERGTH/wfTMW+yhbmHMAAAAABJRU5ErkJggg==\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Show split\n",
+ "df_train['energy(kWh/hh)'].plot(label='train')\n",
+ "df_test['energy(kWh/hh)'].plot(label='test')\n",
+ "plt.ylabel('energy(kWh/hh)')\n",
+ "plt.legend()\n",
+ "# ### Dataset\n",
+ "# These are the columns that we wont know in the future\n",
+ "# We need to blank them out in x_future\n",
+ "columns_blank=['visibility',\n",
+ " 'windBearing', 'temperature', 'dewPoint', 'pressure',\n",
+ " 'apparentTemperature', 'windSpeed', 'humidity']\n",
+ "df_trains = [d.resample(freq).first().ffill().dropna() for _,d in df_train.groupby('LCLid')]\n",
+ "df_tests = [d.resample(freq).first().ffill().dropna() for _,d in df_test.groupby('LCLid')]\n",
+ "ds_train = Seq2SeqDataSets(df_trains,\n",
+ " window_past=window_past,\n",
+ " window_future=window_future,\n",
+ " columns_blank=columns_blank)\n",
+ "ds_test = Seq2SeqDataSets(df_tests,\n",
+ " window_past=window_past,\n",
+ " window_future=window_future,\n",
+ " columns_blank=columns_blank)\n",
+ "print(ds_train)\n",
+ "print(ds_test)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.098542Z",
+ "start_time": "2020-10-19T05:40:18.040901Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[array([[ 0.6572206 , -0.09105182, 0.6410791 , ..., 0. ,\n",
+ " -2. , 1. ],\n",
+ " [ 0.6572206 , -0.09105182, 0.6410791 , ..., 0. ,\n",
+ " -1.9791666 , 1. ],\n",
+ " [ 0.6572206 , -0.09105182, 0.6410791 , ..., 0. ,\n",
+ " -1.9583334 , 1. ],\n",
+ " ...,\n",
+ " [ 0.6572206 , 0.02243031, 0.70826066, ..., 0. ,\n",
+ " -0.0625 , 1. ],\n",
+ " [ 0.6572206 , 0.02243031, 0.70826066, ..., 0. ,\n",
+ " -0.04166667, 1. ],\n",
+ " [ 0.6572206 , 0.13591245, 0.70826066, ..., 0. ,\n",
+ " -0.02083333, 1. ]], dtype=float32),\n",
+ " array([[-0.6901632 ],\n",
+ " [-0.68300295],\n",
+ " [-0.6782295 ],\n",
+ " [-0.6961301 ],\n",
+ " [-0.6686825 ],\n",
+ " [-0.6997102 ],\n",
+ " [-0.6674891 ],\n",
+ " [-0.6006602 ],\n",
+ " [-0.6185608 ],\n",
+ " [-0.6495885 ],\n",
+ " [-0.663909 ],\n",
+ " [-0.6376548 ],\n",
+ " [-0.6281078 ],\n",
+ " [-0.70329034],\n",
+ " [-0.68777645],\n",
+ " [-0.7056771 ],\n",
+ " [ 0.14281139],\n",
+ " [-0.3774994 ],\n",
+ " [-0.53144455],\n",
+ " [ 0.71324384],\n",
+ " [-0.4896765 ],\n",
+ " [-0.71164393],\n",
+ " [-0.68061626],\n",
+ " [-0.2844163 ],\n",
+ " [-0.40972048],\n",
+ " [-0.63049453],\n",
+ " [-0.60782045],\n",
+ " [-0.6853897 ],\n",
+ " [-0.6865831 ],\n",
+ " [-0.7176108 ],\n",
+ " [-0.64600843],\n",
+ " [-0.60424036],\n",
+ " [-0.5195108 ],\n",
+ " [-0.6865831 ],\n",
+ " [-0.67106926],\n",
+ " [-0.475356 ],\n",
+ " [-0.45506868],\n",
+ " [-0.5386048 ],\n",
+ " [-0.37869278],\n",
+ " [-0.5016102 ],\n",
+ " [-0.5242843 ],\n",
+ " [-0.5517319 ],\n",
+ " [-0.6054337 ],\n",
+ " [-0.66987586],\n",
+ " [-0.66032887],\n",
+ " [-0.68300295],\n",
+ " [-0.6579421 ],\n",
+ " [-0.5529253 ],\n",
+ " [-0.6627156 ],\n",
+ " [-0.66152227],\n",
+ " [-0.67942286],\n",
+ " [-0.70209694],\n",
+ " [-0.66987586],\n",
+ " [-0.7044837 ],\n",
+ " [-0.6782295 ],\n",
+ " [-0.69851685],\n",
+ " [-0.68896985],\n",
+ " [-0.6901632 ],\n",
+ " [-0.69732344],\n",
+ " [-0.5397982 ],\n",
+ " [-0.5660524 ],\n",
+ " [-0.55889213],\n",
+ " [-0.71164393],\n",
+ " [-0.68419635],\n",
+ " [-0.7104506 ],\n",
+ " [-0.5624723 ],\n",
+ " [-0.66152227],\n",
+ " [-0.6245277 ],\n",
+ " [-0.68419635],\n",
+ " [-0.67106926],\n",
+ " [-0.70806384],\n",
+ " [-0.6185608 ],\n",
+ " [-0.71164393],\n",
+ " [-0.67942286],\n",
+ " [-0.5672458 ],\n",
+ " [ 0.1690656 ],\n",
+ " [-0.7235777 ],\n",
+ " [-0.68777645],\n",
+ " [-0.65078187],\n",
+ " [-0.58991987],\n",
+ " [-0.6281078 ],\n",
+ " [-0.5433783 ],\n",
+ " [-0.5302512 ],\n",
+ " [-0.53621805],\n",
+ " [-0.4538753 ],\n",
+ " [-0.5338313 ],\n",
+ " [-0.45984215],\n",
+ " [-0.55889213],\n",
+ " [-0.61259395],\n",
+ " [-0.6245277 ],\n",
+ " [-0.6627156 ],\n",
+ " [-0.6901632 ],\n",
+ " [-0.67942286],\n",
+ " [-0.7092572 ],\n",
+ " [-0.6853897 ],\n",
+ " [-0.70687044]], dtype=float32),\n",
+ " array([[0.6572206 , 0.13591245, 0.70826066, ..., 0. , 0. ,\n",
+ " 0. ],\n",
+ " [0.6572206 , 0.13591245, 0.70826066, ..., 0. , 0.02083333,\n",
+ " 0. ],\n",
+ " [0.6572206 , 0.13591245, 0.70826066, ..., 0. , 0.04166667,\n",
+ " 0. ],\n",
+ " ...,\n",
+ " [0.6572206 , 0.2493946 , 0.70826066, ..., 0. , 1.9166666 ,\n",
+ " 0. ],\n",
+ " [0.6572206 , 0.2493946 , 0.70826066, ..., 0. , 1.9375 ,\n",
+ " 0. ],\n",
+ " [0.6572206 , 0.2493946 , 0.70826066, ..., 0. , 1.9583334 ,\n",
+ " 0. ]], dtype=float32),\n",
+ " array([[-0.67584276],\n",
+ " [-0.71164393],\n",
+ " [-0.6782295 ],\n",
+ " [-0.70209694],\n",
+ " [-0.6818096 ],\n",
+ " [-0.69374335],\n",
+ " [-0.6901632 ],\n",
+ " [-0.68777645],\n",
+ " [-0.69732344],\n",
+ " [-0.68061626],\n",
+ " [-0.70687044],\n",
+ " [-0.5660524 ],\n",
+ " [-0.51831746],\n",
+ " [-0.6197542 ],\n",
+ " [-0.7223843 ],\n",
+ " [-0.52189755],\n",
+ " [-0.57917947],\n",
+ " [-0.673456 ],\n",
+ " [-0.6782295 ],\n",
+ " [-0.6591355 ],\n",
+ " [-0.6901632 ],\n",
+ " [-0.6066271 ],\n",
+ " [-0.69254994],\n",
+ " [-0.6376548 ],\n",
+ " [-0.6066271 ],\n",
+ " [-0.5481518 ],\n",
+ " [-0.5755994 ],\n",
+ " [-0.34766507],\n",
+ " [-0.71283734],\n",
+ " [-0.69851685],\n",
+ " [-0.66987586],\n",
+ " [-0.6686825 ],\n",
+ " [-0.52189755],\n",
+ " [-0.50399697],\n",
+ " [-0.68896985],\n",
+ " [-0.5302512 ],\n",
+ " [-0.28202954],\n",
+ " [-0.5111572 ],\n",
+ " [-0.50399697],\n",
+ " [-0.5111572 ],\n",
+ " [-0.3942066 ],\n",
+ " [-0.5254777 ],\n",
+ " [-0.54576504],\n",
+ " [-0.56008554],\n",
+ " [-0.4347813 ],\n",
+ " [-0.6102072 ],\n",
+ " [-0.6770361 ],\n",
+ " [-0.69851685],\n",
+ " [-0.69374335],\n",
+ " [-0.68896985],\n",
+ " [-0.7104506 ],\n",
+ " [-0.6770361 ],\n",
+ " [-0.71283734],\n",
+ " [-0.6722626 ],\n",
+ " [-0.68419635],\n",
+ " [-0.67106926],\n",
+ " [-0.66629577],\n",
+ " [-0.6949367 ],\n",
+ " [-0.6913566 ],\n",
+ " [-0.5672458 ],\n",
+ " [-0.55053854],\n",
+ " [ 0.2860162 ],\n",
+ " [-0.66510236],\n",
+ " [-0.66629577],\n",
+ " [-0.5195108 ],\n",
+ " [-0.63168794],\n",
+ " [-0.7223843 ],\n",
+ " [-0.66032887],\n",
+ " [-0.68061626],\n",
+ " [-0.67464936],\n",
+ " [-0.5016102 ],\n",
+ " [-0.55053854],\n",
+ " [-0.6197542 ],\n",
+ " [-0.36079216],\n",
+ " [-0.49683672],\n",
+ " [ 0.26572883],\n",
+ " [-0.67942286],\n",
+ " [-0.69254994],\n",
+ " [-0.6531686 ],\n",
+ " [-0.5779861 ],\n",
+ " [-0.63049453],\n",
+ " [-0.65078187],\n",
+ " [-0.6627156 ],\n",
+ " [-0.60424036],\n",
+ " [-0.52667105],\n",
+ " [-0.51235056],\n",
+ " [-0.45506868],\n",
+ " [-0.48132288],\n",
+ " [-0.54218495],\n",
+ " [-0.39181986],\n",
+ " [-0.5612789 ],\n",
+ " [-0.6627156 ],\n",
+ " [-0.6770361 ],\n",
+ " [-0.6865831 ],\n",
+ " [-0.6818096 ]], dtype=float32)]"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# we can treat it like an array\n",
+ "ds_train[0]\n",
+ "len(ds_train)\n",
+ "ds_train[-1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.507696Z",
+ "start_time": "2020-10-19T05:40:18.102493Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " month | \n",
+ " day | \n",
+ " week | \n",
+ " hour | \n",
+ " minute | \n",
+ " dayofweek | \n",
+ " visibility | \n",
+ " windBearing | \n",
+ " temperature | \n",
+ " dewPoint | \n",
+ " pressure | \n",
+ " apparentTemperature | \n",
+ " windSpeed | \n",
+ " humidity | \n",
+ " holiday | \n",
+ " Acorn_grouped | \n",
+ " LCLid | \n",
+ " stdorToU | \n",
+ " tsp_days | \n",
+ " is_past | \n",
+ "
\n",
+ " \n",
+ " | Date | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2013-09-16 16:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.02243 | \n",
+ " 0.708261 | \n",
+ " 0.649428 | \n",
+ " 1.0 | \n",
+ " -1.498008 | \n",
+ " 0.264739 | \n",
+ " 0.967943 | \n",
+ " 0.377514 | \n",
+ " -0.582844 | \n",
+ " -1.010844 | \n",
+ " 0.468064 | \n",
+ " 1.477205 | \n",
+ " -1.697750 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " -0.104167 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-16 17:00:00 | \n",
+ " 0.657221 | \n",
+ " 0.02243 | \n",
+ " 0.708261 | \n",
+ " 0.793883 | \n",
+ " -1.0 | \n",
+ " -1.498008 | \n",
+ " 0.264739 | \n",
+ " 0.978979 | \n",
+ " 0.288635 | \n",
+ " -0.665737 | \n",
+ " -0.988507 | \n",
+ " 0.393651 | \n",
+ " 1.076869 | \n",
+ " -1.697750 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " -0.083333 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-16 17:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.02243 | \n",
+ " 0.708261 | \n",
+ " 0.793883 | \n",
+ " 1.0 | \n",
+ " -1.498008 | \n",
+ " 0.264739 | \n",
+ " 0.978979 | \n",
+ " 0.288635 | \n",
+ " -0.665737 | \n",
+ " -0.988507 | \n",
+ " 0.393651 | \n",
+ " 1.076869 | \n",
+ " -1.697750 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " -0.062500 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-16 18:00:00 | \n",
+ " 0.657221 | \n",
+ " 0.02243 | \n",
+ " 0.708261 | \n",
+ " 0.938337 | \n",
+ " -1.0 | \n",
+ " -1.498008 | \n",
+ " 0.516843 | \n",
+ " 0.923796 | \n",
+ " 0.138857 | \n",
+ " -0.559712 | \n",
+ " -0.943833 | \n",
+ " 0.268252 | \n",
+ " 0.557914 | \n",
+ " -1.282234 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " -0.041667 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-16 18:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.02243 | \n",
+ " 0.708261 | \n",
+ " 0.938337 | \n",
+ " 1.0 | \n",
+ " -1.498008 | \n",
+ " 0.516843 | \n",
+ " 0.923796 | \n",
+ " 0.138857 | \n",
+ " -0.559712 | \n",
+ " -0.943833 | \n",
+ " 0.268252 | \n",
+ " 0.557914 | \n",
+ " -1.282234 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " -0.020833 | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " month day week hour minute dayofweek \\\n",
+ "Date \n",
+ "2013-09-16 16:30:00 0.657221 0.02243 0.708261 0.649428 1.0 -1.498008 \n",
+ "2013-09-16 17:00:00 0.657221 0.02243 0.708261 0.793883 -1.0 -1.498008 \n",
+ "2013-09-16 17:30:00 0.657221 0.02243 0.708261 0.793883 1.0 -1.498008 \n",
+ "2013-09-16 18:00:00 0.657221 0.02243 0.708261 0.938337 -1.0 -1.498008 \n",
+ "2013-09-16 18:30:00 0.657221 0.02243 0.708261 0.938337 1.0 -1.498008 \n",
+ "\n",
+ " visibility windBearing temperature dewPoint pressure \\\n",
+ "Date \n",
+ "2013-09-16 16:30:00 0.264739 0.967943 0.377514 -0.582844 -1.010844 \n",
+ "2013-09-16 17:00:00 0.264739 0.978979 0.288635 -0.665737 -0.988507 \n",
+ "2013-09-16 17:30:00 0.264739 0.978979 0.288635 -0.665737 -0.988507 \n",
+ "2013-09-16 18:00:00 0.516843 0.923796 0.138857 -0.559712 -0.943833 \n",
+ "2013-09-16 18:30:00 0.516843 0.923796 0.138857 -0.559712 -0.943833 \n",
+ "\n",
+ " apparentTemperature windSpeed humidity holiday \\\n",
+ "Date \n",
+ "2013-09-16 16:30:00 0.468064 1.477205 -1.697750 -0.153108 \n",
+ "2013-09-16 17:00:00 0.393651 1.076869 -1.697750 -0.153108 \n",
+ "2013-09-16 17:30:00 0.393651 1.076869 -1.697750 -0.153108 \n",
+ "2013-09-16 18:00:00 0.268252 0.557914 -1.282234 -0.153108 \n",
+ "2013-09-16 18:30:00 0.268252 0.557914 -1.282234 -0.153108 \n",
+ "\n",
+ " Acorn_grouped LCLid stdorToU tsp_days is_past \n",
+ "Date \n",
+ "2013-09-16 16:30:00 1.0 0.0 0.0 -0.104167 1.0 \n",
+ "2013-09-16 17:00:00 1.0 0.0 0.0 -0.083333 1.0 \n",
+ "2013-09-16 17:30:00 1.0 0.0 0.0 -0.062500 1.0 \n",
+ "2013-09-16 18:00:00 1.0 0.0 0.0 -0.041667 1.0 \n",
+ "2013-09-16 18:30:00 1.0 0.0 0.0 -0.020833 1.0 "
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# We can get rows\n",
+ "x_past, y_past, x_future, y_future = ds_train.get_rows(10)\n",
+ "\n",
+ "# Plot one instance, this is what the model sees\n",
+ "y_past['energy(kWh/hh)'].plot(label='past')\n",
+ "y_future['energy(kWh/hh)'].plot(ax=plt.gca(), label='future')\n",
+ "plt.legend()\n",
+ "plt.ylabel('energy(kWh/hh)')\n",
+ "\n",
+ "# Notice we've added on two new columns tsp (time since present) and is_past\n",
+ "x_past.tail()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.582960Z",
+ "start_time": "2020-10-19T05:40:18.511125Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " month | \n",
+ " day | \n",
+ " week | \n",
+ " hour | \n",
+ " minute | \n",
+ " dayofweek | \n",
+ " visibility | \n",
+ " windBearing | \n",
+ " temperature | \n",
+ " dewPoint | \n",
+ " pressure | \n",
+ " apparentTemperature | \n",
+ " windSpeed | \n",
+ " humidity | \n",
+ " holiday | \n",
+ " Acorn_grouped | \n",
+ " LCLid | \n",
+ " stdorToU | \n",
+ " tsp_days | \n",
+ " is_past | \n",
+ "
\n",
+ " \n",
+ " | Date | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2013-09-18 16:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.249395 | \n",
+ " 0.708261 | \n",
+ " 0.649428 | \n",
+ " 1.0 | \n",
+ " -0.497572 | \n",
+ " 0.83027 | \n",
+ " 1.729473 | \n",
+ " 0.257362 | \n",
+ " 0.411866 | \n",
+ " 0.461535 | \n",
+ " 0.367469 | \n",
+ " -1.033547 | \n",
+ " 0.10282 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.895833 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-18 17:00:00 | \n",
+ " 0.657221 | \n",
+ " 0.249395 | \n",
+ " 0.708261 | \n",
+ " 0.793883 | \n",
+ " -1.0 | \n",
+ " -0.497572 | \n",
+ " 0.83027 | \n",
+ " 1.729473 | \n",
+ " 0.257362 | \n",
+ " 0.411866 | \n",
+ " 0.461535 | \n",
+ " 0.367469 | \n",
+ " -1.033547 | \n",
+ " 0.10282 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.916667 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-18 17:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.249395 | \n",
+ " 0.708261 | \n",
+ " 0.793883 | \n",
+ " 1.0 | \n",
+ " -0.497572 | \n",
+ " 0.83027 | \n",
+ " 1.729473 | \n",
+ " 0.257362 | \n",
+ " 0.411866 | \n",
+ " 0.461535 | \n",
+ " 0.367469 | \n",
+ " -1.033547 | \n",
+ " 0.10282 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.937500 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-18 18:00:00 | \n",
+ " 0.657221 | \n",
+ " 0.249395 | \n",
+ " 0.708261 | \n",
+ " 0.938337 | \n",
+ " -1.0 | \n",
+ " -0.497572 | \n",
+ " 0.83027 | \n",
+ " 1.729473 | \n",
+ " 0.257362 | \n",
+ " 0.411866 | \n",
+ " 0.461535 | \n",
+ " 0.367469 | \n",
+ " -1.033547 | \n",
+ " 0.10282 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.958333 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2013-09-18 18:30:00 | \n",
+ " 0.657221 | \n",
+ " 0.249395 | \n",
+ " 0.708261 | \n",
+ " 0.938337 | \n",
+ " 1.0 | \n",
+ " -0.497572 | \n",
+ " 0.83027 | \n",
+ " 1.729473 | \n",
+ " 0.257362 | \n",
+ " 0.411866 | \n",
+ " 0.461535 | \n",
+ " 0.367469 | \n",
+ " -1.033547 | \n",
+ " 0.10282 | \n",
+ " -0.153108 | \n",
+ " 1.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 1.979167 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " month day week hour minute \\\n",
+ "Date \n",
+ "2013-09-18 16:30:00 0.657221 0.249395 0.708261 0.649428 1.0 \n",
+ "2013-09-18 17:00:00 0.657221 0.249395 0.708261 0.793883 -1.0 \n",
+ "2013-09-18 17:30:00 0.657221 0.249395 0.708261 0.793883 1.0 \n",
+ "2013-09-18 18:00:00 0.657221 0.249395 0.708261 0.938337 -1.0 \n",
+ "2013-09-18 18:30:00 0.657221 0.249395 0.708261 0.938337 1.0 \n",
+ "\n",
+ " dayofweek visibility windBearing temperature \\\n",
+ "Date \n",
+ "2013-09-18 16:30:00 -0.497572 0.83027 1.729473 0.257362 \n",
+ "2013-09-18 17:00:00 -0.497572 0.83027 1.729473 0.257362 \n",
+ "2013-09-18 17:30:00 -0.497572 0.83027 1.729473 0.257362 \n",
+ "2013-09-18 18:00:00 -0.497572 0.83027 1.729473 0.257362 \n",
+ "2013-09-18 18:30:00 -0.497572 0.83027 1.729473 0.257362 \n",
+ "\n",
+ " dewPoint pressure apparentTemperature windSpeed \\\n",
+ "Date \n",
+ "2013-09-18 16:30:00 0.411866 0.461535 0.367469 -1.033547 \n",
+ "2013-09-18 17:00:00 0.411866 0.461535 0.367469 -1.033547 \n",
+ "2013-09-18 17:30:00 0.411866 0.461535 0.367469 -1.033547 \n",
+ "2013-09-18 18:00:00 0.411866 0.461535 0.367469 -1.033547 \n",
+ "2013-09-18 18:30:00 0.411866 0.461535 0.367469 -1.033547 \n",
+ "\n",
+ " humidity holiday Acorn_grouped LCLid stdorToU \\\n",
+ "Date \n",
+ "2013-09-18 16:30:00 0.10282 -0.153108 1.0 0.0 0.0 \n",
+ "2013-09-18 17:00:00 0.10282 -0.153108 1.0 0.0 0.0 \n",
+ "2013-09-18 17:30:00 0.10282 -0.153108 1.0 0.0 0.0 \n",
+ "2013-09-18 18:00:00 0.10282 -0.153108 1.0 0.0 0.0 \n",
+ "2013-09-18 18:30:00 0.10282 -0.153108 1.0 0.0 0.0 \n",
+ "\n",
+ " tsp_days is_past \n",
+ "Date \n",
+ "2013-09-18 16:30:00 1.895833 0.0 \n",
+ "2013-09-18 17:00:00 1.916667 0.0 \n",
+ "2013-09-18 17:30:00 1.937500 0.0 \n",
+ "2013-09-18 18:00:00 1.958333 0.0 \n",
+ "2013-09-18 18:30:00 1.979167 0.0 "
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Notice we've hidden some future columns to prevent cheating\n",
+ "x_future.tail()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Plot helpers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.639326Z",
+ "start_time": "2020-10-19T05:40:18.586830Z"
+ },
+ "lines_to_end_of_cell_marker": 2,
+ "lines_to_next_cell": 0
+ },
+ "outputs": [],
+ "source": [
+ "from seq2seq_time.models.lstm_seq2seq import LSTMSeq2Seq\n",
+ "from seq2seq_time.models.lstm import LSTM\n",
+ "from seq2seq_time.models.baseline import BaselineLast\n",
+ "from seq2seq_time.models.transformer import Transformer\n",
+ "from seq2seq_time.models.transformer_seq2seq import TransformerSeq2Seq\n",
+ "# ## Plots"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.721649Z",
+ "start_time": "2020-10-19T05:40:18.644528Z"
+ },
+ "lines_to_next_cell": 0
+ },
+ "outputs": [],
+ "source": [
+ "def plot_prediction(ds_preds, i):\n",
+ " \"\"\"Plot a prediction into the future, at a single point in time.\"\"\"\n",
+ " d = ds_preds.isel(t_source=i)\n",
+ "\n",
+ " # Get arrays\n",
+ " xf = d.t_target\n",
+ " yp = d.y_pred\n",
+ " s = d.y_pred_std\n",
+ " yt = d.y_true\n",
+ " now = d.t_source.squeeze()\n",
+ " \n",
+ " \n",
+ " plt.figure(figsize=(12, 4))\n",
+ " \n",
+ " plt.scatter(xf, yt, label='true', c='k', s=6)\n",
+ " ylim = plt.ylim()\n",
+ "\n",
+ " # plot prediction\n",
+ " plt.fill_between(xf, yp-2*s, yp+2*s, alpha=0.25,\n",
+ " facecolor=\"b\",\n",
+ " interpolate=True,\n",
+ " label=\"2 std\",)\n",
+ " plt.plot(xf, yp, label='pred', c='b')\n",
+ "\n",
+ " # plot true\n",
+ " plt.scatter(\n",
+ " d.t_past,\n",
+ " d.y_past,\n",
+ " c='k',\n",
+ " s=6\n",
+ " )\n",
+ " \n",
+ " # plot a red line for now\n",
+ " plt.vlines(x=now, ymin=0, ymax=1, label='now', color='r')\n",
+ " plt.ylim(*ylim)\n",
+ "\n",
+ " now=pd.Timestamp(now.values)\n",
+ " plt.title(f'Prediction NLL={d.nll.mean().item():2.2g}')\n",
+ " plt.xlabel(f'{now.date()}')\n",
+ " plt.ylabel('energy(kWh/hh)')\n",
+ " plt.legend()\n",
+ " plt.xticks(rotation=45)\n",
+ " plt.show()\n",
+ " \n",
+ "def plot_performance(ds_preds, full=False):\n",
+ " \"\"\"Multiple plots using xr_preds\"\"\"\n",
+ " plot_prediction(ds_preds, 24)\n",
+ " plot_prediction(ds_preds, 480)\n",
+ "\n",
+ " ds_preds.mean('t_source').plot.scatter('t_ahead_hours', 'nll') # Mean over all predictions\n",
+ " n = len(ds_preds.t_source)\n",
+ " plt.ylabel('Negative Log Likelihood (lower is better)')\n",
+ " plt.xlabel('Hours ahead')\n",
+ " plt.title(f'NLL vs time ahead (no. samples={n})')\n",
+ " plt.show()\n",
+ "\n",
+ " # Make a plot of the NLL over time. Does this solution get worse with time?\n",
+ " if full:\n",
+ " d = ds_preds.mean('t_ahead').groupby('t_source').mean().plot.scatter('t_source', 'nll')\n",
+ " plt.xticks(rotation=45)\n",
+ " plt.title('NLL over source time (lower is better)')\n",
+ " plt.show()\n",
+ "\n",
+ " # A scatter plot is easy with xarray\n",
+ " if full:\n",
+ " plt.figure(figsize=(5, 5))\n",
+ " ds_preds.plot.scatter('y_true', 'y_pred', s=.01)\n",
+ " plt.show()\n",
+ " \n",
+ " print(f'mean_NLL {ds_preds.nll.mean().item():2.2f}')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-18T11:37:24.369849Z",
+ "start_time": "2020-10-18T11:37:21.316103Z"
+ },
+ "lines_to_next_cell": 2
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.780864Z",
+ "start_time": "2020-10-19T05:40:18.727453Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def plot_hist(trainer):\n",
+ " try:\n",
+ " df_hist = pd.read_csv(trainer.logger.experiment.metrics_file_path)\n",
+ " df_hist['epoch'] = df_hist['epoch'].ffill()\n",
+ " df_histe = df_hist.set_index('epoch').groupby('epoch').mean()\n",
+ " if len(df_histe)>1:\n",
+ " df_histe[['loss/train', 'loss/val']].plot(title='history')\n",
+ " return df_histe\n",
+ " except Exception:\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Lightning"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.846945Z",
+ "start_time": "2020-10-19T05:40:18.788688Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import pytorch_lightning as pl\n",
+ "\n",
+ "class PL_MODEL(pl.LightningModule):\n",
+ " def __init__(self, model, lr=3e-4, patience=2):\n",
+ " super().__init__()\n",
+ " self._model = model\n",
+ " self.lr = lr\n",
+ " self.patience = patience\n",
+ "\n",
+ " def forward(self, x_past, y_past, x_future, y_future=None):\n",
+ " \"\"\"Eval/Predict\"\"\"\n",
+ " y_dist = self._model(x_past, y_past, x_future)\n",
+ " return y_dist\n",
+ "\n",
+ " def training_step(self, batch, batch_idx, phase='train'):\n",
+ " x_past, y_past, x_future, y_future = batch\n",
+ " y_dist = self.forward(*batch)\n",
+ " loss = -y_dist.log_prob(y_future).mean()\n",
+ " self.log_dict({f'loss/{phase}':loss})\n",
+ " return loss\n",
+ "\n",
+ " def validation_step(self, batch, batch_idx):\n",
+ " return self.training_step(batch, batch_idx, phase='val')\n",
+ "\n",
+ " def configure_optimizers(self):\n",
+ " optim = torch.optim.Adam(self.parameters(), lr=self.lr)\n",
+ " scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n",
+ " optim,\n",
+ " patience=self.patience,\n",
+ " verbose=True,\n",
+ " min_lr=1e-7,\n",
+ " )\n",
+ " return {'optimizer': optim, 'lr_scheduler': scheduler, 'monitor': 'loss/val'}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:18.963085Z",
+ "start_time": "2020-10-19T05:40:18.850277Z"
+ },
+ "lines_to_next_cell": 2
+ },
+ "outputs": [],
+ "source": [
+ "# # Run\n",
+ "from torch.utils.data import DataLoader\n",
+ "from pytorch_lightning.loggers import CSVLogger\n",
+ "from pl_bolts.callbacks import PrintTableMetricsCallback\n",
+ "from pytorch_lightning.callbacks.early_stopping import EarlyStopping"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:19.027332Z",
+ "start_time": "2020-10-19T05:40:18.971531Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Init data\n",
+ "x_past, y_past, x_future, y_future = ds_train.get_rows(10)\n",
+ "input_size = x_past.shape[-1]\n",
+ "output_size = y_future.shape[-1]\n",
+ "\n",
+ "dl_train = DataLoader(ds_train,\n",
+ " batch_size=batch_size,\n",
+ " shuffle=True,\n",
+ " pin_memory=num_workers==0,\n",
+ " num_workers=num_workers)\n",
+ "dl_test = DataLoader(ds_test, batch_size=batch_size, num_workers=num_workers)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:26.916992Z",
+ "start_time": "2020-10-19T05:40:19.031698Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "GPU available: True, used: True\n",
+ "TPU available: False, using: 0 TPU cores\n",
+ "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]\n",
+ "\n",
+ " | Name | Type | Params\n",
+ "----------------------------------------\n",
+ "0 | _model | BaselineLast | 1 \n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='Validation sanity check'), FloatProgress(value=1.0, bar_style='info', layout=Layout…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "2c4e5be7f11243a4b5d70e14c95e23b2",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='Training'), FloatProgress(value=1.0, bar_style='info', layout=Layout(flex='2'), max…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='Validating'), FloatProgress(value=1.0, bar_style='info', layout=Layout(flex='2'), m…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "None\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "6d22af7a572d45c0bcf2769ce92dc32d",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='predict'), FloatProgress(value=0.0, max=27.0), HTML(value='')))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "baseline nll: 1.8\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Baseline model\n",
+ "pt_model = BaselineLast()\n",
+ "model = PL_MODEL(pt_model).to(device)\n",
+ "trainer = pl.Trainer(gpus=1,\n",
+ " max_epochs=1, \n",
+ " limit_train_batches=0.01,\n",
+ " logger=CSVLogger(\"logs\",\n",
+ " name=type(pt_model).__name__),\n",
+ " )\n",
+ "trainer.fit(model, dl_train, dl_test)\n",
+ "print(plot_hist(trainer))\n",
+ "ds_preds = predict(model.to(device), ds_test.datasets[0], batch_size, device=device, scaler=output_scaler)\n",
+ "print(f'baseline nll: {ds_preds.nll.mean().item():2.2g}')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T01:45:07.878992Z",
+ "start_time": "2020-10-19T01:45:07.775810Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:40:27.042145Z",
+ "start_time": "2020-10-19T05:40:26.922340Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "models = [\n",
+ "# BaselineLast(),\n",
+ " LSTM(input_size,\n",
+ " output_size,\n",
+ " hidden_size=80,\n",
+ " lstm_layers=3,\n",
+ " lstm_dropout=0.3),\n",
+ " Transformer(input_size,\n",
+ " output_size,\n",
+ " attention_dropout=0.3,\n",
+ " nhead=8,\n",
+ " nlayers=6,\n",
+ " hidden_size=64),\n",
+ " LSTMSeq2Seq(input_size,\n",
+ " output_size,\n",
+ " hidden_size=64,\n",
+ " lstm_layers=2,\n",
+ " lstm_dropout=0.25),\n",
+ " TransformerSeq2Seq(input_size,\n",
+ " output_size,\n",
+ " hidden_size=64,\n",
+ " nhead=8,\n",
+ " nlayers=4,\n",
+ " attention_dropout=0.3),\n",
+ "# Transformer(input_size,\n",
+ "# output_size,\n",
+ "# attention_dropout=0.2,\n",
+ "# nhead=8,\n",
+ "# nlayers=6,\n",
+ "# hidden_size=128),\n",
+ "# LSTM(input_size,\n",
+ "# output_size,\n",
+ "# hidden_size=128,\n",
+ "# lstm_layers=3,\n",
+ "# lstm_dropout=0.3),\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2020-10-19T05:22:41.150645Z",
+ "start_time": "2020-10-19T05:22:41.034632Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:28:34.600Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "GPU available: True, used: True\n",
+ "TPU available: False, using: 0 TPU cores\n",
+ "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]\n",
+ "Using native 16bit precision.\n",
+ "\n",
+ " | Name | Type | Params\n",
+ "--------------------------------\n",
+ "0 | _model | LSTM | 136 K \n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "LSTM\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='Validation sanity check'), FloatProgress(value=1.0, bar_style='info', layout=Layout…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "0b42a6f196704822bb114aa11c3ffa5b",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value='Training'), FloatProgress(value=1.0, bar_style='info', layout=Layout(flex='2'), max…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "for pt_model in models:\n",
+ " name = type(pt_model).__name__\n",
+ " print(name)\n",
+ "\n",
+ " # Wrap in lightning\n",
+ " patience = 2\n",
+ " model = PL_MODEL(pt_model, patience=patience, lr=3e-4).to(device)\n",
+ "\n",
+ " # Trainer\n",
+ " \n",
+ " trainer = pl.Trainer(gpus=1,\n",
+ " min_epochs=1,\n",
+ " max_epochs=10,\n",
+ " amp_level='O1',\n",
+ " precision=16,\n",
+ " gradient_clip_val=0.5,\n",
+ " logger=CSVLogger(\"logs\",\n",
+ " name=type(pt_model).__name__),\n",
+ " callbacks=[\n",
+ " EarlyStopping(monitor='loss/val', patience=patience*2),\n",
+ " PrintTableMetricsCallback()\n",
+ " ],\n",
+ " )\n",
+ "\n",
+ " # Train\n",
+ " trainer.fit(model, dl_train, dl_test)\n",
+ "\n",
+ " # Performance\n",
+ " print(plot_hist(trainer))\n",
+ "\n",
+ " ds_preds = predict(model.to(device),\n",
+ " ds_test.datasets[0],\n",
+ " batch_size,\n",
+ " device=device,\n",
+ " scaler=output_scaler)\n",
+ " plot_performance(ds_preds)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:39:06.800Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# holoviews pred"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import holoviews as hv\n",
+ "from holoviews import opts"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ },
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "def plot_prediction_now(t_source):\n",
+ " \"\"\"Plot predictions with holoviews\"\"\"\n",
+ "\n",
+ " # Let us pass in an int\n",
+ " if isinstance(t_source, int):\n",
+ " t_source = ds_preds.t_source[t_source].to_pandas()\n",
+ "\n",
+ " d = ds_preds.sel(t_source=t_source)\n",
+ "\n",
+ " # Sometimes there are duplicate time, take the first\n",
+ " if len(d.t_source.shape) and d.t_source.shape[0] > 0:\n",
+ " d = d.isel(t_source=0)\n",
+ " if len(d.t_source.shape) and d.t_source.shape[0] == 0:\n",
+ " return None\n",
+ "\n",
+ " now = d.t_source.to_pandas()\n",
+ "\n",
+ " # Plot true\n",
+ " x = np.concatenate([d.t_past, d.t_target])\n",
+ " yt = np.concatenate([d.y_past, d.y_true])\n",
+ " p = hv.Scatter({\n",
+ " 'x': x,\n",
+ " 'y': yt\n",
+ " }, label='true').opts(color='black', framewise=True)\n",
+ "\n",
+ " # Get arrays\n",
+ " xf = d.t_target.values\n",
+ " yp = d.y_pred\n",
+ " s = d.y_pred_std\n",
+ " p *= hv.Curve({\n",
+ " 'x': xf,\n",
+ " 'y': yp\n",
+ " }, label='pred').opts(color='blue')\n",
+ " p *= hv.Area((xf, yp - 2 * s, yp + 2 * s),\n",
+ " vdims=['y', 'y2'],\n",
+ " label='2*std').opts(alpha=0.5, line_width=0)\n",
+ "\n",
+ " # plot now line\n",
+ " p *= hv.VLine(now, label='now').opts(color='red')\n",
+ " return p.opts(title=f'Prediction at {now}. NLL={d.nll.mean().item():2.2f}')\n",
+ "\n",
+ "\n",
+ "dmap_pred = (hv.DynamicMap(plot_prediction_now, kdims=['t_source'])\n",
+ " .redim.values(t_source=ds_preds.t_source.to_pandas())\n",
+ " .opts(width=800,\n",
+ " height=300, \n",
+ " ))\n",
+ "dmap_pred"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def plot_predictions_vs_time(it_ahead):\n",
+ " \"\"\"Plot predictions vs time with holoviews\"\"\"\n",
+ "\n",
+ " d = ds_preds.isel(t_ahead=it_ahead).groupby('t_source').first()\n",
+ "\n",
+ " p = hv.Scatter({\n",
+ " 'x': d.t_source,\n",
+ " 'y': d.y_true\n",
+ " }, label='true').opts(color='black')\n",
+ "\n",
+ " # Get arrays\n",
+ " xf = d.t_source.values\n",
+ " yp = d.y_pred\n",
+ " s = d.y_pred_std\n",
+ " p *= hv.Curve({\n",
+ " 'x': xf,\n",
+ " 'y': yp\n",
+ " }, label='pred').opts(color='blue')\n",
+ " p *= hv.Area((xf, yp - 2 * s, yp + 2 * s),\n",
+ " vdims=['y', 'y2'],\n",
+ " label='2*std').opts(alpha=0.5, line_width=0)\n",
+ "\n",
+ "\n",
+ " return p.opts(title=f'Prediction at {it_ahead * pd.Timedelta(freq)} ahead. NLL={d.nll.mean().item():2.2f}')\n",
+ "\n",
+ "\n",
+ "dmap_preds = (hv.DynamicMap(plot_predictions_vs_time, kdims=['it_ahead'])\n",
+ " .redim.values(it_ahead=range(ds_preds.t_ahead.shape[0]))\n",
+ " .opts(width=800,\n",
+ " height=300, \n",
+ " ))\n",
+ "dmap_preds\n",
+ "# plot_prediction2(10).opts(width=800)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "d = ds_preds.mean('t_source')['nll'].groupby('t_ahead_hours').mean()\n",
+ "nll_vs_tahead = hv.Curve((d.t_ahead_hours, d)).redim(x='hours ahead', y='nll').opts(width=800)\n",
+ "nll_vs_tahead"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "d = ds_preds.mean('t_ahead')['nll'].groupby('t_source').mean()\n",
+ "nll_vs_time = hv.Curve(d).opts(width=800)\n",
+ "nll_vs_time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ },
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "true_vs_pred = hv.Scatter((ds_preds.y_true, ds_preds.y_pred))\n",
+ "dynspread(datashade(true_vs_pred))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Summarize experiments"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# LR finder"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2020-10-19T05:40:02.000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "\n",
+ "# Run learning rate finder\n",
+ "lr_finder = trainer.tuner.lr_find(model)\n",
+ "\n",
+ "# Results can be found in\n",
+ "lr_finder.results\n",
+ "\n",
+ "# Plot with\n",
+ "fig = lr_finder.plot(suggest=True)\n",
+ "fig.show()\n",
+ "\n",
+ "# Pick point based on plot, or get suggestion\n",
+ "new_lr = lr_finder.suggestion()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "jupytext": {
+ "encoding": "# -*- coding: utf-8 -*-",
+ "formats": "ipynb,py:light"
+ },
+ "kernelspec": {
+ "display_name": "seq2seq-time",
+ "language": "python",
+ "name": "seq2seq-time"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.8"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": true,
+ "sideBar": true,
+ "skip_h1_title": false,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {
+ "height": "calc(100% - 180px)",
+ "left": "10px",
+ "top": "150px",
+ "width": "307.2px"
+ },
+ "toc_section_display": true,
+ "toc_window_display": true
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/notebooks/02.0-mike-RNN_Timeseries_Seq2Seq.py b/notebooks/02.0-mike-RNN_Timeseries_Seq2Seq.py
new file mode 100644
index 0000000..f7cc8d4
--- /dev/null
+++ b/notebooks/02.0-mike-RNN_Timeseries_Seq2Seq.py
@@ -0,0 +1,676 @@
+# -*- coding: utf-8 -*-
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:light
+# text_representation:
+# extension: .py
+# format_name: light
+# format_version: '1.5'
+# jupytext_version: 1.6.0
+# kernelspec:
+# display_name: seq2seq-time
+# language: python
+# name: seq2seq-time
+# ---
+
+# # Sequence to Sequence Models for Timeseries Regression
+#
+#
+# In this notebook we are going to tackle a harder problem:
+# - predicting the future on a timeseries
+# - using an LSTM
+# - with rough uncertainty (uncalibrated)
+# - outputing sequence of predictions
+#
+#
+#
+#
+# https://medium.com/@boitemailjeanmid/smart-meters-in-london-part1-description-and-first-insights-jean-michel-d-db97af2de71b
+#
+
+# OPTIONAL: Load the "autoreload" extension so that code can change. But blacklist large modules
+# %load_ext autoreload
+# %autoreload 2
+# %aimport -pandas
+# %aimport -torch
+# %aimport -numpy
+# %aimport -matplotlib
+# %aimport -dask
+# %aimport -tqdm
+# %matplotlib inline
+
+# +
+# Imports
+import torch
+from torch import nn, optim
+from torch.nn import functional as F
+from torch.autograd import Variable
+import torch
+import torch.utils.data
+
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+plt.rcParams['figure.figsize'] = (12.0, 3.0)
+plt.style.use('ggplot')
+
+from pathlib import Path
+from tqdm.auto import tqdm
+
+import pytorch_lightning as pl
+# -
+
+from seq2seq_time.data.dataset import Seq2SeqDataSet, Seq2SeqDataSets
+from seq2seq_time.predict import predict
+
+import logging, sys
+# logging.basicConfig(stream=sys.stdout, level=logging.INFO)
+
+# ## Parameters
+
+# +
+device = "cuda" if torch.cuda.is_available() else "cpu"
+print(f'using {device}')
+
+columns_target=['energy(kWh/hh)']
+window_past = 48*2
+window_future = 48*2
+batch_size = 256
+num_workers = 5
+freq = '30T'
+max_rows = 2e5
+
+
+# -
+
+# ## Load data
+
+# +
+
+def get_smartmeter_df(indir=Path('../data/raw/smart-meters-in-london'), max_files=1):
+ """
+ Data loading and cleanding is always messy, so understand this code is optional.
+ """
+
+ # Load csv files
+ csv_files = sorted((indir/'halfhourly_dataset').glob('*.csv'))[:max_files]
+
+ # concatendate them
+ df = pd.concat([pd.read_csv(f, parse_dates=[1], na_values=['Null']) for f in csv_files])
+
+ # Add ACORN categories
+ df_households = pd.read_csv(indir/'informations_households.csv')
+ df_households = df_households[['LCLid', 'stdorToU', 'Acorn_grouped']]
+ df = pd.merge(df, df_households, on='LCLid')
+
+
+ df = df.sort_values(['tstp', 'LCLid'])
+
+ df = df.set_index('tstp')
+
+ # Drop nan and 0's
+ df = df[df['energy(kWh/hh)']!=0]
+ df = df.dropna()
+
+ # Add time features
+ time = df.index.to_series()
+ df["month"] = time.dt.month
+ df['day'] = time.dt.day
+ df['week'] = time.dt.week
+ df['hour'] = time.dt.hour
+ df['minute'] = time.dt.minute
+ df['dayofweek'] = time.dt.dayofweek
+
+ # Load weather data
+ df_weather = pd.read_csv(indir/'weather_hourly_darksky.csv', parse_dates=[3])
+ use_cols = ['visibility', 'windBearing', 'temperature', 'time', 'dewPoint',
+ 'pressure', 'apparentTemperature', 'windSpeed',
+ 'humidity']
+ df_weather = df_weather[use_cols].set_index('time')
+ df_weather = df_weather.resample(freq).first().ffill() # Resample to match energy data
+
+ # Join weather and energy data
+ df = pd.merge(df, df_weather, how='inner', left_index=True, right_index=True, sort=True)
+
+ # Holidays
+ df_hols = pd.read_csv(indir/'uk_bank_holidays.csv', parse_dates=[0])
+ holidays = set(df_hols['Bank holidays'].dt.round('D'))
+ def is_holiday(dt):
+ return dt in holidays
+ days = df.index.floor('D')
+ holiday_mapping = days.unique().to_series().apply(is_holiday).astype(int).to_dict()
+ df['holiday'] = days.to_series().map(holiday_mapping).values
+
+ # sort
+ df = df.reset_index().sort_values(['LCLid', 'index']).set_index('index')
+ df.index.name = 'Date'
+
+ return df
+# -
+# Our dataset is the london smartmeter data. But at half hour intervals
+
+# +
+df = get_smartmeter_df()
+
+# # Just get the first one for now
+# dfs = list(dfs)
+
+# # df = df.resample(freq).first().dropna() # Where empty we will backfill, this will respect causality, and mostly maintain the mean
+
+df = df.tail(int(max_rows)).copy() # Just use last X rows
+# df = pd.concat(dfs[:6], 0)
+# # df = dfs[0]
+df.LCLid.value_counts()
+# -
+
+
+
+# ### Plot/explore
+
+df
+
+
+
+# +
+import holoviews as hv
+from holoviews import opts
+
+from holoviews.plotting.links import RangeToolLink
+
+import datashader as ds
+
+from holoviews.operation.datashader import datashade, shade, dynspread, rasterize
+from holoviews.operation import decimate
+
+hv.extension('bokeh')
+
+
+def house_curve(Name=None):
+ if isinstance(Name, int):
+ name = df.LCLid.unique()[Name]
+ d = df[df.LCLid == Name]
+ d_curve = hv.Curve(d, 'Date', 'energy(kWh/hh)', label=Name)
+ return d_curve
+
+
+dmap = hv.DynamicMap(house_curve, kdims=['Name'])
+dmap = dmap.redim.values(Name=list(df.LCLid.unique()))
+dynspread(datashade(dmap).opts(width=800,
+ height=300,
+ tools=['xwheel_zoom', 'pan'],
+ active_tools=['xwheel_zoom', 'pan'],
+ default_tools=['reset', 'save', 'hover']
+ ))
+# -
+
+
+
+
+# ### Profiling
+
+# +
+# from pandas_profiling import ProfileReport
+# profile = ProfileReport(df, title="Pandas Profiling Report", minimal=True)
+# profile
+# -
+
+# ### Norm
+
+df.describe()
+
+# +
+import sklearn
+from sklearn.preprocessing import StandardScaler, OrdinalEncoder
+from sklearn_pandas import DataFrameMapper
+
+columns_input_numeric = list(df.drop(columns=columns_target)._get_numeric_data().columns)
+columns_categorical = list(set(df.columns)-set(columns_input_numeric)-set(columns_target))
+
+output_scalers = [([n], StandardScaler()) for n in columns_target]
+transformers=output_scalers + \
+[([n], StandardScaler()) for n in columns_input_numeric] + \
+[([n], OrdinalEncoder()) for n in columns_categorical]
+scaler = DataFrameMapper(transformers, df_out=True)
+df_norm = scaler.fit_transform(df)
+df_norm
+# -
+
+output_scaler = next(filter(lambda r:r[0][0] in columns_target, scaler.features))[-1]
+output_scaler
+
+# +
+# split data, with the test in the future
+
+d0 =df_norm.index.min()
+d1 = df_norm.index.max()
+split_time = d0+(d1-d0)*0.8
+split_time = split_time.round('1D')
+print(split_time)
+df_train = df_norm.groupby('LCLid').apply(lambda d:d.loc[:split_time]).reset_index(level=0, drop=True)
+df_test = df_norm.groupby('LCLid').apply(lambda d:d.loc[split_time:]).reset_index(level=0, drop=True)
+# df_test
+# -
+
+# Show split
+df_train['energy(kWh/hh)'].plot(label='train')
+df_test['energy(kWh/hh)'].plot(label='test')
+plt.ylabel('energy(kWh/hh)')
+plt.legend()
+# ### Dataset
+# These are the columns that we wont know in the future
+# We need to blank them out in x_future
+columns_blank=['visibility',
+ 'windBearing', 'temperature', 'dewPoint', 'pressure',
+ 'apparentTemperature', 'windSpeed', 'humidity']
+df_trains = [d.resample(freq).first().ffill().dropna() for _,d in df_train.groupby('LCLid')]
+df_tests = [d.resample(freq).first().ffill().dropna() for _,d in df_test.groupby('LCLid')]
+ds_train = Seq2SeqDataSets(df_trains,
+ window_past=window_past,
+ window_future=window_future,
+ columns_blank=columns_blank)
+ds_test = Seq2SeqDataSets(df_tests,
+ window_past=window_past,
+ window_future=window_future,
+ columns_blank=columns_blank)
+print(ds_train)
+print(ds_test)
+# we can treat it like an array
+ds_train[0]
+len(ds_train)
+ds_train[-1]
+
+# +
+# We can get rows
+x_past, y_past, x_future, y_future = ds_train.get_rows(10)
+
+# Plot one instance, this is what the model sees
+y_past['energy(kWh/hh)'].plot(label='past')
+y_future['energy(kWh/hh)'].plot(ax=plt.gca(), label='future')
+plt.legend()
+plt.ylabel('energy(kWh/hh)')
+
+# Notice we've added on two new columns tsp (time since present) and is_past
+x_past.tail()
+# -
+
+# Notice we've hidden some future columns to prevent cheating
+x_future.tail()
+
+# ## Plot helpers
+
+from seq2seq_time.models.lstm_seq2seq import LSTMSeq2Seq
+from seq2seq_time.models.lstm import LSTM
+from seq2seq_time.models.baseline import BaselineLast
+from seq2seq_time.models.transformer import Transformer
+from seq2seq_time.models.transformer_seq2seq import TransformerSeq2Seq
+# ## Plots
+# +
+def plot_prediction(ds_preds, i):
+ """Plot a prediction into the future, at a single point in time."""
+ d = ds_preds.isel(t_source=i)
+
+ # Get arrays
+ xf = d.t_target
+ yp = d.y_pred
+ s = d.y_pred_std
+ yt = d.y_true
+ now = d.t_source.squeeze()
+
+
+ plt.figure(figsize=(12, 4))
+
+ plt.scatter(xf, yt, label='true', c='k', s=6)
+ ylim = plt.ylim()
+
+ # plot prediction
+ plt.fill_between(xf, yp-2*s, yp+2*s, alpha=0.25,
+ facecolor="b",
+ interpolate=True,
+ label="2 std",)
+ plt.plot(xf, yp, label='pred', c='b')
+
+ # plot true
+ plt.scatter(
+ d.t_past,
+ d.y_past,
+ c='k',
+ s=6
+ )
+
+ # plot a red line for now
+ plt.vlines(x=now, ymin=0, ymax=1, label='now', color='r')
+ plt.ylim(*ylim)
+
+ now=pd.Timestamp(now.values)
+ plt.title(f'Prediction NLL={d.nll.mean().item():2.2g}')
+ plt.xlabel(f'{now.date()}')
+ plt.ylabel('energy(kWh/hh)')
+ plt.legend()
+ plt.xticks(rotation=45)
+ plt.show()
+
+def plot_performance(ds_preds, full=False):
+ """Multiple plots using xr_preds"""
+ plot_prediction(ds_preds, 24)
+ plot_prediction(ds_preds, 480)
+
+ ds_preds.mean('t_source').plot.scatter('t_ahead_hours', 'nll') # Mean over all predictions
+ n = len(ds_preds.t_source)
+ plt.ylabel('Negative Log Likelihood (lower is better)')
+ plt.xlabel('Hours ahead')
+ plt.title(f'NLL vs time ahead (no. samples={n})')
+ plt.show()
+
+ # Make a plot of the NLL over time. Does this solution get worse with time?
+ if full:
+ d = ds_preds.mean('t_ahead').groupby('t_source').mean().plot.scatter('t_source', 'nll')
+ plt.xticks(rotation=45)
+ plt.title('NLL over source time (lower is better)')
+ plt.show()
+
+ # A scatter plot is easy with xarray
+ if full:
+ plt.figure(figsize=(5, 5))
+ ds_preds.plot.scatter('y_true', 'y_pred', s=.01)
+ plt.show()
+
+ print(f'mean_NLL {ds_preds.nll.mean().item():2.2f}')
+# -
+
+
+
+def plot_hist(trainer):
+ try:
+ df_hist = pd.read_csv(trainer.logger.experiment.metrics_file_path)
+ df_hist['epoch'] = df_hist['epoch'].ffill()
+ df_histe = df_hist.set_index('epoch').groupby('epoch').mean()
+ if len(df_histe)>1:
+ df_histe[['loss/train', 'loss/val']].plot(title='history')
+ return df_histe
+ except Exception:
+ pass
+
+
+# ## Lightning
+
+# +
+import pytorch_lightning as pl
+
+class PL_MODEL(pl.LightningModule):
+ def __init__(self, model, lr=3e-4, patience=2):
+ super().__init__()
+ self._model = model
+ self.lr = lr
+ self.patience = patience
+
+ def forward(self, x_past, y_past, x_future, y_future=None):
+ """Eval/Predict"""
+ y_dist = self._model(x_past, y_past, x_future)
+ return y_dist
+
+ def training_step(self, batch, batch_idx, phase='train'):
+ x_past, y_past, x_future, y_future = batch
+ y_dist = self.forward(*batch)
+ loss = -y_dist.log_prob(y_future).mean()
+ self.log_dict({f'loss/{phase}':loss})
+ return loss
+
+ def validation_step(self, batch, batch_idx):
+ return self.training_step(batch, batch_idx, phase='val')
+
+ def configure_optimizers(self):
+ optim = torch.optim.Adam(self.parameters(), lr=self.lr)
+ scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
+ optim,
+ patience=self.patience,
+ verbose=True,
+ min_lr=1e-7,
+ )
+ return {'optimizer': optim, 'lr_scheduler': scheduler, 'monitor': 'loss/val'}
+
+
+# -
+
+# # Run
+from torch.utils.data import DataLoader
+from pytorch_lightning.loggers import CSVLogger
+from pl_bolts.callbacks import PrintTableMetricsCallback
+from pytorch_lightning.callbacks.early_stopping import EarlyStopping
+
+
+# +
+# Init data
+x_past, y_past, x_future, y_future = ds_train.get_rows(10)
+input_size = x_past.shape[-1]
+output_size = y_future.shape[-1]
+
+dl_train = DataLoader(ds_train,
+ batch_size=batch_size,
+ shuffle=True,
+ pin_memory=num_workers==0,
+ num_workers=num_workers)
+dl_test = DataLoader(ds_test, batch_size=batch_size, num_workers=num_workers)
+# -
+
+# Baseline model
+pt_model = BaselineLast()
+model = PL_MODEL(pt_model).to(device)
+trainer = pl.Trainer(gpus=1,
+ max_epochs=1,
+ limit_train_batches=0.01,
+ logger=CSVLogger("logs",
+ name=type(pt_model).__name__),
+ )
+trainer.fit(model, dl_train, dl_test)
+print(plot_hist(trainer))
+ds_preds = predict(model.to(device), ds_test.datasets[0], batch_size, device=device, scaler=output_scaler)
+print(f'baseline nll: {ds_preds.nll.mean().item():2.2g}')
+
+
+
+models = [
+# BaselineLast(),
+ LSTM(input_size,
+ output_size,
+ hidden_size=80,
+ lstm_layers=3,
+ lstm_dropout=0.3),
+ Transformer(input_size,
+ output_size,
+ attention_dropout=0.3,
+ nhead=8,
+ nlayers=6,
+ hidden_size=64),
+ LSTMSeq2Seq(input_size,
+ output_size,
+ hidden_size=64,
+ lstm_layers=2,
+ lstm_dropout=0.25),
+ TransformerSeq2Seq(input_size,
+ output_size,
+ hidden_size=64,
+ nhead=8,
+ nlayers=4,
+ attention_dropout=0.3),
+# Transformer(input_size,
+# output_size,
+# attention_dropout=0.2,
+# nhead=8,
+# nlayers=6,
+# hidden_size=128),
+# LSTM(input_size,
+# output_size,
+# hidden_size=128,
+# lstm_layers=3,
+# lstm_dropout=0.3),
+]
+
+
+
+
+
+for pt_model in models:
+ name = type(pt_model).__name__
+ print(name)
+
+ # Wrap in lightning
+ patience = 2
+ model = PL_MODEL(pt_model, patience=patience, lr=3e-4).to(device)
+
+ # Trainer
+
+ trainer = pl.Trainer(gpus=1,
+ min_epochs=1,
+ max_epochs=10,
+ amp_level='O1',
+ precision=16,
+ gradient_clip_val=0.5,
+ logger=CSVLogger("logs",
+ name=type(pt_model).__name__),
+ callbacks=[
+ EarlyStopping(monitor='loss/val', patience=patience*2),
+ PrintTableMetricsCallback()
+ ],
+ )
+
+ # Train
+ trainer.fit(model, dl_train, dl_test)
+
+ # Performance
+ print(plot_hist(trainer))
+
+ ds_preds = predict(model.to(device),
+ ds_test.datasets[0],
+ batch_size,
+ device=device,
+ scaler=output_scaler)
+ plot_performance(ds_preds)
+
+
+
+# # holoviews pred
+
+import holoviews as hv
+from holoviews import opts
+
+
+# +
+def plot_prediction_now(t_source):
+ """Plot predictions with holoviews"""
+
+ # Let us pass in an int
+ if isinstance(t_source, int):
+ t_source = ds_preds.t_source[t_source].to_pandas()
+
+ d = ds_preds.sel(t_source=t_source)
+
+ # Sometimes there are duplicate time, take the first
+ if len(d.t_source.shape) and d.t_source.shape[0] > 0:
+ d = d.isel(t_source=0)
+ if len(d.t_source.shape) and d.t_source.shape[0] == 0:
+ return None
+
+ now = d.t_source.to_pandas()
+
+ # Plot true
+ x = np.concatenate([d.t_past, d.t_target])
+ yt = np.concatenate([d.y_past, d.y_true])
+ p = hv.Scatter({
+ 'x': x,
+ 'y': yt
+ }, label='true').opts(color='black', framewise=True)
+
+ # Get arrays
+ xf = d.t_target.values
+ yp = d.y_pred
+ s = d.y_pred_std
+ p *= hv.Curve({
+ 'x': xf,
+ 'y': yp
+ }, label='pred').opts(color='blue')
+ p *= hv.Area((xf, yp - 2 * s, yp + 2 * s),
+ vdims=['y', 'y2'],
+ label='2*std').opts(alpha=0.5, line_width=0)
+
+ # plot now line
+ p *= hv.VLine(now, label='now').opts(color='red')
+ return p.opts(title=f'Prediction at {now}. NLL={d.nll.mean().item():2.2f}')
+
+
+dmap_pred = (hv.DynamicMap(plot_prediction_now, kdims=['t_source'])
+ .redim.values(t_source=ds_preds.t_source.to_pandas())
+ .opts(width=800,
+ height=300,
+ ))
+dmap_pred
+
+
+# +
+def plot_predictions_vs_time(it_ahead):
+ """Plot predictions vs time with holoviews"""
+
+ d = ds_preds.isel(t_ahead=it_ahead).groupby('t_source').first()
+
+ p = hv.Scatter({
+ 'x': d.t_source,
+ 'y': d.y_true
+ }, label='true').opts(color='black')
+
+ # Get arrays
+ xf = d.t_source.values
+ yp = d.y_pred
+ s = d.y_pred_std
+ p *= hv.Curve({
+ 'x': xf,
+ 'y': yp
+ }, label='pred').opts(color='blue')
+ p *= hv.Area((xf, yp - 2 * s, yp + 2 * s),
+ vdims=['y', 'y2'],
+ label='2*std').opts(alpha=0.5, line_width=0)
+
+
+ return p.opts(title=f'Prediction at {it_ahead * pd.Timedelta(freq)} ahead. NLL={d.nll.mean().item():2.2f}')
+
+
+dmap_preds = (hv.DynamicMap(plot_predictions_vs_time, kdims=['it_ahead'])
+ .redim.values(it_ahead=range(ds_preds.t_ahead.shape[0]))
+ .opts(width=800,
+ height=300,
+ ))
+dmap_preds
+# plot_prediction2(10).opts(width=800)
+# -
+
+d = ds_preds.mean('t_source')['nll'].groupby('t_ahead_hours').mean()
+nll_vs_tahead = hv.Curve((d.t_ahead_hours, d)).redim(x='hours ahead', y='nll').opts(width=800)
+nll_vs_tahead
+
+d = ds_preds.mean('t_ahead')['nll'].groupby('t_source').mean()
+nll_vs_time = hv.Curve(d).opts(width=800)
+nll_vs_time
+
+true_vs_pred = hv.Scatter((ds_preds.y_true, ds_preds.y_pred))
+dynspread(datashade(true_vs_pred))
+
+# # Summarize experiments
+
+# # LR finder
+
+# +
+
+# Run learning rate finder
+lr_finder = trainer.tuner.lr_find(model)
+
+# Results can be found in
+lr_finder.results
+
+# Plot with
+fig = lr_finder.plot(suggest=True)
+fig.show()
+
+# Pick point based on plot, or get suggestion
+new_lr = lr_finder.suggestion()
+# -
+
+
diff --git a/requirements/environment.max.yaml b/requirements/environment.max.yaml
index a485d98..4781f05 100644
--- a/requirements/environment.max.yaml
+++ b/requirements/environment.max.yaml
@@ -21,6 +21,7 @@ dependencies:
- blas=1.0=mkl
- bleach=3.2.1=pyh9f0ad1d_0
- blinker=1.4=py_1
+ - bokeh=2.2.2=py37hc8dfbb8_0
- botocore=1.18.18=pyh9f0ad1d_0
- brotlipy=0.7.0=py37hb5d75c8_1001
- c-ares=1.16.1=h516909a_3
@@ -30,14 +31,23 @@ dependencies:
- cffi=1.14.3=py37h00ebd2e_1
- chardet=3.0.4=py37he5f6b98_1008
- click=7.1.2=pyh9f0ad1d_0
+ - cloudpickle=1.6.0=py_0
- colorama=0.4.3=py_0
+ - colorcet=2.0.2=py_0
+ - confuse=1.3.0=pyh9f0ad1d_0
- cryptography=3.1.1=py37hff6837a_1
- cudatoolkit=10.2.89=hfd86e86_1
- cycler=0.10.0=py_2
+ - cytoolz=0.11.0=py37h8f50634_1
+ - dask=2.30.0=py_0
+ - dask-core=2.30.0=py_0
- dataclasses=0.7=py37_0
+ - datashader=0.11.1=pyh9f0ad1d_0
+ - datashape=0.5.4=py_1
- dbus=1.13.18=hb2f20db_0
- decorator=4.4.2=py_0
- defusedxml=0.6.0=py_0
+ - distributed=2.30.0=py37hc8dfbb8_1
- docutils=0.15.2=py37_0
- entrypoints=0.3=py37hc8dfbb8_1002
- expat=2.2.10=he6710b0_2
@@ -52,8 +62,12 @@ dependencies:
- grpcio=1.31.0=py37hb0870dc_0
- gst-plugins-base=1.14.5=h0935bb2_2
- gstreamer=1.14.5=h36ae1b5_2
+ - heapdict=1.0.1=py_0
+ - holoviews=1.13.4=pyh9f0ad1d_0
+ - htmlmin=0.1.12=py_1
- icu=67.1=he1b5a44_0
- idna=2.10=pyh9f0ad1d_0
+ - imagehash=4.1.0=pyh9f0ad1d_0
- importlib-metadata=2.0.0=py37hc8dfbb8_0
- importlib_metadata=2.0.0=1
- iniconfig=1.1.1=py_0
@@ -101,16 +115,21 @@ dependencies:
- libxcb=1.14=h7b6447c_0
- libxkbcommon=0.10.0=he1b5a44_0
- libxml2=2.9.10=h68273f3_2
+ - llvmlite=0.34.0=py37h5202443_2
+ - locket=0.2.0=py_2
- lz4-c=1.9.2=he1b5a44_3
- markdown=3.3.1=pyh9f0ad1d_0
- markupsafe=1.1.1=py37hb5d75c8_2
- matplotlib=3.3.2=py37hc8dfbb8_1
- matplotlib-base=3.3.2=py37hc9afd2a_1
- mccabe=0.6.1=py_1
+ - missingno=0.4.2=py_1
- mistune=0.8.4=py37h8f50634_1002
- mkl=2020.2=256
- more-itertools=8.5.0=py_0
+ - msgpack-python=1.0.0=py37h99015e2_2
- multidict=4.7.6=py37h7b6447c_1
+ - multipledispatch=0.6.0=py_0
- mypy=0.790=py_0
- mypy_extensions=0.4.3=py37hc8dfbb8_1
- mysql-common=8.0.21=2
@@ -120,22 +139,30 @@ dependencies:
- nbformat=5.0.8=py_0
- ncurses=6.2=he1b5a44_2
- nest-asyncio=1.4.1=py_0
+ - networkx=2.5=py_0
- ninja=1.10.1=hfc4b9b4_2
- notebook=6.1.4=py37hc8dfbb8_1
- nspr=4.29=he1b5a44_1
- nss=3.58=h27285de_1
+ - numba=0.51.2=py37h9fdb41a_0
- numpy=1.19.2=py37h7ea13bd_1
- oauthlib=3.1.0=py_0
- olefile=0.46=pyh9f0ad1d_1
- openssl=1.1.1h=h516909a_0
- packaging=20.4=pyh9f0ad1d_0
- pandas=1.1.3=py37h9fdb41a_2
+ - pandas-profiling=2.9.0=pyh9f0ad1d_0
- pandoc=2.11.0.2=hd18ef5c_0
- pandocfilters=1.4.2=py_1
+ - panel=0.9.7=py_0
+ - param=1.9.3=py_0
- parso=0.7.1=pyh9f0ad1d_0
+ - partd=1.1.0=py_0
- pathspec=0.8.0=pyh9f0ad1d_0
+ - patsy=0.5.1=py_0
- pcre=8.44=he1b5a44_0
- pexpect=4.8.0=py37hc8dfbb8_1
+ - phik=0.10.0=py_0
- pickleshare=0.7.5=py37hc8dfbb8_1002
- pillow=8.0.0=py37h718be6c_0
- pip=20.2.4=py_0
@@ -150,6 +177,7 @@ dependencies:
- pyasn1-modules=0.2.8=py_0
- pycodestyle=2.6.0=pyh9f0ad1d_0
- pycparser=2.20=pyh9f0ad1d_2
+ - pyct=0.4.8=py37_0
- pydocstyle=5.1.1=py_0
- pyflakes=2.2.0=pyh9f0ad1d_0
- pygments=2.7.1=py_0
@@ -167,6 +195,8 @@ dependencies:
- pytorch=1.6.0=py3.7_cuda10.2.89_cudnn7.6.5_0
- pytorch-lightning=1.0.2=py_0
- pytz=2020.1=pyh9f0ad1d_0
+ - pyviz_comms=0.7.6=pyh9f0ad1d_0
+ - pywavelets=1.1.1=py37h161383b_3
- pyyaml=5.3.1=py37hb5d75c8_1
- pyzmq=19.0.2=py37hac76be4_2
- qt=5.12.9=h1f2b2cb_0
@@ -178,11 +208,17 @@ dependencies:
- s3transfer=0.3.3=py37hc8dfbb8_2
- scikit-learn=0.23.2=py37h6785257_0
- scipy=1.5.2=py37hb14ef9d_2
+ - seaborn=0.11.0=0
+ - seaborn-base=0.11.0=py_0
- send2trash=1.5.0=py_0
- setuptools=49.6.0=py37he5f6b98_2
- six=1.15.0=pyh9f0ad1d_0
- snowballstemmer=2.0.0=py_0
+ - sortedcontainers=2.2.2=pyh9f0ad1d_0
- sqlite=3.33.0=h4cf870e_1
+ - statsmodels=0.12.0=py37h161383b_1
+ - tangled-up-in-unicode=0.0.6=pyh9f0ad1d_0
+ - tblib=1.7.0=py_0
- tensorboard=2.3.0=py_0
- tensorboard-plugin-wit=1.6.0=pyh9f0ad1d_0
- terminado=0.9.1=py37hc8dfbb8_1
@@ -190,6 +226,7 @@ dependencies:
- threadpoolctl=2.1.0=pyh5ca1d4c_0
- tk=8.6.10=hed695b0_1
- toml=0.10.1=pyh9f0ad1d_0
+ - toolz=0.11.1=py_0
- torchvision=0.7.0=py37_cu102
- tornado=6.0.4=py37h8f50634_2
- tqdm=4.50.2=pyh9f0ad1d_0
@@ -198,6 +235,7 @@ dependencies:
- typing-extensions=3.7.4.3=0
- typing_extensions=3.7.4.3=py_0
- urllib3=1.25.10=py_0
+ - visions=0.5.0=pyh9f0ad1d_0
- wcwidth=0.2.5=pyh9f0ad1d_2
- webencodings=0.5.1=py_1
- werkzeug=1.0.1=pyh9f0ad1d_0
@@ -209,6 +247,7 @@ dependencies:
- yapf=0.30.0=pyh9f0ad1d_0
- yarl=1.6.2=py37h8f50634_0
- zeromq=4.3.3=he1b5a44_2
+ - zict=2.0.0=py_0
- zipp=3.3.1=py_0
- zlib=1.2.11=h516909a_1010
- zstd=1.4.5=h6597ccf_2
@@ -216,6 +255,7 @@ dependencies:
- pyqt5-sip==4.19.18
- pyqtchart==5.12
- pyqtwebengine==5.12.1
+ - pytorch-lightning-bolts==0.2.5
- sklearn-pandas==2.0.2
- torchsummaryx==1.3.0
prefix: /home/wassname/anaconda/envs/seq2seq-time
diff --git a/requirements/environment.min.yaml b/requirements/environment.min.yaml
index fdbc036..11096e8 100644
--- a/requirements/environment.min.yaml
+++ b/requirements/environment.min.yaml
@@ -23,4 +23,7 @@ dependencies:
- pytorch-lightning
- yapf
- ipywidgets
+ - holoviews
+ - pandas-profiling
+ - datashader
prefix: /home/wassname/anaconda/envs/seq2seq-time
diff --git a/seq2seq_time/data/dataset.py b/seq2seq_time/data/dataset.py
index b793bdf..fcea476 100644
--- a/seq2seq_time/data/dataset.py
+++ b/seq2seq_time/data/dataset.py
@@ -62,8 +62,8 @@ class Seq2SeqDataSet(torch.utils.data.Dataset):
x_future = x[self.window_past:]
y_future = y[self.window_past:]
- # Stop it cheating by using future weather measurements
- x_future[:, self._icol_blank] = 0
+ # Stop it cheating by using future weather measurements. Fill in with last value
+ x_future[:, self._icol_blank] = x_past[0, self._icol_blank]
return x_past, y_past, x_future, y_future
diff --git a/seq2seq_time/models/baseline.py b/seq2seq_time/models/baseline.py
new file mode 100644
index 0000000..08e76d8
--- /dev/null
+++ b/seq2seq_time/models/baseline.py
@@ -0,0 +1,15 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+class BaselineLast(nn.Module):
+ def __init__(self):
+ super().__init__()
+ self.std = nn.Parameter(torch.tensor(1.))
+
+ def forward(self, past_x, past_y, future_x, future_y=None):
+ device = next(self.parameters()).device
+ B, S, F = future_x.shape
+ mean = past_y[:, -1:].repeat(1, S, 1)
+ std = (self.std * 1.0).repeat(1, S, 1)
+ return torch.distributions.Normal(mean, std)
diff --git a/seq2seq_time/models/lstm.py b/seq2seq_time/models/lstm.py
new file mode 100644
index 0000000..f2e16a5
--- /dev/null
+++ b/seq2seq_time/models/lstm.py
@@ -0,0 +1,39 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+class LSTM(nn.Module):
+ def __init__(self, input_size, output_size, hidden_size=32, lstm_layers=2, lstm_dropout=0, _min_std = 0.05, nan_value=0):
+ super().__init__()
+ self._min_std = _min_std
+ self.nan_value = nan_value
+
+ self.lstm = nn.LSTM(
+ input_size=input_size + output_size,
+ hidden_size=hidden_size,
+ batch_first=True,
+ num_layers=lstm_layers,
+ dropout=lstm_dropout,
+ )
+ self.mean = nn.Linear(hidden_size, output_size)
+ self.std = nn.Linear(hidden_size, output_size)
+
+ def forward(self, past_x, past_y, future_x, future_y=None):
+ device = next(self.parameters()).device
+ future_y_fake = (
+ torch.ones(past_y.shape[0], future_x.shape[1], past_y.shape[2]).float().to(device) * self.nan_value
+ )
+ context = torch.cat([past_x, past_y], -1).detach()
+ target = torch.cat([future_x, future_y_fake], -1).detach()
+ x = torch.cat([context, target * 1], 1).detach()
+
+ steps = past_y.shape[1]
+ outputs, _ = self.lstm(x)
+ outputs = outputs[:, steps:, :]
+
+ # outputs: [B, T, num_direction * H]
+ mean = self.mean(outputs)
+ log_sigma = self.std(outputs)
+ sigma = self._min_std + (1 - self._min_std) * F.softplus(log_sigma)
+ y_dist = torch.distributions.Normal(mean, sigma)
+ return y_dist
diff --git a/seq2seq_time/models/lstm_seq2seq.py b/seq2seq_time/models/lstm_seq2seq.py
new file mode 100644
index 0000000..893ff08
--- /dev/null
+++ b/seq2seq_time/models/lstm_seq2seq.py
@@ -0,0 +1,39 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+class LSTMSeq2Seq(nn.Module):
+ def __init__(self, input_size, output_size, hidden_size=32, lstm_layers=2, lstm_dropout=0, _min_std = 0.05):
+ super().__init__()
+ self._min_std = _min_std
+
+ self.encoder = nn.LSTM(
+ input_size=input_size + output_size,
+ hidden_size=hidden_size,
+ batch_first=True,
+ num_layers=lstm_layers,
+ dropout=lstm_dropout,
+ )
+ self.decoder = nn.LSTM(
+ input_size=input_size,
+ hidden_size=hidden_size,
+ batch_first=True,
+ num_layers=lstm_layers,
+ dropout=lstm_dropout,
+ )
+ self.mean = nn.Linear(hidden_size, output_size)
+ self.std = nn.Linear(hidden_size, output_size)
+
+ def forward(self, past_x, past_y, future_x, future_y=None):
+ x = torch.cat([past_x, past_y], -1)
+ _, (h_out, cell) = self.encoder(x)
+
+ # output = [batch size, seq len, hid dim * n directions]
+ outputs, (_, _) = self.decoder(future_x, (h_out, cell))
+
+ # outputs: [B, T, num_direction * H]
+ mean = self.mean(outputs)
+ log_sigma = self.std(outputs)
+ sigma = self._min_std + (1 - self._min_std) * F.softplus(log_sigma)
+ y_dist = torch.distributions.Normal(mean, sigma)
+ return y_dist
diff --git a/seq2seq_time/models/transformer.py b/seq2seq_time/models/transformer.py
new file mode 100644
index 0000000..51c8609
--- /dev/null
+++ b/seq2seq_time/models/transformer.py
@@ -0,0 +1,59 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+
+class Transformer(nn.Module):
+ """
+ A single transformer, masking nan or 0
+ """
+ def __init__(self, x_dim, y_dim, attention_dropout=0, nhead=8, nlayers=2, hidden_size=16, nan_value=0, min_std=0.01):
+ super().__init__()
+ self._min_std = min_std
+ self.nan_value = nan_value
+ enc_x_dim = x_dim + y_dim
+
+ self.enc_emb = nn.Linear(enc_x_dim, hidden_size)
+ encoder_norm = nn.LayerNorm(hidden_size)
+ layer_enc = nn.TransformerEncoderLayer(
+ d_model=hidden_size,
+ dim_feedforward=hidden_size*4,
+ dropout=attention_dropout,
+ nhead=nhead,
+ # activation
+ )
+ self.encoder = nn.TransformerEncoder(
+ layer_enc, num_layers=nlayers, norm=encoder_norm
+ )
+ self.mean = nn.Linear(hidden_size, y_dim)
+ self.std = nn.Linear(hidden_size, y_dim)
+
+ def forward(self, past_x, past_y, future_x, future_y=None):
+ device = next(self.parameters()).device
+ future_y_fake = (
+ torch.ones(past_y.shape[0], future_x.shape[1], past_y.shape[2]).float().to(device) * self.nan_value
+ )
+ context = torch.cat([past_x, past_y], -1).detach()
+ target = torch.cat([future_x, future_y_fake], -1).detach()
+ x = torch.cat([context, target * 1], 1).detach()
+
+ # Masks
+ x_mask = torch.isfinite(x) & (x != self.nan_value)
+ x[~x_mask] = 0
+ x = x.detach()
+ x_key_padding_mask = ~x_mask.any(-1)
+
+ x = self.enc_emb(x).permute(1, 0, 2)
+
+ outputs = self.encoder(x, src_key_padding_mask=x_key_padding_mask).permute(
+ 1, 0, 2
+ )
+
+ # Seems to help a little, especially with extrapolating out of bounds
+ steps = past_y.shape[1]
+ mean = self.mean(outputs)[:, steps:, :]
+ log_sigma = self.std(outputs)[:, steps:, :]
+
+ sigma = self._min_std + (1 - self._min_std) * F.softplus(log_sigma)
+ return torch.distributions.Normal(mean, sigma)
+
diff --git a/seq2seq_time/models/transformer_seq2seq.py b/seq2seq_time/models/transformer_seq2seq.py
new file mode 100644
index 0000000..4a05ac1
--- /dev/null
+++ b/seq2seq_time/models/transformer_seq2seq.py
@@ -0,0 +1,80 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+
+
+class TransformerSeq2Seq(nn.Module):
+ def __init__(self, x_size, y_size, hidden_size=16, nhead=8, nlayers=2, attention_dropout=0, min_std=0.01, nan_value=0):
+ super().__init__()
+ self._min_std = min_std
+ self.nan_value = nan_value
+
+ self.enc_emb = nn.Linear(x_size + y_size, hidden_size)
+ self.dec_emb = nn.Linear(x_size, hidden_size)
+
+ encoder_norm = nn.LayerNorm(hidden_size)
+ layer_enc = nn.TransformerEncoderLayer(
+ d_model=hidden_size,
+ dim_feedforward=hidden_size*4,
+ dropout=attention_dropout,
+ nhead=nhead,
+ # activation
+ )
+ self.encoder = nn.TransformerEncoder(
+ layer_enc, num_layers=nlayers, norm=encoder_norm
+ )
+
+ layer_dec = nn.TransformerDecoderLayer(
+ d_model=hidden_size,
+ dim_feedforward=hidden_size*4,
+ dropout=attention_dropout,
+ nhead=nhead,
+ )
+ decoder_norm = nn.LayerNorm(hidden_size)
+ self.decoder = nn.TransformerDecoder(
+ layer_dec, num_layers=nlayers, norm=decoder_norm
+ )
+ self.mean = nn.Linear(hidden_size, y_size)
+ self.std = nn.Linear(hidden_size, y_size)
+
+
+ def forward(self, past_x, past_y, future_x, future_y=None):
+ device = next(self.parameters()).device
+ x = torch.cat([past_x, past_y], -1)
+
+ # Masks
+ future_mask = torch.isfinite(future_x) & (future_x!=self.nan_value)
+ tgt_key_padding_mask = ~future_mask.any(-1)
+
+ past_mask = torch.isfinite(x) & (x!=self.nan_value)
+ src_key_padding_mask = ~past_mask.any(-1)# * float('-inf')
+
+ # Embed
+ x = self.enc_emb(x)
+ # Size([B, C, X]) -> Size([B, C, hidden_dim])
+ future_x = self.dec_emb(future_x)
+ # Size([B, C, T]) -> Size([B, C, hidden_dim])
+
+ x = x.permute(1, 0, 2) # (B,C,hidden_dim) -> (C,B,hidden_dim)
+ future_x = future_x.permute(1, 0, 2)
+ # requires (C, B, hidden_dim)
+ memory = self.encoder(x, src_key_padding_mask=src_key_padding_mask)
+
+ # In transformers the memory and future_x need to be the same length. Lets use a permutation invariant agg on the context
+ # Then expand it, so it's available as we decode, conditional on future_x
+ # (C, B, emb_dim) -> (B, emb_dim) -> (T, B, emb_dim)
+ # In transformers the memory and future_x need to be the same length. Lets use a permutation invariant agg on the context
+ # Then expand it, so it's available as we decode, conditional on future_x
+ memory = memory.max(dim=0, keepdim=True)[0].expand_as(future_x)
+
+ outputs = self.decoder(future_x, memory, tgt_key_padding_mask=tgt_key_padding_mask)
+
+ # [T, B, emb_dim] -> [B, T, emb_dim]
+ outputs = outputs.permute(1, 0, 2).contiguous()
+ # Size([B, T, emb_dim])
+ mean = self.mean(outputs)
+ log_sigma = self.std(outputs)
+ sigma = self._min_std + (1 - self._min_std) * F.softplus(log_sigma)
+ return torch.distributions.Normal(mean, sigma)
+