44import pandas as pd
55import tsfresh
66
7+ STORE_MAX_DATAPOINT_FACTOR = 10
8+
79class PredictorError (Exception ):
810 pass
911
1012class Predictor ():
11- def __init__ (self , predictor , sensors , window_size , labels ):
13+ def __init__ (self , predictor , sensors , window_size , labels , scaler = None , windowing_mode = "sample" ):
1214 self .predictor = predictor
1315 self .sensors = sensors
1416 self .window_size = window_size
15- self .labels = labels # TODO: we don't store this in the ml backend currently afaik
17+ self .labels = labels
18+ self .scaler = scaler
19+ self .windowing_mode = windowing_mode
20+ self .last_add_time = None
1621
1722 self .store = { key : {
18- 'data' : deque (maxlen = self .window_size * 2 ),
19- 'time' : deque (maxlen = self .window_size * 2 )
23+ 'data' : deque (maxlen = self .window_size * STORE_MAX_DATAPOINT_FACTOR ),
24+ 'time' : deque (maxlen = self .window_size * STORE_MAX_DATAPOINT_FACTOR )
2025 } for key in self .sensors }
2126
2227 def add_datapoint (self , sensor_name : str , value : float , time : int = None ):
@@ -29,16 +34,25 @@ def add_datapoint(self, sensor_name: str, value: float, time: int = None):
2934 if time is None :
3035 time = timelib .time ()
3136
37+ self .last_add_time = time
3238 self .store [sensor_name ]["data" ].append (value )
3339 self .store [sensor_name ]["time" ].append (round (time * 1000 ))
3440
3541 def predict (self ):
3642 samples = Predictor ._merge (self .store )
3743 interpolated = Predictor ._interpolate (samples )
38- window = interpolated .tail (self .window_size )
39-
40- if (len (window .index ) < self .window_size ):
41- raise PredictorError ("Not enough samples" )
44+ window = None
45+ if self .windowing_mode == 'sample' :
46+ window = interpolated .tail (self .window_size )
47+ if len (window .index ) < self .window_size :
48+ raise PredictorError ("Not enough samples" )
49+ elif self .windowing_mode == 'time' :
50+ window = interpolated .last (str (self .window_size ) + 'ms' )
51+ # this is probably not ideal, with edge-fel NaN values don't throw an error, so js code doesn't drop these
52+ # still, this should only be a problem in the beginning phase since we do interpolation beforehand
53+ window = window .dropna (axis = 0 )
54+ if len (window .index ) <= 0 :
55+ raise PredictorError ("Empty window" )
4256
4357 settings = tsfresh .feature_extraction .settings .MinimalFCParameters ()
4458 features = tsfresh .extract_features (
@@ -48,7 +62,10 @@ def predict(self):
4862 l = features .iloc [0 ].values .tolist ()
4963 pred = self .predictor (l )
5064
51- return pred # TODO: use labels to map labels
65+ return {
66+ 'prediction' : self .labels [pred .index (max (pred ))],
67+ 'result' : pred
68+ }
5269
5370 @staticmethod
5471 def _merge (store ):
0 commit comments