Skip to content

Instantly share code, notes, and snippets.

@ionescuv
Last active September 6, 2019 04:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ionescuv/63fdace1dda266ae89c7 to your computer and use it in GitHub Desktop.
Save ionescuv/63fdace1dda266ae89c7 to your computer and use it in GitHub Desktop.
Adaptive Piecewise Linear Approximation of Time Series [HANA, R, UI5]
HANA&R adaptive PLA
index value
1 1.375860
2 1.375690
3 1.375360
4 1.375540
5 1.375500
6 1.375980
7 1.375680
8 1.375990
9 1.375990
10 1.375860
11 1.376000
12 1.375310
13 1.374130
14 1.375640
15 1.375610
16 1.375140
17 1.375030
18 1.375390
19 1.375610
20 1.375620
21 1.375620
22 1.376100
23 1.376040
24 1.375750
25 1.375860
26 1.375020
27 1.374950
28 1.375010
29 1.375090
30 1.375440
31 1.375310
32 1.375260
33 1.375550
34 1.375370
35 1.375320
36 1.375330
37 1.375330
38 1.375400
39 1.375280
40 1.375280
41 1.375350
42 1.375400
43 1.375350
44 1.375490
45 1.375380
46 1.375560
47 1.375700
48 1.375530
49 1.375630
50 1.375560
51 1.375430
52 1.376140
53 1.375790
54 1.376460
55 1.375990
56 1.376540
57 1.376460
58 1.376170
59 1.376090
60 1.376240
61 1.376460
62 1.376700
63 1.376830
64 1.376850
65 1.376620
66 1.376680
67 1.376700
68 1.376680
69 1.376730
70 1.377030
71 1.376880
72 1.377020
73 1.377210
74 1.377050
75 1.377010
76 1.376800
77 1.377120
78 1.376800
79 1.376500
80 1.376450
81 1.376440
82 1.376450
83 1.376440
84 1.376440
85 1.376560
86 1.376520
87 1.376490
88 1.376430
89 1.376610
90 1.376990
91 1.376680
92 1.377070
93 1.376790
94 1.376800
95 1.376710
96 1.376480
97 1.376580
98 1.376600
99 1.376600
100 1.376500
101 1.376480
102 1.376470
103 1.376460
104 1.376360
105 1.376380
106 1.376340
107 1.376330
108 1.376320
109 1.376330
110 1.376320
111 1.376420
112 1.376550
113 1.376480
114 1.376520
115 1.376350
116 1.376520
117 1.376830
118 1.376830
119 1.376830
120 1.376780
121 1.376520
122 1.376520
123 1.376500
124 1.376550
125 1.376560
126 1.376560
127 1.376590
128 1.376610
129 1.376350
130 1.376380
131 1.376350
132 1.376240
133 1.376210
134 1.376270
135 1.376520
136 1.376260
137 1.376280
138 1.376300
139 1.376300
140 1.376170
141 1.376120
142 1.376090
143 1.376000
144 1.375960
145 1.375980
146 1.375840
147 1.375900
148 1.375900
149 1.375920
150 1.375980
151 1.375940
152 1.375980
153 1.375950
154 1.375990
155 1.376030
156 1.376030
157 1.376010
158 1.376080
159 1.376170
160 1.376180
161 1.376210
162 1.376120
163 1.376050
164 1.375990
165 1.376170
166 1.376180
167 1.376100
168 1.376420
169 1.376130
170 1.376140
171 1.375860
172 1.375820
173 1.375810
174 1.375890
175 1.376010
176 1.375980
177 1.375990
178 1.375980
179 1.376030
180 1.376040
181 1.376000
182 1.375860
183 1.375760
184 1.375770
185 1.375790
186 1.375730
187 1.375740
188 1.375750
189 1.375740
190 1.375650
191 1.375660
192 1.375600
193 1.375600
194 1.375600
195 1.375610
196 1.375620
197 1.375520
198 1.375540
199 1.375550
200 1.375290
toSlopes <- function(ts){
slopes <- diff(ts);
indexes <- 0:(length(slopes)-1);
aux <- slopes*indexes;
offset <- ts[1:length(slopes)] - aux;
lngth <- rep(1,length(slopes));
res <- data.frame(matrix(c(slopes, offset, indexes, lngth), ncol = 4));
colnames(res) <- c("a", "b", "startp", "runlength");
return(res);
};
fValue <- function(segs, x){
return(segs[1,"a"]*x + segs[1,"b"]);
};
merge <- function(segs){
slope <- weighted.mean(segs[,"a"], segs[,"runlength"]);
start <- segs[1, "startp"];
length <- sum(segs[, "runlength"]);
if ( segs[1,"a"] == segs[2,"a"] ){
offset <- segs[1,"b"];
} else {
offset <- fValue(segs[1,], segs[1, "startp"]) - (slope * segs[1, "startp"]) ;
}
res <- data.frame( slope, offset, start, length);
colnames(res) <- c("a", "b", "startp", "runlength");
return(res);
};
segdif <- function(segs, x1, x2){
a1 <- segs[1,"a"];
a2 <- segs[2,"a"];
b1 <- segs[1,"b"];
b2 <- segs[2,"b"];
out <- (a1 - a2)*(x2*x2 - x1*x1)/2 + (b1 - b2)*(x2 - x1);
return(out);
};
mergeCost <- function(segs){
merged <- merge(segs);
start1 <- segs[1, "startp"];
end1 <- start1 + segs[1,"runlength"];
cost <- segdif(rbind(merged, segs[1,]), start1, end1);
start2 <- segs[2, "startp"];
end2 <- start2 + segs[2,"runlength"];
cost <- cost + segdif(rbind(merged, segs[2,]), start2, end2);
return(abs(cost));
};
costVector <- function(segs){
start <- 0;
cost <- NULL;
## determine merge cost for each pair of segments
for (i in 1:(length(segs[,1])-1))
{
cost[i] <- mergeCost(segs[i:(i+1),]);
}
return(cost);
};
bottomUp <- function(segs, minIndex){
## now adjust the data
out <- segs;
out[minIndex,] <- merge(out[minIndex:(minIndex+1),]);
out <- out[-c(minIndex+1),];
return(out);
};
bottomUpCost <- function(segs, cost, minIndex){
## now adjust the data
out <- cost;
if (minIndex < length(segs[,"a"]))
{
out <- out[-c(minIndex+1)]; # eliminate cost for entry that is being deleted
out[minIndex] <- mergeCost(segs[minIndex:(minIndex+1),]); #recalculate cost after merge
}
else ## special case when we are eliminating the last entry
{
out <- out[-c(minIndex)]; # eliminate last cost entry
}
return(out);
};
segment <- function(ts, segments){
slopes <- toSlopes(ts);
cost <- costVector(slopes);
while( length(slopes[,"a"]) > segments ){
## find minimum merge cost
minIndex <- which.min(cost);
## now adjust the data
slopes <- bottomUp(slopes, minIndex);
cost <- bottomUpCost(slopes, cost, minIndex);
}
return(slopes);
}
#### helper functions for display ####
segPlot <- function(segs){
x <- 0;
vec <- c(x,fValue(segs[1,],x));
for (i in 1:length(segs[,"a"])){
x <- x + segs[i,"runlength"];
vec <- rbind(vec, c(x,fValue(segs[i,],x)));
}
return(vec);
};
table.schemaName = "IONESCUV";
table.tableType = COLUMNSTORE; // ROWSTORE is an alternative value
table.columns =
[
{name = "index"; sqlType = INTEGER; },
{name = "val"; sqlType = DECIMAL; precision = 6; scale = 5; }
];
table.primaryKey.pkcolumns = ["index"];
sap.ui.controller("r_pal_sample.view.Master", {
onInit : function() {
//Raw Data plot using Viz Line Chart
var oDatasetRaw = new sap.viz.ui5.data.FlattenedDataset({
dimensions : [ {
axis : 1,
name : 'index',
value : '{index}'
}
],
measures : [
{
name : 'val',
value : '{val}'
} ],
data : {
path : "/rawData"
}
});
var oVizFrameLine = this.getView().byId("idVizFrameLine");
oVizFrameLine.setDataset(oDatasetRaw);
//Segementation plot using VizFrame with 'timeseries_line' visualization
var oVizFrame = this.getView().byId("idVizFrameCombined");
oVizFrame.setVizType('timeseries_line');
oDatasetVizFrame = new sap.viz.ui5.data.FlattenedDataset({
dimensions : [ {
name : 'index',
value : '{INDEX}',
dataType:'date'
} ],
measures : [ {
name : 'val',
value : '{VAL}'
}
} ],
data : {
path : "/segmentation(granularity=5)/Results"
}
});
oVizFrame.setVizProperties({
general: {
layout: {
padding: 0.04
}
},
valueAxis: {
visible: false,
title: {
visible: false
},
label: {
formatString: 'u'
}
},
timeAxis: {
visible: false,
title: {
visible: false
},
levelConfig: {
"year": {
row: 2
}
}
},
plotArea: {
dataLabel: {
visible: false
}
},
legend: {
visible: false,
title: {
visible: false
}
},
title: {
visible: false,
}
});
oVizFrame.setDataset(oDatasetVizFrame);
feedValueAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
'uid': "valueAxis",
'type': "Measure",
'values': ["val"]
});
oVizFrame.addFeed(feedValueAxis);
feedTimeAxis = new sap.viz.ui5.controls.common.feeds.FeedItem({
'uid': "timeAxis",
'type': "Dimension",
'values': ["index"]
});
oVizFrame.addFeed(feedTimeAxis);
},
onSegChange : function(event) {
var newGranularity = Math.round(event.getParameters().value);
oDatasetVizFrame.bindData({
path : "/segmentation(granularity=" + newGranularity + ")/Results"
});
}
});
<core:View controllerName="r_pal_sample.view.Master" xmlns:com="sap.ui.commons" xmlns:core="sap.ui.core"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:l="sap.ui.layout" xmlns:viz="sap.viz.ui5" xmlns="sap.m">
<Page id="TextPage" title="Segmentation">
<l:HorizontalLayout>
<viz:Line id="idVizFrameLine" width="600px" height="500px">
<viz:legend>
<viz:types.legend.Common visible="false"/>
</viz:legend>
<viz:title>
<viz:types.Title text="Original Data" visible="true"/>
</viz:title>
</viz:Line>
<com:Slider change="onSegChange" height="400px" max="99" min="1" stepLabels="true" vertical="true" width="50px"/>
<viz:controls.VizFrame id="idVizFrameCombined" uiConfig="{applicationSet:'fiori'}" width="600px" height="500px"/>
</l:HorizontalLayout>
</Page>
</core:View>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment