Fola-lad commited on
Commit
13747e9
Β·
1 Parent(s): be090cd

wire up Phyphox pipeline: full 561 feature extraction from raw CSV

Browse files
data/norm_params.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"tBodyAcc-mean()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-mean()-Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-mean()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-std()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-std()-Y": {"min": -0.99987292, "max": 0.91623796}, "tBodyAcc-std()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-mad()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-mad()-Y": {"min": -1.0, "max": 0.96766404}, "tBodyAcc-mad()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-max()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-max()-Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-max()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-min()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-min()-Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-min()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-sma()": {"min": -1.0, "max": 1.0}, "tBodyAcc-energy()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-energy()-Y": {"min": -0.9999986, "max": 1.0}, "tBodyAcc-energy()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-iqr()-X": {"min": -1.0, "max": 1.0}, "tBodyAcc-iqr()-Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-iqr()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-entropy()-X": {"min": -1.0, "max": 0.91966171}, "tBodyAcc-entropy()-Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-entropy()-Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-X,1": {"min": -0.92589735, "max": 1.0}, "tBodyAcc-arCoeff()-X,2": {"min": -0.9630993, "max": 0.97844881}, "tBodyAcc-arCoeff()-X,3": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-X,4": {"min": -0.82205333, "max": 1.0}, "tBodyAcc-arCoeff()-Y,1": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-Y,2": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-Y,3": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-Y,4": {"min": -1.0, "max": 1.0}, "tBodyAcc-arCoeff()-Z,1": {"min": -1.0, "max": 0.81462304}, "tBodyAcc-arCoeff()-Z,2": {"min": -0.75375375, "max": 1.0}, "tBodyAcc-arCoeff()-Z,3": {"min": -1.0, "max": 0.99720675}, "tBodyAcc-arCoeff()-Z,4": {"min": -1.0, "max": 1.0}, "tBodyAcc-correlation()-X,Y": {"min": -1.0, "max": 1.0}, "tBodyAcc-correlation()-X,Z": {"min": -1.0, "max": 1.0}, "tBodyAcc-correlation()-Y,Z": {"min": -0.97221933, "max": 1.0}, "tGravityAcc-mean()-X": {"min": -1.0, "max": 0.99154906}, "tGravityAcc-mean()-Y": {"min": -0.53522237, "max": 1.0}, "tGravityAcc-mean()-Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-std()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-std()-Y": {"min": -0.99982977, "max": 1.0}, "tGravityAcc-std()-Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-mad()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-mad()-Y": {"min": -0.99984878, "max": 1.0}, "tGravityAcc-mad()-Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-max()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-max()-Y": {"min": -0.49387412, "max": 0.96830748}, "tGravityAcc-max()-Z": {"min": -1.0, "max": 0.99658549}, "tGravityAcc-min()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-min()-Y": {"min": -0.56815651, "max": 1.0}, "tGravityAcc-min()-Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-sma()": {"min": -1.0, "max": 1.0}, "tGravityAcc-energy()-X": {"min": -1.0, "max": 0.975855}, "tGravityAcc-energy()-Y": {"min": -1.0, "max": 1.0}, "tGravityAcc-energy()-Z": {"min": -0.99999923, "max": 1.0}, "tGravityAcc-iqr()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-iqr()-Y": {"min": -0.99994572, "max": 1.0}, "tGravityAcc-iqr()-Z": {"min": -0.99996947, "max": 1.0}, "tGravityAcc-entropy()-X": {"min": -1.0, "max": 1.0}, "tGravityAcc-entropy()-Y": {"min": -1.0, "max": 1.0}, "tGravityAcc-entropy()-Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-X,1": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-X,2": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-X,3": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-X,4": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-Y,1": {"min": -1.0, "max": 0.81316975}, "tGravityAcc-arCoeff()-Y,2": {"min": -0.95417313, "max": 1.0}, "tGravityAcc-arCoeff()-Y,3": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-Y,4": {"min": -1.0, "max": 1.0}, "tGravityAcc-arCoeff()-Z,1": {"min": -1.0, "max": 0.64877436}, "tGravityAcc-arCoeff()-Z,2": {"min": -0.61763642, "max": 1.0}, "tGravityAcc-arCoeff()-Z,3": {"min": -1.0, "max": 0.58576939}, "tGravityAcc-arCoeff()-Z,4": {"min": -0.55400018, "max": 1.0}, "tGravityAcc-correlation()-X,Y": {"min": -1.0, "max": 1.0}, "tGravityAcc-correlation()-X,Z": {"min": -1.0, "max": 1.0}, "tGravityAcc-correlation()-Y,Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mean()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mean()-Y": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mean()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-std()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-std()-Y": {"min": -1.0, "max": 0.80707793}, "tBodyAccJerk-std()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mad()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mad()-Y": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-mad()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-max()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-max()-Y": {"min": -1.0, "max": 0.62437285}, "tBodyAccJerk-max()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-min()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-min()-Y": {"min": -0.74690717, "max": 1.0}, "tBodyAccJerk-min()-Z": {"min": -1.0, "max": 0.99926934}, "tBodyAccJerk-sma()": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-energy()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-energy()-Y": {"min": -1.0, "max": 0.63441522}, "tBodyAccJerk-energy()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-iqr()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-iqr()-Y": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-iqr()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-entropy()-X": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-entropy()-Y": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-entropy()-Z": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-X,1": {"min": -0.97483758, "max": 1.0}, "tBodyAccJerk-arCoeff()-X,2": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-X,3": {"min": -1.0, "max": 0.96457587}, "tBodyAccJerk-arCoeff()-X,4": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-Y,1": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-Y,2": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-Y,3": {"min": -1.0, "max": 0.99159215}, "tBodyAccJerk-arCoeff()-Y,4": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-arCoeff()-Z,1": {"min": -1.0, "max": 0.92520697}, "tBodyAccJerk-arCoeff()-Z,2": {"min": -0.79906863, "max": 1.0}, "tBodyAccJerk-arCoeff()-Z,3": {"min": -1.0, "max": 0.93354746}, "tBodyAccJerk-arCoeff()-Z,4": {"min": -1.0, "max": 1.0}, "tBodyAccJerk-correlation()-X,Y": {"min": -1.0, "max": 0.90448}, "tBodyAccJerk-correlation()-X,Z": {"min": -1.0, "max": 0.99325846}, "tBodyAccJerk-correlation()-Y,Z": {"min": -0.95950504, "max": 1.0}, "tBodyGyro-mean()-X": {"min": -0.91404957, "max": 0.73895313}, "tBodyGyro-mean()-Y": {"min": -0.85197318, "max": 1.0}, "tBodyGyro-mean()-Z": {"min": -0.90285978, "max": 1.0}, "tBodyGyro-std()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-std()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-std()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyro-mad()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-mad()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-mad()-Z": {"min": -0.99991531, "max": 1.0}, "tBodyGyro-max()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-max()-Y": {"min": -0.99801363, "max": 1.0}, "tBodyGyro-max()-Z": {"min": -0.9434386, "max": 1.0}, "tBodyGyro-min()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-min()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-min()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyro-sma()": {"min": -1.0, "max": 1.0}, "tBodyGyro-energy()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-energy()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-energy()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyro-iqr()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-iqr()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-iqr()-Z": {"min": -0.99998266, "max": 1.0}, "tBodyGyro-entropy()-X": {"min": -1.0, "max": 1.0}, "tBodyGyro-entropy()-Y": {"min": -1.0, "max": 0.972798}, "tBodyGyro-entropy()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-X,1": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-X,2": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-X,3": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-X,4": {"min": -0.96659121, "max": 0.85273955}, "tBodyGyro-arCoeff()-Y,1": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-Y,2": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-Y,3": {"min": -0.96253937, "max": 1.0}, "tBodyGyro-arCoeff()-Y,4": {"min": -1.0, "max": 1.0}, "tBodyGyro-arCoeff()-Z,1": {"min": -0.90717752, "max": 0.86609404}, "tBodyGyro-arCoeff()-Z,2": {"min": -0.92338795, "max": 0.94520707}, "tBodyGyro-arCoeff()-Z,3": {"min": -0.94730392, "max": 1.0}, "tBodyGyro-arCoeff()-Z,4": {"min": -1.0, "max": 0.98513795}, "tBodyGyro-correlation()-X,Y": {"min": -1.0, "max": 1.0}, "tBodyGyro-correlation()-X,Z": {"min": -1.0, "max": 1.0}, "tBodyGyro-correlation()-Y,Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-mean()-X": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-mean()-Y": {"min": -1.0, "max": 0.84803136}, "tBodyGyroJerk-mean()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-std()-X": {"min": -0.99988263, "max": 1.0}, "tBodyGyroJerk-std()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-std()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-mad()-X": {"min": -0.9998892, "max": 1.0}, "tBodyGyroJerk-mad()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-mad()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-max()-X": {"min": -1.0, "max": 0.92841647}, "tBodyGyroJerk-max()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-max()-Z": {"min": -1.0, "max": 0.97903145}, "tBodyGyroJerk-min()-X": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-min()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-min()-Z": {"min": -0.7597897, "max": 1.0}, "tBodyGyroJerk-sma()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-energy()-X": {"min": -0.99999952, "max": 1.0}, "tBodyGyroJerk-energy()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-energy()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-iqr()-X": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-iqr()-Y": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-iqr()-Z": {"min": -0.9996578, "max": 1.0}, "tBodyGyroJerk-entropy()-X": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-entropy()-Y": {"min": -1.0, "max": 0.99093504}, "tBodyGyroJerk-entropy()-Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-arCoeff()-X,1": {"min": -0.90591841, "max": 1.0}, "tBodyGyroJerk-arCoeff()-X,2": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-arCoeff()-X,3": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-arCoeff()-X,4": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-arCoeff()-Y,1": {"min": -0.92113806, "max": 1.0}, "tBodyGyroJerk-arCoeff()-Y,2": {"min": -1.0, "max": 0.89744607}, "tBodyGyroJerk-arCoeff()-Y,3": {"min": -0.77359738, "max": 0.99901522}, "tBodyGyroJerk-arCoeff()-Y,4": {"min": -1.0, "max": 0.92337662}, "tBodyGyroJerk-arCoeff()-Z,1": {"min": -0.95282078, "max": 0.94558999}, "tBodyGyroJerk-arCoeff()-Z,2": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-arCoeff()-Z,3": {"min": -0.95389422, "max": 1.0}, "tBodyGyroJerk-arCoeff()-Z,4": {"min": -0.95448559, "max": 1.0}, "tBodyGyroJerk-correlation()-X,Y": {"min": -0.8952687, "max": 1.0}, "tBodyGyroJerk-correlation()-X,Z": {"min": -1.0, "max": 1.0}, "tBodyGyroJerk-correlation()-Y,Z": {"min": -1.0, "max": 0.99680328}, "tBodyAccMag-mean()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-std()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-mad()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-max()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-min()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-sma()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-energy()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-iqr()": {"min": -1.0, "max": 1.0}, "tBodyAccMag-entropy()": {"min": -0.99537696, "max": 1.0}, "tBodyAccMag-arCoeff()1": {"min": -1.0, "max": 1.0}, "tBodyAccMag-arCoeff()2": {"min": -1.0, "max": 1.0}, "tBodyAccMag-arCoeff()3": {"min": -1.0, "max": 0.994731}, "tBodyAccMag-arCoeff()4": {"min": -1.0, "max": 1.0}, "tGravityAccMag-mean()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-std()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-mad()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-max()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-min()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-sma()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-energy()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-iqr()": {"min": -1.0, "max": 1.0}, "tGravityAccMag-entropy()": {"min": -0.99537696, "max": 1.0}, "tGravityAccMag-arCoeff()1": {"min": -1.0, "max": 1.0}, "tGravityAccMag-arCoeff()2": {"min": -1.0, "max": 1.0}, "tGravityAccMag-arCoeff()3": {"min": -1.0, "max": 0.994731}, "tGravityAccMag-arCoeff()4": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-mean()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-std()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-mad()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-max()": {"min": -1.0, "max": 0.98365438}, "tBodyAccJerkMag-min()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-sma()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-energy()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-iqr()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-entropy()": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-arCoeff()1": {"min": -1.0, "max": 0.86283136}, "tBodyAccJerkMag-arCoeff()2": {"min": -1.0, "max": 1.0}, "tBodyAccJerkMag-arCoeff()3": {"min": -0.83055874, "max": 0.91434541}, "tBodyAccJerkMag-arCoeff()4": {"min": -1.0, "max": 0.97433286}, "tBodyGyroMag-mean()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-std()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-mad()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-max()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-min()": {"min": -0.99983528, "max": 1.0}, "tBodyGyroMag-sma()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-energy()": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-iqr()": {"min": -1.0, "max": 0.96899245}, "tBodyGyroMag-entropy()": {"min": -1.0, "max": 0.99263589}, "tBodyGyroMag-arCoeff()1": {"min": -1.0, "max": 0.99352363}, "tBodyGyroMag-arCoeff()2": {"min": -1.0, "max": 1.0}, "tBodyGyroMag-arCoeff()3": {"min": -0.89991431, "max": 0.99462757}, "tBodyGyroMag-arCoeff()4": {"min": -0.99742291, "max": 1.0}, "tBodyGyroJerkMag-mean()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-std()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-mad()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-max()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-min()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-sma()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-energy()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-iqr()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-entropy()": {"min": -1.0, "max": 1.0}, "tBodyGyroJerkMag-arCoeff()1": {"min": -1.0, "max": 0.99282314}, "tBodyGyroJerkMag-arCoeff()2": {"min": -0.99766425, "max": 1.0}, "tBodyGyroJerkMag-arCoeff()3": {"min": -1.0, "max": 0.88033546}, "tBodyGyroJerkMag-arCoeff()4": {"min": -1.0, "max": 0.83672766}, "fBodyAcc-mean()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-mean()-Y": {"min": -1.0, "max": 0.97184989}, "fBodyAcc-mean()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-std()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-std()-Y": {"min": -0.9998571, "max": 0.86033908}, "fBodyAcc-std()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-mad()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-mad()-Y": {"min": -0.99997862, "max": 0.96120357}, "fBodyAcc-mad()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-max()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-max()-Y": {"min": -1.0, "max": 1.0}, "fBodyAcc-max()-Z": {"min": -0.99980523, "max": 1.0}, "fBodyAcc-min()-X": {"min": -0.99999831, "max": 1.0}, "fBodyAcc-min()-Y": {"min": -1.0, "max": 1.0}, "fBodyAcc-min()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-sma()": {"min": -1.0, "max": 1.0}, "fBodyAcc-energy()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-energy()-Y": {"min": -0.99999872, "max": 0.83686006}, "fBodyAcc-energy()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-iqr()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-iqr()-Y": {"min": -1.0, "max": 1.0}, "fBodyAcc-iqr()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-entropy()-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-entropy()-Y": {"min": -1.0, "max": 0.90901463}, "fBodyAcc-entropy()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-maxInds-X": {"min": -1.0, "max": 1.0}, "fBodyAcc-maxInds-Y": {"min": -1.0, "max": 1.0}, "fBodyAcc-maxInds-Z": {"min": -1.0, "max": 0.92307692}, "fBodyAcc-meanFreq()-X": {"min": -1.0, "max": 0.9141467}, "fBodyAcc-meanFreq()-Y": {"min": -1.0, "max": 1.0}, "fBodyAcc-meanFreq()-Z": {"min": -1.0, "max": 1.0}, "fBodyAcc-skewness()-X": {"min": -0.93120793, "max": 1.0}, "fBodyAcc-kurtosis()-X": {"min": -0.99941209, "max": 1.0}, "fBodyAcc-skewness()-Y": {"min": -1.0, "max": 0.97555005}, "fBodyAcc-kurtosis()-Y": {"min": -1.0, "max": 0.96627476}, "fBodyAcc-skewness()-Z": {"min": -1.0, "max": 0.9849423}, "fBodyAcc-kurtosis()-Z": {"min": -1.0, "max": 0.97998881}, "fBodyAcc-bandsEnergy()-1,8": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-9,16": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-17,24": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-25,32": {"min": -0.99999405, "max": 0.88145236}, "fBodyAcc-bandsEnergy()-33,40": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-41,48": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-49,56": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-57,64": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,16": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-17,32": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-33,48": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-49,64": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,24": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-25,48": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,8.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-9,16.1": {"min": -1.0, "max": 0.94996979}, "fBodyAcc-bandsEnergy()-17,24.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-25,32.1": {"min": -1.0, "max": 0.95807252}, "fBodyAcc-bandsEnergy()-33,40.1": {"min": -1.0, "max": 0.84661001}, "fBodyAcc-bandsEnergy()-41,48.1": {"min": -1.0, "max": 0.89774723}, "fBodyAcc-bandsEnergy()-49,56.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-57,64.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,16.1": {"min": -1.0, "max": 0.86657061}, "fBodyAcc-bandsEnergy()-17,32.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-33,48.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-49,64.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,24.1": {"min": -0.99999419, "max": 0.8210904}, "fBodyAcc-bandsEnergy()-25,48.1": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,8.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-9,16.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-17,24.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-25,32.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-33,40.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-41,48.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-49,56.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-57,64.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,16.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-17,32.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-33,48.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-49,64.2": {"min": -0.99999706, "max": 1.0}, "fBodyAcc-bandsEnergy()-1,24.2": {"min": -1.0, "max": 1.0}, "fBodyAcc-bandsEnergy()-25,48.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-mean()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-mean()-Y": {"min": -1.0, "max": 0.64680948}, "fBodyAccJerk-mean()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-std()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-std()-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-std()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-mad()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-mad()-Y": {"min": -1.0, "max": 0.80506356}, "fBodyAccJerk-mad()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-max()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-max()-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-max()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-min()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-min()-Y": {"min": -0.99999774, "max": 1.0}, "fBodyAccJerk-min()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-sma()": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-energy()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-energy()-Y": {"min": -1.0, "max": 0.63432019}, "fBodyAccJerk-energy()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-iqr()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-iqr()-Y": {"min": -1.0, "max": 0.64132146}, "fBodyAccJerk-iqr()-Z": {"min": -0.9991623, "max": 1.0}, "fBodyAccJerk-entropy()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-entropy()-Y": {"min": -1.0, "max": 0.99682502}, "fBodyAccJerk-entropy()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-maxInds-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-maxInds-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-maxInds-Z": {"min": -1.0, "max": 0.96}, "fBodyAccJerk-meanFreq()-X": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-meanFreq()-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-meanFreq()-Z": {"min": -1.0, "max": 0.67862794}, "fBodyAccJerk-skewness()-X": {"min": -1.0, "max": 0.82920557}, "fBodyAccJerk-kurtosis()-X": {"min": -1.0, "max": 0.66004197}, "fBodyAccJerk-skewness()-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-kurtosis()-Y": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-skewness()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-kurtosis()-Z": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,8": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-9,16": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-17,24": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-25,32": {"min": -0.99999606, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-33,40": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-41,48": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,56": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-57,64": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,16": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-17,32": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-33,48": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,64": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,24": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-25,48": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,8.1": {"min": -1.0, "max": 0.70292796}, "fBodyAccJerk-bandsEnergy()-9,16.1": {"min": -1.0, "max": 0.89173636}, "fBodyAccJerk-bandsEnergy()-17,24.1": {"min": -1.0, "max": 0.90836103}, "fBodyAccJerk-bandsEnergy()-25,32.1": {"min": -1.0, "max": 0.88786954}, "fBodyAccJerk-bandsEnergy()-33,40.1": {"min": -1.0, "max": 0.63241538}, "fBodyAccJerk-bandsEnergy()-41,48.1": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,56.1": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-57,64.1": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,16.1": {"min": -1.0, "max": 0.89789843}, "fBodyAccJerk-bandsEnergy()-17,32.1": {"min": -1.0, "max": 0.9303172}, "fBodyAccJerk-bandsEnergy()-33,48.1": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,64.1": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,24.1": {"min": -1.0, "max": 0.91489223}, "fBodyAccJerk-bandsEnergy()-25,48.1": {"min": -1.0, "max": 0.80737469}, "fBodyAccJerk-bandsEnergy()-1,8.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-9,16.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-17,24.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-25,32.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-33,40.2": {"min": -0.99998949, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-41,48.2": {"min": -0.99999894, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,56.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-57,64.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,16.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-17,32.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-33,48.2": {"min": -0.99996684, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-49,64.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-1,24.2": {"min": -1.0, "max": 1.0}, "fBodyAccJerk-bandsEnergy()-25,48.2": {"min": -0.99999585, "max": 1.0}, "fBodyGyro-mean()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-mean()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-mean()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-std()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-std()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-std()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-mad()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-mad()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-mad()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-max()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-max()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-max()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-min()-X": {"min": -0.99999996, "max": 0.46519723}, "fBodyGyro-min()-Y": {"min": -0.9999967, "max": 0.79449806}, "fBodyGyro-min()-Z": {"min": -0.99999972, "max": 1.0}, "fBodyGyro-sma()": {"min": -1.0, "max": 1.0}, "fBodyGyro-energy()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-energy()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-energy()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-iqr()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-iqr()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-iqr()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-entropy()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-entropy()-Y": {"min": -1.0, "max": 1.0}, "fBodyGyro-entropy()-Z": {"min": -1.0, "max": 1.0}, "fBodyGyro-maxInds-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-maxInds-Y": {"min": -1.0, "max": 0.80645161}, "fBodyGyro-maxInds-Z": {"min": -1.0, "max": 0.65517241}, "fBodyGyro-meanFreq()-X": {"min": -1.0, "max": 1.0}, "fBodyGyro-meanFreq()-Y": {"min": -1.0, "max": 0.99374103}, "fBodyGyro-meanFreq()-Z": {"min": -0.96634502, "max": 1.0}, "fBodyGyro-skewness()-X": {"min": -1.0, "max": 0.92089008}, "fBodyGyro-kurtosis()-X": {"min": -1.0, "max": 0.8944949}, "fBodyGyro-skewness()-Y": {"min": -1.0, "max": 0.98132937}, "fBodyGyro-kurtosis()-Y": {"min": -0.99821279, "max": 0.97760819}, "fBodyGyro-skewness()-Z": {"min": -0.90898061, "max": 0.9035711}, "fBodyGyro-kurtosis()-Z": {"min": -1.0, "max": 0.857113}, "fBodyGyro-bandsEnergy()-1,8": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-9,16": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,24": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,32": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,40": {"min": -0.99999991, "max": 1.0}, "fBodyGyro-bandsEnergy()-41,48": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,56": {"min": -0.99999967, "max": 1.0}, "fBodyGyro-bandsEnergy()-57,64": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,16": {"min": -0.99999982, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,32": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,48": {"min": -0.99999696, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,64": {"min": -0.99999982, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,24": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,48": {"min": -0.99999832, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,8.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-9,16.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,24.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,32.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,40.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-41,48.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,56.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-57,64.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,16.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,32.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,48.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,64.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,24.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,48.1": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,8.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-9,16.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,24.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,32.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,40.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-41,48.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,56.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-57,64.2": {"min": -0.99999999, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,16.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-17,32.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-33,48.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-49,64.2": {"min": -1.0, "max": 1.0}, "fBodyGyro-bandsEnergy()-1,24.2": {"min": -0.99999974, "max": 1.0}, "fBodyGyro-bandsEnergy()-25,48.2": {"min": -1.0, "max": 1.0}, "fBodyAccMag-mean()": {"min": -0.99989278, "max": 1.0}, "fBodyAccMag-std()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-mad()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-max()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-min()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-sma()": {"min": -0.99989278, "max": 1.0}, "fBodyAccMag-energy()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-iqr()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-entropy()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-maxInds": {"min": -1.0, "max": 1.0}, "fBodyAccMag-meanFreq()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-skewness()": {"min": -1.0, "max": 1.0}, "fBodyAccMag-kurtosis()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-mean()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-std()": {"min": -0.99996007, "max": 1.0}, "fBodyBodyAccJerkMag-mad()": {"min": -0.9991519, "max": 1.0}, "fBodyBodyAccJerkMag-max()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-min()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-sma()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-energy()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-iqr()": {"min": -0.99949935, "max": 1.0}, "fBodyBodyAccJerkMag-entropy()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-maxInds": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-meanFreq()": {"min": -1.0, "max": 0.97582069}, "fBodyBodyAccJerkMag-skewness()": {"min": -1.0, "max": 1.0}, "fBodyBodyAccJerkMag-kurtosis()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-mean()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-std()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-mad()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-max()": {"min": -1.0, "max": 0.84211941}, "fBodyBodyGyroMag-min()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-sma()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-energy()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-iqr()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-entropy()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-maxInds": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroMag-meanFreq()": {"min": -0.99750026, "max": 1.0}, "fBodyBodyGyroMag-skewness()": {"min": -1.0, "max": 0.96931086}, "fBodyBodyGyroMag-kurtosis()": {"min": -1.0, "max": 0.94935049}, "fBodyBodyGyroJerkMag-mean()": {"min": -0.99999636, "max": 1.0}, "fBodyBodyGyroJerkMag-std()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-mad()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-max()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-min()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-sma()": {"min": -0.99999636, "max": 1.0}, "fBodyBodyGyroJerkMag-energy()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-iqr()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-entropy()": {"min": -1.0, "max": 1.0}, "fBodyBodyGyroJerkMag-maxInds": {"min": -1.0, "max": 0.96825397}, "fBodyBodyGyroJerkMag-meanFreq()": {"min": -1.0, "max": 0.94669994}, "fBodyBodyGyroJerkMag-skewness()": {"min": -0.99535747, "max": 0.98953847}, "fBodyBodyGyroJerkMag-kurtosis()": {"min": -0.99976469, "max": 0.95684539}, "angle(tBodyAccMean,gravity)": {"min": -0.97658002, "max": 1.0}, "angle(tBodyAccJerkMean),gravityMean)": {"min": -1.0, "max": 1.0}, "angle(tBodyGyroMean,gravityMean)": {"min": -1.0, "max": 0.99870219}, "angle(tBodyGyroJerkMean,gravityMean)": {"min": -1.0, "max": 0.99607819}, "angle(X,gravityMean)": {"min": -1.0, "max": 1.0}, "angle(Y,gravityMean)": {"min": -1.0, "max": 0.47815733}, "angle(Z,gravityMean)": {"min": -1.0, "max": 1.0}}
src/phyphox_app_block.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Streamlit UI block for Tab 2 β€” Phyphox live sensor upload.
2
+
3
+ Call from streamlit_app.py:
4
+
5
+ from phyphox_app_block import render_phyphox_tab
6
+ with tab2:
7
+ render_phyphox_tab(ffn_model, ffn_status, cnn_model, cnn_status)
8
+ """
9
+
10
+ import json
11
+ import os
12
+
13
+ import numpy as np
14
+ import pandas as pd
15
+ import streamlit as st
16
+
17
+ from phyphox_pipeline import process_phyphox_files, FS, WINDOW, STEP
18
+
19
+ # ── Constants ─────────────────────────────────────────────────────────────────
20
+
21
+ LABEL_MAP = {
22
+ 0: "WALKING",
23
+ 1: "WALKING_UPSTAIRS",
24
+ 2: "WALKING_DOWNSTAIRS",
25
+ 3: "SITTING",
26
+ 4: "STANDING",
27
+ 5: "LAYING",
28
+ }
29
+
30
+ EXPLANATIONS = {
31
+ "LAYING": "Minimal movement detected across all axes β€” consistent with a stationary horizontal posture.",
32
+ "SITTING": "Low dynamic acceleration with stable gravity β€” stationary upright posture.",
33
+ "STANDING": "Similar to sitting with slight postural micro-movements.",
34
+ "WALKING": "Rhythmic periodic acceleration on the vertical axis β€” level walking at normal cadence.",
35
+ "WALKING_DOWNSTAIRS": "Downward gravitational shift with higher impact peaks β€” descending stairs.",
36
+ "WALKING_UPSTAIRS": "Elevated vertical acceleration effort β€” climbing stairs.",
37
+ }
38
+
39
+ # ── Normalisation ─────────────────────────────────────────────────────────────
40
+
41
+ @st.cache_resource
42
+ def _load_norm_params(norm_path: str):
43
+ """Load per-feature min/max from norm_params.json in features.txt order."""
44
+ with open(norm_path) as f:
45
+ d = json.load(f)
46
+ min_vals = np.array([v["min"] for v in d.values()], dtype=np.float32)
47
+ max_vals = np.array([v["max"] for v in d.values()], dtype=np.float32)
48
+ return min_vals, max_vals
49
+
50
+
51
+ def _normalize(features: np.ndarray, min_vals: np.ndarray, max_vals: np.ndarray) -> np.ndarray:
52
+ """Best-effort feature-level min-max scaling to [-1, 1].
53
+
54
+ Uses per-feature min/max observed in the UCI HAR training set. This is
55
+ an approximation β€” the UCI pipeline normalises raw signals before feature
56
+ extraction, so physical-unit features may fall outside the training range.
57
+ Values are clipped before scaling to keep outputs bounded.
58
+ """
59
+ rng = max_vals - min_vals
60
+ rng = np.where(rng < 1e-8, 1.0, rng) # avoid div-by-zero
61
+ clipped = np.clip(features, min_vals, max_vals)
62
+ return 2.0 * (clipped - min_vals) / rng - 1.0
63
+
64
+
65
+ # ── Public render function ────────────────────────────────────────────────────
66
+
67
+ def render_phyphox_tab(
68
+ ffn_model, ffn_status: str,
69
+ cnn_model, cnn_status: str,
70
+ norm_params_path: str,
71
+ ) -> None:
72
+ st.subheader("Upload Phyphox sensor recording")
73
+ st.markdown("""
74
+ **How to record your own data:**
75
+ 1. Install [Phyphox](https://phyphox.org/) on your phone
76
+ 2. Open **Acceleration (without g)** and **Gyroscope** β€” record simultaneously
77
+ 3. Hold the phone at your waist (same position as the UCI dataset)
78
+ 4. Record at least 3 seconds of a single activity
79
+ 5. Export both experiments as CSV and upload below
80
+ """)
81
+
82
+ col1, col2 = st.columns(2)
83
+ with col1:
84
+ acc_file = st.file_uploader(
85
+ "Accelerometer CSV",
86
+ type=["csv"],
87
+ key="acc_upload",
88
+ help="Columns: Time (s), X (m/sΒ²), Y (m/sΒ²), Z (m/sΒ²)",
89
+ )
90
+ with col2:
91
+ gyro_file = st.file_uploader(
92
+ "Gyroscope CSV",
93
+ type=["csv"],
94
+ key="gyro_upload",
95
+ help="Columns: Time (s), X (rad/s), Y (rad/s), Z (rad/s)",
96
+ )
97
+
98
+ if acc_file is None or gyro_file is None:
99
+ st.info("Upload both files to continue.")
100
+ return
101
+
102
+ # ── Feature extraction ────────────────────────────────────────────────────
103
+ try:
104
+ with st.spinner("Extracting 561 features from sensor data…"):
105
+ features, pipeline_warnings = process_phyphox_files(acc_file, gyro_file)
106
+ except ValueError as err:
107
+ st.error(str(err))
108
+ return
109
+ except Exception as err:
110
+ st.error(f"Unexpected error during feature extraction: {err}")
111
+ return
112
+
113
+ for w in pipeline_warnings:
114
+ st.warning(w)
115
+
116
+ n_windows = len(features)
117
+ duration_s = (n_windows - 1) * (STEP / FS) + (WINDOW / FS)
118
+
119
+ c1, c2, c3 = st.columns(3)
120
+ c1.metric("Windows extracted", n_windows)
121
+ c2.metric("Approx. duration", f"{duration_s:.1f} s")
122
+ c3.metric("Features per window", 561)
123
+ st.caption(
124
+ f"Each window = {WINDOW / FS:.2f} s at {FS} Hz Β· "
125
+ f"50% overlap ({STEP / FS:.2f} s hop)"
126
+ )
127
+
128
+ # ── Normalisation ─────────────────────────────────────────────────────────
129
+ if os.path.exists(norm_params_path):
130
+ min_vals, max_vals = _load_norm_params(norm_params_path)
131
+ features = _normalize(features, min_vals, max_vals)
132
+ st.caption(
133
+ "Features scaled to [βˆ’1, 1] using per-feature min/max from the UCI HAR "
134
+ "training set. Values outside the training range are clipped before scaling."
135
+ )
136
+ else:
137
+ st.warning(
138
+ "norm_params.json not found β€” features are in physical units. "
139
+ "Predictions will be unreliable until normalisation is applied."
140
+ )
141
+
142
+ # ── Predictions ───────────────────────────────────────────────────────────
143
+ if ffn_status != "ready" and cnn_status != "ready":
144
+ st.warning("Models not loaded β€” cannot predict yet.")
145
+ return
146
+
147
+ st.markdown("---")
148
+ st.subheader("Model comparison")
149
+
150
+ left, right = st.columns(2)
151
+
152
+ def _render_model_col(col, model, status, name):
153
+ with col:
154
+ st.markdown(f"#### {name}")
155
+ if status != "ready":
156
+ st.error(f"Model not loaded β€” {status}")
157
+ return
158
+
159
+ probs_all = model.predict(features, verbose=0) # (n_windows, 6)
160
+ pred_labels = [LABEL_MAP[int(np.argmax(p))] for p in probs_all]
161
+
162
+ from collections import Counter
163
+ vote = Counter(pred_labels).most_common(1)[0][0]
164
+ avg_conf = float(np.mean(np.max(probs_all, axis=1))) * 100
165
+
166
+ st.success(f"**{vote}** Β· {avg_conf:.1f}% avg confidence")
167
+ st.markdown(f"_{EXPLANATIONS[vote]}_")
168
+
169
+ if n_windows > 1:
170
+ with st.expander(f"Per-window breakdown ({n_windows} windows)"):
171
+ rows = []
172
+ for i, (p, label) in enumerate(zip(probs_all, pred_labels)):
173
+ t_start = i * STEP / FS
174
+ rows.append({
175
+ "Window": i + 1,
176
+ "Time (s)": f"{t_start:.1f}–{t_start + WINDOW/FS:.1f}",
177
+ "Prediction": label,
178
+ "Confidence": f"{float(np.max(p))*100:.1f}%",
179
+ })
180
+ st.dataframe(pd.DataFrame(rows), use_container_width=True)
181
+
182
+ mean_probs = probs_all.mean(axis=0)
183
+ st.markdown("**Average confidence across all classes**")
184
+ st.bar_chart(pd.DataFrame(
185
+ {"Confidence (%)": [float(mean_probs[i]) * 100 for i in range(6)]},
186
+ index=[LABEL_MAP[i] for i in range(6)],
187
+ ))
188
+
189
+ _render_model_col(left, ffn_model, ffn_status, "Feedforward Network")
190
+ _render_model_col(right, cnn_model, cnn_status, "1D Convolutional Network")
src/phyphox_pipeline.py ADDED
@@ -0,0 +1,516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Phyphox sensor pipeline for Human Activity Recognition.
2
+
3
+ Converts raw Phyphox accelerometer + gyroscope CSV exports into the
4
+ 561-feature vector expected by the UCI HAR classifier.
5
+
6
+ Feature order matches features.txt exactly:
7
+ 1-200 : time-domain 3-axis signals (5 signals Γ— 40 features)
8
+ 201-265 : time-domain magnitudes (5 signals Γ— 13 features)
9
+ 266-502 : frequency-domain 3-axis (3 signals Γ— 79 features)
10
+ 503-554 : frequency-domain magnitudes (4 signals Γ— 13 features)
11
+ 555-561 : angle features (7 features)
12
+ """
13
+
14
+ import io
15
+ import numpy as np
16
+ import pandas as pd
17
+ from scipy import signal as sp_signal
18
+ from scipy.stats import skew, kurtosis as sp_kurtosis
19
+
20
+ # ── Constants ─────────────────────────────────────────────────────────────────
21
+
22
+ FS = 50 # target sampling rate Hz
23
+ WINDOW = 128 # samples per window (2.56 s)
24
+ STEP = 64 # hop size β€” 50% overlap
25
+ AR_ORDER = 4 # Burg AR model order
26
+
27
+ # 14 frequency band pairs (1-indexed, inclusive) applied per axis
28
+ BANDS = [
29
+ (1, 8), (9, 16), (17, 24), (25, 32), (33, 40), (41, 48),
30
+ (49, 56), (57, 64), (1, 16), (17, 32), (33, 48), (49, 64),
31
+ (1, 24), (25, 48),
32
+ ]
33
+
34
+ ACC_COLS = ["Time (s)", "X (m/s^2)", "Y (m/s^2)", "Z (m/s^2)"]
35
+ GYRO_COLS = ["Time (s)", "X (rad/s)", "Y (rad/s)", "Z (rad/s)"]
36
+
37
+
38
+ # ── CSV helpers ───────────────────────────────────────────────────────────────
39
+
40
+ def _parse_csv(file_obj, expected_cols: list) -> pd.DataFrame:
41
+ """Parse a Phyphox CSV export and validate required columns.
42
+
43
+ Args:
44
+ file_obj: file-like object (bytes or str) from Phyphox export
45
+ expected_cols: list of required column names
46
+
47
+ Returns:
48
+ DataFrame with numeric data, NaN rows dropped
49
+
50
+ Raises:
51
+ ValueError: if columns are missing or file cannot be parsed
52
+ """
53
+ try:
54
+ raw = file_obj.read()
55
+ if isinstance(raw, bytes):
56
+ raw = raw.decode("utf-8")
57
+ df = pd.read_csv(io.StringIO(raw), float_precision="high")
58
+ except Exception as exc:
59
+ raise ValueError(f"Cannot parse CSV: {exc}") from exc
60
+
61
+ df.columns = [c.strip('"').strip() for c in df.columns]
62
+ missing = [c for c in expected_cols if c not in df.columns]
63
+ if missing:
64
+ raise ValueError(
65
+ f"Missing columns {missing}. Found: {list(df.columns)}. "
66
+ "Check you uploaded the correct Phyphox CSV (Accelerometer or Gyroscope)."
67
+ )
68
+ return df[expected_cols].apply(pd.to_numeric, errors="coerce").dropna()
69
+
70
+
71
+ # ── DSP helpers ───────────────────────────────────────────────────────────────
72
+
73
+ def _butter_lp(data: np.ndarray, cutoff: float, fs: float = FS, order: int = 3) -> np.ndarray:
74
+ """Zero-phase Butterworth low-pass filter applied along axis 0.
75
+
76
+ Args:
77
+ data: 1-D or 2-D array
78
+ cutoff: cutoff frequency in Hz
79
+ fs: sampling rate in Hz
80
+ order: filter order
81
+
82
+ Returns:
83
+ Filtered array, same shape as input
84
+ """
85
+ b, a = sp_signal.butter(order, cutoff / (fs / 2.0), btype="low")
86
+ if data.ndim == 1:
87
+ return sp_signal.filtfilt(b, a, data)
88
+ return np.column_stack(
89
+ [sp_signal.filtfilt(b, a, data[:, i]) for i in range(data.shape[1])]
90
+ )
91
+
92
+
93
+ def _median_filt(data: np.ndarray, k: int = 3) -> np.ndarray:
94
+ """Median filter applied along axis 0.
95
+
96
+ Args:
97
+ data: 1-D or 2-D array
98
+ k: kernel size (must be odd)
99
+
100
+ Returns:
101
+ Filtered array, same shape as input
102
+ """
103
+ if data.ndim == 1:
104
+ return sp_signal.medfilt(data, kernel_size=k)
105
+ return np.column_stack(
106
+ [sp_signal.medfilt(data[:, i], kernel_size=k) for i in range(data.shape[1])]
107
+ )
108
+
109
+
110
+ def _burg_ar(x: np.ndarray, order: int = AR_ORDER) -> np.ndarray:
111
+ """Burg method autoregressive coefficients.
112
+
113
+ Implements the standard Burg recursion with Levinson-Durbin update.
114
+ Mean-centres the signal before fitting.
115
+
116
+ Args:
117
+ x: 1-D signal array
118
+ order: AR model order
119
+
120
+ Returns:
121
+ Array of `order` AR coefficients [a1, a2, ..., ap]
122
+ """
123
+ x = np.asarray(x, dtype=np.float64)
124
+ x = x - x.mean()
125
+ N = len(x)
126
+ ef = x.copy()
127
+ eb = x.copy()
128
+ a = np.zeros(order)
129
+
130
+ for m in range(1, order + 1):
131
+ f = ef[m:].copy()
132
+ b = eb[m - 1: N - 1].copy()
133
+
134
+ denom = np.dot(f, f) + np.dot(b, b) + 1e-12
135
+ km = -2.0 * np.dot(f, b) / denom
136
+
137
+ # Levinson-Durbin update of AR polynomial
138
+ a_prev = a[:m - 1].copy()
139
+ for j in range(m - 1):
140
+ a[j] = a_prev[j] + km * a_prev[m - 2 - j]
141
+ a[m - 1] = km
142
+
143
+ # Update forward/backward prediction errors
144
+ ef[m:] = f + km * b
145
+ eb[m - 1: N - 1] = b + km * f
146
+
147
+ return a
148
+
149
+
150
+ def _entropy(x: np.ndarray) -> float:
151
+ """Normalised signal entropy via absolute-value probability distribution.
152
+
153
+ Args:
154
+ x: 1-D array
155
+
156
+ Returns:
157
+ Entropy value (>= 0)
158
+ """
159
+ total = np.abs(x).sum()
160
+ if total < 1e-12:
161
+ return 0.0
162
+ p = np.abs(x) / total
163
+ p = p[p > 0]
164
+ return float(-np.sum(p * np.log(p)))
165
+
166
+
167
+ def _bands_energy(fft_mag: np.ndarray) -> np.ndarray:
168
+ """Energy in each of the 14 UCI HAR frequency bands (1-indexed, inclusive).
169
+
170
+ Args:
171
+ fft_mag: FFT magnitude array, must contain at least 64 values
172
+
173
+ Returns:
174
+ Array of 14 energy values
175
+ """
176
+ m = fft_mag[:64]
177
+ return np.array([float(np.sum(m[s - 1: e] ** 2)) for s, e in BANDS])
178
+
179
+
180
+ def _safe_corr(a: np.ndarray, b: np.ndarray) -> float:
181
+ """Pearson correlation, returns 0.0 if either signal is constant.
182
+
183
+ Args:
184
+ a: first 1-D array
185
+ b: second 1-D array
186
+
187
+ Returns:
188
+ Correlation coefficient in [-1, 1]
189
+ """
190
+ if a.std() < 1e-10 or b.std() < 1e-10:
191
+ return 0.0
192
+ r = np.corrcoef(a, b)[0, 1]
193
+ return 0.0 if not np.isfinite(r) else float(r)
194
+
195
+
196
+ def _angle(u: np.ndarray, v: np.ndarray) -> float:
197
+ """Angle in radians between two 3-D vectors.
198
+
199
+ Args:
200
+ u: first vector, shape (3,)
201
+ v: second vector, shape (3,)
202
+
203
+ Returns:
204
+ Angle in radians, or 0.0 if either vector is zero
205
+ """
206
+ un, vn = np.linalg.norm(u), np.linalg.norm(v)
207
+ if un < 1e-10 or vn < 1e-10:
208
+ return 0.0
209
+ return float(np.arccos(np.clip(np.dot(u, v) / (un * vn), -1.0, 1.0)))
210
+
211
+
212
+ # ── Feature extractors ────────────────────────────────────────────────────────
213
+
214
+ def _t3ax(sig: np.ndarray) -> np.ndarray:
215
+ """40 time-domain features from a 3-axis signal (N, 3).
216
+
217
+ Order: meanΓ—3, stdΓ—3, madΓ—3, maxΓ—3, minΓ—3, sma,
218
+ energyΓ—3, iqrΓ—3, entropyΓ—3, arCoeffΓ—12, correlationΓ—3
219
+ """
220
+ N = len(sig)
221
+ x, y, z = sig[:, 0], sig[:, 1], sig[:, 2]
222
+ out = []
223
+
224
+ out += [x.mean(), y.mean(), z.mean()]
225
+ out += [x.std(), y.std(), z.std()]
226
+ out += [
227
+ float(np.median(np.abs(x - np.median(x)))),
228
+ float(np.median(np.abs(y - np.median(y)))),
229
+ float(np.median(np.abs(z - np.median(z)))),
230
+ ]
231
+ out += [x.max(), y.max(), z.max()]
232
+ out += [x.min(), y.min(), z.min()]
233
+ out += [float((np.abs(x) + np.abs(y) + np.abs(z)).sum() / N)] # sma
234
+ out += [float(np.sum(x ** 2) / N), float(np.sum(y ** 2) / N), float(np.sum(z ** 2) / N)]
235
+ out += [
236
+ float(np.percentile(x, 75) - np.percentile(x, 25)),
237
+ float(np.percentile(y, 75) - np.percentile(y, 25)),
238
+ float(np.percentile(z, 75) - np.percentile(z, 25)),
239
+ ]
240
+ out += [_entropy(x), _entropy(y), _entropy(z)]
241
+ out += _burg_ar(x).tolist()
242
+ out += _burg_ar(y).tolist()
243
+ out += _burg_ar(z).tolist()
244
+ out += [_safe_corr(x, y), _safe_corr(x, z), _safe_corr(y, z)]
245
+
246
+ return np.array(out, dtype=np.float64) # 40 values
247
+
248
+
249
+ def _tmag(sig: np.ndarray) -> np.ndarray:
250
+ """13 time-domain features from a 1-D magnitude signal.
251
+
252
+ Order: mean, std, mad, max, min, sma, energy, iqr, entropy, arCoeffΓ—4
253
+ """
254
+ N = len(sig)
255
+ out = [
256
+ float(sig.mean()),
257
+ float(sig.std()),
258
+ float(np.median(np.abs(sig - np.median(sig)))),
259
+ float(sig.max()),
260
+ float(sig.min()),
261
+ float(np.abs(sig).sum() / N), # sma (1-D)
262
+ float(np.sum(sig ** 2) / N), # energy
263
+ float(np.percentile(sig, 75) - np.percentile(sig, 25)),
264
+ _entropy(sig),
265
+ ]
266
+ out += _burg_ar(sig).tolist()
267
+ return np.array(out, dtype=np.float64) # 13 values
268
+
269
+
270
+ def _f3ax(sig: np.ndarray) -> np.ndarray:
271
+ """79 frequency-domain features from a 3-axis signal (N, 3).
272
+
273
+ Order: meanΓ—3, stdΓ—3, madΓ—3, maxΓ—3, minΓ—3, sma,
274
+ energyΓ—3, iqrΓ—3, entropyΓ—3,
275
+ maxIndsΓ—3, meanFreqΓ—3,
276
+ (skewness, kurtosis)Γ—3 interleaved,
277
+ bandsEnergyΓ—14 per axis (Γ—3 axes = 42)
278
+ """
279
+ x, y, z = sig[:, 0], sig[:, 1], sig[:, 2]
280
+
281
+ def _fft(s):
282
+ return np.abs(np.fft.rfft(s))[:64]
283
+
284
+ fx, fy, fz = _fft(x), _fft(y), _fft(z)
285
+ bins = np.arange(1, 65, dtype=np.float64) # 1-indexed bin numbers
286
+
287
+ def _mfreq(fm):
288
+ t = fm.sum()
289
+ return float(np.dot(bins[:len(fm)], fm) / t) if t > 1e-12 else 0.0
290
+
291
+ def _maxinds(fm):
292
+ return float(np.argmax(fm) + 1) # 1-indexed
293
+
294
+ out = []
295
+ out += [fx.mean(), fy.mean(), fz.mean()]
296
+ out += [fx.std(), fy.std(), fz.std()]
297
+ out += [
298
+ float(np.median(np.abs(fx - np.median(fx)))),
299
+ float(np.median(np.abs(fy - np.median(fy)))),
300
+ float(np.median(np.abs(fz - np.median(fz)))),
301
+ ]
302
+ out += [fx.max(), fy.max(), fz.max()]
303
+ out += [fx.min(), fy.min(), fz.min()]
304
+ n = len(fx)
305
+ out += [float((fx + fy + fz).sum() / n)] # sma of FFT mags
306
+ out += [float(np.sum(fx ** 2) / n), float(np.sum(fy ** 2) / n), float(np.sum(fz ** 2) / n)]
307
+ out += [
308
+ float(np.percentile(fx, 75) - np.percentile(fx, 25)),
309
+ float(np.percentile(fy, 75) - np.percentile(fy, 25)),
310
+ float(np.percentile(fz, 75) - np.percentile(fz, 25)),
311
+ ]
312
+ out += [_entropy(fx), _entropy(fy), _entropy(fz)]
313
+ out += [_maxinds(fx), _maxinds(fy), _maxinds(fz)]
314
+ out += [_mfreq(fx), _mfreq(fy), _mfreq(fz)]
315
+ # skewness/kurtosis interleaved per axis (skX,kurX, skY,kurY, skZ,kurZ)
316
+ out += [float(skew(fx)), float(sp_kurtosis(fx))]
317
+ out += [float(skew(fy)), float(sp_kurtosis(fy))]
318
+ out += [float(skew(fz)), float(sp_kurtosis(fz))]
319
+ out += _bands_energy(fx).tolist()
320
+ out += _bands_energy(fy).tolist()
321
+ out += _bands_energy(fz).tolist()
322
+
323
+ return np.array(out, dtype=np.float64) # 79 values
324
+
325
+
326
+ def _fmag(sig: np.ndarray) -> np.ndarray:
327
+ """13 frequency-domain features from a 1-D magnitude signal.
328
+
329
+ Order: mean, std, mad, max, min, sma, energy, iqr, entropy,
330
+ maxInds, meanFreq, skewness, kurtosis
331
+ """
332
+ fm = np.abs(np.fft.rfft(sig))[:64]
333
+ n = len(fm)
334
+ bins = np.arange(1, n + 1, dtype=np.float64)
335
+ total = fm.sum()
336
+ out = [
337
+ float(fm.mean()),
338
+ float(fm.std()),
339
+ float(np.median(np.abs(fm - np.median(fm)))),
340
+ float(fm.max()),
341
+ float(fm.min()),
342
+ float(np.abs(fm).sum() / n),
343
+ float(np.sum(fm ** 2) / n),
344
+ float(np.percentile(fm, 75) - np.percentile(fm, 25)),
345
+ _entropy(fm),
346
+ float(np.argmax(fm) + 1), # maxInds (1-indexed)
347
+ float(np.dot(bins, fm) / total) if total > 1e-12 else 0.0, # meanFreq
348
+ float(skew(fm)),
349
+ float(sp_kurtosis(fm)),
350
+ ]
351
+ return np.array(out, dtype=np.float64) # 13 values
352
+
353
+
354
+ def _window_features(
355
+ body_acc: np.ndarray,
356
+ grav_acc: np.ndarray,
357
+ body_jerk: np.ndarray,
358
+ gyro: np.ndarray,
359
+ gyro_jerk: np.ndarray,
360
+ ) -> np.ndarray:
361
+ """Extract all 561 features from one pre-processed window.
362
+
363
+ Args:
364
+ body_acc: body linear acceleration (128, 3) m/sΒ²
365
+ grav_acc: gravity component (128, 3) m/sΒ²
366
+ body_jerk: body jerk (127, 3) m/sΒ³
367
+ gyro: angular velocity (128, 3) rad/s
368
+ gyro_jerk: gyro jerk (127, 3) rad/sΒ²
369
+
370
+ Returns:
371
+ 1-D array of 561 features
372
+ """
373
+ # Magnitudes
374
+ ba_mag = np.linalg.norm(body_acc, axis=1)
375
+ ga_mag = np.linalg.norm(grav_acc, axis=1)
376
+ bj_mag = np.linalg.norm(body_jerk, axis=1)
377
+ gy_mag = np.linalg.norm(gyro, axis=1)
378
+ gj_mag = np.linalg.norm(gyro_jerk, axis=1)
379
+
380
+ parts = []
381
+
382
+ # Time 3-axis (5 Γ— 40 = 200)
383
+ for sig in [body_acc, grav_acc, body_jerk, gyro, gyro_jerk]:
384
+ parts.append(_t3ax(sig))
385
+
386
+ # Time magnitudes (5 Γ— 13 = 65)
387
+ for mag in [ba_mag, ga_mag, bj_mag, gy_mag, gj_mag]:
388
+ parts.append(_tmag(mag))
389
+
390
+ # Freq 3-axis (3 Γ— 79 = 237)
391
+ for sig in [body_acc, body_jerk, gyro]:
392
+ parts.append(_f3ax(sig))
393
+
394
+ # Freq magnitudes (4 Γ— 13 = 52)
395
+ for mag in [ba_mag, bj_mag, gy_mag, gj_mag]:
396
+ parts.append(_fmag(mag))
397
+
398
+ # Angle features (7)
399
+ ba_mean = body_acc.mean(axis=0)
400
+ ga_mean = grav_acc.mean(axis=0)
401
+ bj_mean = body_jerk.mean(axis=0)
402
+ gy_mean = gyro.mean(axis=0)
403
+ gj_mean = gyro_jerk.mean(axis=0)
404
+
405
+ parts.append(np.array([
406
+ _angle(ba_mean, ga_mean),
407
+ _angle(bj_mean, ga_mean),
408
+ _angle(gy_mean, ga_mean),
409
+ _angle(gj_mean, ga_mean),
410
+ _angle(np.array([1.0, 0.0, 0.0]), ga_mean),
411
+ _angle(np.array([0.0, 1.0, 0.0]), ga_mean),
412
+ _angle(np.array([0.0, 0.0, 1.0]), ga_mean),
413
+ ]))
414
+
415
+ result = np.concatenate(parts)
416
+ assert result.shape == (561,), f"Feature count error: got {result.shape[0]}, expected 561"
417
+ return result
418
+
419
+
420
+ # ── Public API ────────────────────────────────────────────────────────────────
421
+
422
+ def process_phyphox_files(
423
+ acc_file,
424
+ gyro_file,
425
+ ) -> tuple:
426
+ """Convert Phyphox CSV exports to (n_windows, 561) feature array.
427
+
428
+ Pipeline:
429
+ 1. Parse + validate both CSVs
430
+ 2. Interpolate onto common 50 Hz grid
431
+ 3. Segment: 128-sample windows, 64-sample hop (50% overlap)
432
+ 4. Per window: median filter β†’ 20 Hz Butterworth β†’ gravity separation
433
+ at 0.3 Hz β†’ jerk β†’ magnitudes β†’ 561 features
434
+
435
+ Args:
436
+ acc_file: file-like object β€” Phyphox Accelerometer CSV
437
+ (columns: Time (s), X (m/s^2), Y (m/s^2), Z (m/s^2))
438
+ gyro_file: file-like object β€” Phyphox Gyroscope CSV
439
+ (columns: Time (s), X (rad/s), Y (rad/s), Z (rad/s))
440
+
441
+ Returns:
442
+ Tuple of:
443
+ np.ndarray shape (n_windows, 561) οΏ½οΏ½ raw (un-normalised) features
444
+ list[str] β€” warning messages
445
+
446
+ Raises:
447
+ ValueError: invalid format, wrong columns, or < 3 s of data
448
+ """
449
+ warnings: list = []
450
+
451
+ acc_df = _parse_csv(acc_file, ACC_COLS)
452
+ gyro_df = _parse_csv(gyro_file, GYRO_COLS)
453
+
454
+ acc_t = acc_df["Time (s)"].values
455
+ acc_xyz = acc_df[["X (m/s^2)", "Y (m/s^2)", "Z (m/s^2)"]].values
456
+ gyro_t = gyro_df["Time (s)"].values
457
+ gyro_xyz = gyro_df[["X (rad/s)", "Y (rad/s)", "Z (rad/s)"]].values
458
+
459
+ # Common time window
460
+ t0 = max(acc_t[0], gyro_t[0])
461
+ t1 = min(acc_t[-1], gyro_t[-1])
462
+ duration = t1 - t0
463
+
464
+ if duration < 3.0:
465
+ raise ValueError(
466
+ f"Recording is {duration:.2f} s β€” minimum 3 seconds required. "
467
+ "Hold the phone still or walk for at least 3 seconds before exporting."
468
+ )
469
+
470
+ t_grid = np.arange(t0, t1, 1.0 / FS)
471
+ am = (acc_t >= t0) & (acc_t <= t1)
472
+ gm = (gyro_t >= t0) & (gyro_t <= t1)
473
+
474
+ acc_50 = np.column_stack(
475
+ [np.interp(t_grid, acc_t[am], acc_xyz[am, i]) for i in range(3)]
476
+ )
477
+ gyro_50 = np.column_stack(
478
+ [np.interp(t_grid, gyro_t[gm], gyro_xyz[gm, i]) for i in range(3)]
479
+ )
480
+
481
+ n = min(len(acc_50), len(gyro_50))
482
+ acc_50, gyro_50 = acc_50[:n], gyro_50[:n]
483
+
484
+ n_windows = max(0, (n - WINDOW) // STEP + 1)
485
+ if n_windows == 0:
486
+ raise ValueError(
487
+ f"Only {n} samples ({n / FS:.1f} s) after alignment β€” "
488
+ f"need at least {WINDOW} samples ({WINDOW / FS:.1f} s)."
489
+ )
490
+
491
+ if duration > 60:
492
+ warnings.append(f"Long recording ({duration:.0f} s) β€” {n_windows} windows extracted.")
493
+
494
+ all_features = []
495
+ dt = 1.0 / FS
496
+
497
+ for start in range(0, n - WINDOW + 1, STEP):
498
+ end = start + WINDOW
499
+ aw = acc_50[start:end] # (128, 3)
500
+ gw = gyro_50[start:end] # (128, 3)
501
+
502
+ # 1. Noise: median filter then 3rd-order Butterworth LP at 20 Hz
503
+ aw = _butter_lp(_median_filt(aw), cutoff=20.0)
504
+ gw = _butter_lp(_median_filt(gw), cutoff=20.0)
505
+
506
+ # 2. Gravity separation: LP at 0.3 Hz
507
+ grav = _butter_lp(aw, cutoff=0.3)
508
+ body = aw - grav
509
+
510
+ # 3. Jerk: finite difference β†’ (127, 3)
511
+ body_jerk = np.diff(body, axis=0) / dt
512
+ gyro_jerk = np.diff(gw, axis=0) / dt
513
+
514
+ all_features.append(_window_features(body, grav, body_jerk, gw, gyro_jerk))
515
+
516
+ return np.array(all_features), warnings
src/streamlit_app.py CHANGED
@@ -7,6 +7,7 @@ import pandas as pd
7
  _SRC_DIR = os.path.dirname(os.path.abspath(__file__))
8
  _REPO_ROOT = os.path.dirname(_SRC_DIR)
9
  _SAMPLES_PATH = os.path.join(_REPO_ROOT, "data", "samples.csv")
 
10
 
11
  # ── Constants ──────────────────────────────────────────────────────────────
12
 
@@ -193,36 +194,8 @@ with tab1:
193
  except FileNotFoundError:
194
  st.error("Sample data file not found. Add `data/samples.csv` to the repo.")
195
 
196
- # ── Tab 2: Phyphox upload (placeholder) ─────────────────────────────────────
197
 
198
  with tab2:
199
- st.subheader("Upload Phyphox sensor recording")
200
- st.markdown("""
201
- **How to record your own data:**
202
- 1. Install [Phyphox](https://phyphox.org/) on your phone
203
- 2. Open the **Acceleration (without g)** and **Gyroscope** experiments
204
- 3. Record at least 3 seconds of a single activity
205
- 4. Export as CSV and upload below
206
- """)
207
-
208
- uploaded_file = st.file_uploader(
209
- "Upload Phyphox CSV export",
210
- type=["csv"],
211
- help="Export from Phyphox as CSV β€” must contain accelerometer and gyroscope columns"
212
- )
213
-
214
- if uploaded_file is not None:
215
- st.info(
216
- "Phyphox pipeline coming soon. "
217
- "Feature extraction from raw sensor readings "
218
- "(filtering β†’ jerk β†’ FFT β†’ 561 features) is under development."
219
- )
220
- try:
221
- preview = pd.read_csv(uploaded_file)
222
- st.markdown("**File preview:**")
223
- st.dataframe(preview.head(10))
224
- st.caption(
225
- f"{len(preview)} rows Β· {len(preview.columns)} columns detected"
226
- )
227
- except Exception as e:
228
- st.error(f"Could not read file: {e}")
 
7
  _SRC_DIR = os.path.dirname(os.path.abspath(__file__))
8
  _REPO_ROOT = os.path.dirname(_SRC_DIR)
9
  _SAMPLES_PATH = os.path.join(_REPO_ROOT, "data", "samples.csv")
10
+ _NORM_PATH = os.path.join(_REPO_ROOT, "data", "norm_params.json")
11
 
12
  # ── Constants ──────────────────────────────────────────────────────────────
13
 
 
194
  except FileNotFoundError:
195
  st.error("Sample data file not found. Add `data/samples.csv` to the repo.")
196
 
197
+ # ── Tab 2: Phyphox upload ─────────────────────────────────────────────────────
198
 
199
  with tab2:
200
+ from phyphox_app_block import render_phyphox_tab
201
+ render_phyphox_tab(ffn_model, ffn_status, cnn_model, cnn_status, _NORM_PATH)