4
4
import matplotlib .pyplot as plt
5
5
from GeneralAnalyser import GeneralAnalyser
6
6
7
+ # TODO: add the name because the same analyser can be used for many plots
7
8
8
9
class ChairAnalyser (GeneralAnalyser ):
9
10
@@ -12,80 +13,181 @@ def __init__(self,
12
13
pic_prefix ,
13
14
sensor_name ,
14
15
session_id ,
15
- measurement_interval = 0.01 ,
16
- measurements_per_batch = 1000 ,
16
+ events_intervals_list = None ,
17
+ interval = 2 ,
18
+ # measurement_interval=0.01,
19
+ # measurements_per_batch=1000,
17
20
name = None ,
21
+ reaction_multiplier = 5 ,
18
22
):
19
- super ().__init__ (df , pic_prefix , sensor_name , session_id )
23
+ super ().__init__ (df , pic_prefix , sensor_name , session_id , name )
20
24
21
- # self.df_chair = df_chair
22
- self .measurement_interval = measurement_interval
23
- self .pic_prefix = pic_prefix
24
- self .measurements_per_batch = measurements_per_batch
25
- self .name = name
25
+ self .interval = interval
26
+ self .reaction_multiplier = reaction_multiplier
27
+ self .events_intervals_list = events_intervals_list
28
+ # self.sensor_columns = ['acc_x', 'acc_y', 'acc_z', 'gyro_x', 'gyro_y', 'gyro_z']
29
+ self .sensor_columns = [ 'acc_x' , 'gyro_x' , 'acc_y' , 'gyro_y' , 'acc_z' , 'gyro_z' ]
26
30
27
- self .means , self .stds , medians = self .create_mean_stds ()
31
+ def get_floating_features (self , interval = 2 ):
32
+ # time_interval = f'{interval}s'
33
+ time_interval = f'{ int (interval * 1000 )} ms'
28
34
29
- def create_mean_stds (self , columns = ('acc_x' , 'acc_y' , 'acc_z' , 'gyro_x' , 'gyro_y' , 'gyro_z' )):
30
- df_chair = self .df .loc [:, columns ]
31
- # df_chair = df_chair.loc[:, columns]
32
- # medians, lower_bounds, upper_bounds = np.percentile(df_chair, [50, percentile2crop, 100 - percentile2crop], axis=0)
35
+ df2roll = self .df .loc [:, ['time' ] + self .sensor_columns ].set_index ('time' )
36
+ df2roll .index = pd .to_timedelta (df2roll .index , unit = 's' )
37
+ df_rolling = df2roll .rolling (time_interval ) # Can't be centered by default
38
+ stds = df_rolling .std ()
39
+ # stds.columns = [f'{column}_std_{time_interval}' for column in stds.columns]
40
+ stds .columns = [f'{ column } _std' for column in stds .columns ]
41
+ stds .reset_index (drop = True , inplace = True )
33
42
34
- means = df_chair .mean (axis = 0 )
35
- medians = df_chair .median (axis = 0 )
36
- stds = df_chair .std (axis = 0 )
43
+ return stds
37
44
38
- return means , stds , medians
45
+ def _append_floating_features (self , interval = 2 ):
46
+ floating_std = self .get_floating_features (interval )
39
47
40
- def get_nonstationary_values_portion (self , n_sigma = 3 ):
41
- means = self .means
42
- stds = self .stds
48
+ self .df = pd .concat ([self .df , floating_std ], axis = 1 )
43
49
44
- columns = stds .index
45
- df_chair = self .df .loc [:, columns ]
46
50
47
- lower_bounds = means - n_sigma * stds
48
- upper_bounds = means + n_sigma * stds
51
+ def get_reactions_mask (self , floating_std , medians , reaction_multiplier ):
52
+ reaction_levels = medians * reaction_multiplier
53
+ reactions_masks = floating_std > reaction_levels .values
49
54
50
- low_values_means = (df_chair .loc [:, columns ] < lower_bounds ).mean ()
51
- high_values_means = (df_chair .loc [:, columns ] > upper_bounds ).mean ()
55
+ return reactions_masks
52
56
53
- nonstationary_values_portion = low_values_means + high_values_means
54
- nonstationary_values_portion .index = [colname + '__nonstationary_portion' for colname in nonstationary_values_portion .index ]
55
- nonstationary_values_portion .name = self .name
57
+ def get_events_masks_dict (self , events_intervals_list ):
58
+ events_masks_dict = {}
56
59
57
- return nonstationary_values_portion
60
+ for events_intervals in events_intervals_list :
61
+ mask_interval = events_intervals .get_mask_intervals_union (self .df ['time' ])
62
+ event_label = events_intervals .label
63
+ events_masks_dict [event_label ] = mask_interval
58
64
59
- # def get_lean_back_portion(acc_z, means_stds=means_stds, n_sigma=5):
60
- def get_lean_back_portion (self , acc_z_threshold = 0.97 ):
61
- df_chair = self .df
62
- lean_back_portion = (df_chair [['acc_z' ]] < acc_z_threshold ).mean ()
63
- lean_back_portion .index = ['lean_back_portion' ]
64
- lean_back_portion .name = self .name
65
+ return events_masks_dict
66
+
67
+ def get_reaction_events_features (self , reactions_mask , events_masks_dict ):
68
+ reactions_mask_sum = reactions_mask .sum (axis = 0 )
65
69
66
- return lean_back_portion
70
+ reaction_events_features_dict = {}
67
71
68
- def get_oscillation_intensity (self , percentile2crop = 10 , columns = ('acc_x' , 'acc_y' , 'acc_z' , 'gyro_x' , 'gyro_y' , 'gyro_z' )):
69
- df_chair = self .df .loc [:, columns ]
70
- result = {}
72
+ for event_label , event_mask in events_masks_dict .items ():
73
+ event_mask_sum = event_mask .sum ()
74
+ # print(type(reactions_mask), type(event_mask))
75
+ reactions_mask_events = reactions_mask .values & event_mask .reshape (- 1 , 1 )
76
+ reactions_mask_events_sum = pd .Series (reactions_mask_events .sum (axis = 0 ), index = reactions_mask_sum .index )
71
77
72
- for column in columns :
73
- lower_bounds , upper_bounds = np .percentile (df_chair .loc [:, column ], [percentile2crop , 100 - percentile2crop ], axis = 0 )
74
- # intervals = upper_bounds - lower_bounds
75
- low_values_mask = (df_chair .loc [:, column ] < lower_bounds )
76
- high_values_mask = (df_chair .loc [:, column ] > upper_bounds )
78
+ # events_in_reactions = reactions_mask_events_sum / reactions_mask_sum
79
+ # events_in_reactions.index = [f'events_in_reactions__{event_label}__{index}' for index in events_in_reactions.index]
80
+ # reaction_events_features_dict.update(events_in_reactions.to_dict())
77
81
78
- normal_values_mask = (~ low_values_mask ) & (~ high_values_mask )
82
+ reactions_in_events = reactions_mask_events_sum / event_mask_sum
83
+ # reactions_in_events.index = [f'reactions_in_events__{event_label}__{index}' for index in reactions_in_events.index]
84
+ reactions_in_events .index = [f'moving_{ event_label } _{ index [:- 4 ]} ' for index in reactions_in_events .index ]
85
+ reaction_events_features_dict .update (reactions_in_events .to_dict ())
79
86
80
- usual_sitting_stds = df_chair .loc [normal_values_mask , column ].std ()
81
- oscillations = usual_sitting_stds # / intervals
82
- feature_name = f'{ column } __oscillations'
83
- result [feature_name ] = oscillations
87
+ return reaction_events_features_dict
84
88
85
- result = pd .Series (result )
86
- result .name = self .name
89
+ def get_reaction_features (self , reactions_mask ):
90
+ reactions_mask_mean = reactions_mask .mean (axis = 0 )
91
+ # reactions_mask_mean.index = [f'reactions_{index}' for index in reactions_mask_mean.index]
92
+ reactions_mask_mean .index = [f'moving_{ index [:- 4 ]} ' for index in reactions_mask_mean .index ]
87
93
88
- return result
94
+ return reactions_mask_mean .to_dict ()
95
+
96
+ def get_lean_back_portion (self , acc_z_threshold = 0.97 ):
97
+ lean_back_portion = (self .df [['acc_z' ]] < acc_z_threshold ).mean ()
98
+ # lean_back_portion.index = ['lean_back_portion']
99
+ # lean_back_portion.name = self.name
100
+
101
+ return {
102
+ # 'lean_back_portion': lean_back_portion.values[0],
103
+ 'lean_back' : lean_back_portion .values [0 ],
104
+ }
105
+
106
+ def get_features (self , interval = None , reaction_multiplier = None ):
107
+ if interval is None :
108
+ interval = self .interval
109
+
110
+ if reaction_multiplier is None :
111
+ reaction_multiplier = self .reaction_multiplier
112
+
113
+ floating_std = self .get_floating_features (interval )
114
+ floating_std_median = floating_std .quantile (0.5 , axis = 0 )
115
+ # floating_std_median.index = [f'median_{index}' for index in floating_std_median.index]
116
+ floating_std_median .index = [f'med_{ index } ' for index in floating_std_median .index ]
117
+
118
+ reactions_mask = self .get_reactions_mask (floating_std , floating_std_median , reaction_multiplier = reaction_multiplier )
119
+
120
+ reaction_features = self .get_reaction_features (reactions_mask )
121
+ oscillations_features = floating_std_median .to_dict ()
122
+ lean_back_portion = self .get_lean_back_portion ()
123
+
124
+ features_list = [reaction_features , oscillations_features , lean_back_portion ]
125
+
126
+ if self .events_intervals_list is not None :
127
+ events_masks_dict = self .get_events_masks_dict (self .events_intervals_list )
128
+ reaction_events_features = self .get_reaction_events_features (reactions_mask , events_masks_dict )
129
+ features_list .append (reaction_events_features )
130
+
131
+ all_features_dict = {}
132
+ for features in features_list :
133
+ all_features_dict .update (features )
134
+
135
+ all_features = pd .Series (all_features_dict , name = self .session_id )
136
+
137
+ return all_features
138
+
139
+ # # def create_mean_stds(self, columns=('acc_x', 'acc_y', 'acc_z', 'gyro_x', 'gyro_y', 'gyro_z')):
140
+ # def create_mean_stds(self):
141
+ # df_chair = self.df.loc[:, self.sensor_columns]
142
+ # # df_chair = df_chair.loc[:, columns]
143
+ # # medians, lower_bounds, upper_bounds = np.percentile(df_chair, [50, percentile2crop, 100 - percentile2crop], axis=0)
144
+ #
145
+ # means = df_chair.mean(axis=0)
146
+ # medians = df_chair.median(axis=0)
147
+ # stds = df_chair.std(axis=0)
148
+ #
149
+ # return means, stds, medians
150
+ #
151
+ # def get_nonstationary_values_portion(self, n_sigma=3):
152
+ # means = self.means
153
+ # stds = self.stds
154
+ #
155
+ # columns = stds.index
156
+ # df_chair = self.df.loc[:, columns]
157
+ #
158
+ # lower_bounds = means - n_sigma * stds
159
+ # upper_bounds = means + n_sigma * stds
160
+ #
161
+ # low_values_means = (df_chair.loc[:, columns] < lower_bounds).mean()
162
+ # high_values_means = (df_chair.loc[:, columns] > upper_bounds).mean()
163
+ #
164
+ # nonstationary_values_portion = low_values_means + high_values_means
165
+ # nonstationary_values_portion.index = [colname + '__nonstationary_portion' for colname in nonstationary_values_portion.index]
166
+ # nonstationary_values_portion.name = self.name
167
+ #
168
+ # return nonstationary_values_portion
169
+ #
170
+ # def get_oscillation_intensity(self, percentile2crop=10, columns=('acc_x', 'acc_y', 'acc_z', 'gyro_x', 'gyro_y', 'gyro_z')):
171
+ # df_chair = self.df.loc[:, columns]
172
+ # result = {}
173
+ #
174
+ # for column in columns:
175
+ # lower_bounds, upper_bounds = np.percentile(df_chair.loc[:, column], [percentile2crop, 100 - percentile2crop], axis=0)
176
+ # # intervals = upper_bounds - lower_bounds
177
+ # low_values_mask = (df_chair.loc[:, column] < lower_bounds)
178
+ # high_values_mask = (df_chair.loc[:, column] > upper_bounds)
179
+ #
180
+ # normal_values_mask = (~low_values_mask) & (~high_values_mask)
181
+ #
182
+ # usual_sitting_stds = df_chair.loc[normal_values_mask, column].std()
183
+ # oscillations = usual_sitting_stds# / intervals
184
+ # feature_name = f'{column}__oscillations'
185
+ # result[feature_name] = oscillations
186
+ #
187
+ # result = pd.Series(result)
188
+ # result.name = self.name
189
+ #
190
+ # return result
89
191
90
192
91
193
0 commit comments