3
3
4
4
# builtin
5
5
import os
6
+ from time import strptime
6
7
7
8
# external
8
9
import numpy as np
26
27
set_trace = Tracer ()
27
28
28
29
30
+ def compare_data_frames (actual , expected , rtol = 1e-7 , atol = 0.0 ):
31
+ """Compares two data frames column by column for numerical
32
+ equivalence."""
33
+
34
+ # Make sure all columns are present.
35
+ assert sorted (list (expected .columns )) == sorted (list (actual .columns ))
36
+
37
+ for col in expected .columns :
38
+ testing .assert_allclose (actual [col ], expected [col ], rtol , atol )
39
+
40
+
29
41
def test_find_constant_speed ():
30
42
31
43
speed_array = np .loadtxt (os .path .join (os .path .dirname (__file__ ),
@@ -68,65 +80,6 @@ def test_interpolate():
68
80
69
81
70
82
class TestDFlowData ():
71
- """we need class that deals with d flow data and running the hbm command
72
- line program
73
-
74
- input
75
- -----
76
-
77
- mocap module file path (or handle): times series of markers, force plate
78
- stuff, analog measurements this is basically at 100 hz, but not exactly
79
-
80
- record module file path (or handle): variable sample rate of treadmill
81
- motions (or any other variables from dflow) and events
82
-
83
- mocap compensation file path: with no loads for compensation
84
-
85
- meta data file path (or handle)
86
-
87
- output
88
- ------
89
- pandas data frame with all measurements from both files at 100 hertz,
90
- missing values set at NA,
91
-
92
- time start from 0 for all?
93
-
94
- event times?
95
-
96
- actions
97
- -------
98
-
99
- look for metadata in directory of the files (search up the hierarchy?)
100
- parse meta data
101
- store meta data as a dictionary
102
-
103
- parse record file for event times
104
- load time series from record file into data frame
105
- interpolate data in record file at 100 hertz
106
-
107
- load time series from mocap file into data frame
108
- identify missing markers
109
-
110
- load the compensation file
111
- compute the compensatation for the forces
112
-
113
- generate hbm output with the mocap file
114
- load hbm outputs into data frames
115
- interpolate at 100 hertz
116
-
117
- join all off the data frames in to one data frame at 100 hz sample rate
118
- with the time stamp starting at zero as the index
119
-
120
- attributes
121
- ----------
122
-
123
- record module data path (or file handle)
124
- mocap module data path (or file handle)
125
- meta data file path (or file handle)
126
- hbmtest configuration details
127
-
128
-
129
- """
130
83
131
84
cortex_start_frame = 2375
132
85
cortex_sample_period = 0.01
@@ -149,6 +102,7 @@ class TestDFlowData():
149
102
'Channel2.Anlg' ]
150
103
dflow_hbm_labels = ['RKneeFlexion.Ang' ,
151
104
'RKneeFlexion.Mom' ,
105
+ 'RKneeFlexion.Pow' ,
152
106
'R_PectoralisMajorTH1' ,
153
107
'L_RectusFemoris' ]
154
108
mocap_labels_without_hbm = (['TimeStamp' , 'FrameNumber' ] +
@@ -160,15 +114,58 @@ class TestDFlowData():
160
114
path_to_record_data_file = 'example_record_tsv_file.txt'
161
115
path_to_meta_data_file = 'example_meta_data_file.yml'
162
116
163
- meta_data = {'date' : '2013-10-3' ,
164
- 'trial number' : 5 ,
165
- 'project' : 'projecta' ,
117
+ meta_data = {'trial' : {'id' : 5 ,
118
+ 'datetime' : strptime ('2013-10-03' , "%Y-%m-%d" )},
119
+ 'subject' : {'id' : 234 ,
120
+ 'age' : 28 ,
121
+ 'mass' : 70 ,
122
+ 'mass-units' : 'kilogram' },
123
+ 'study' : {'id' : 12 ,
124
+ 'name' : 'Human Locomotion Control Identification' ,
125
+ 'description' : 'Perturbations during walking and running.' },
126
+ 'files' : [path_to_mocap_data_file ,
127
+ path_to_record_data_file ],
166
128
'events' : {'A' : 'Zeroing' ,
167
129
'B' : 'Walking' ,
168
130
'C' : 'Relaxing' },
131
+ 'units' : {
132
+ '.*\.Pos[XYZ]$' : 'meters' ,
133
+ '^[LR]_.*' : 'newtons' ,
134
+ '.*\.Mom$' : 'newton-meters' ,
135
+ '.*\.Ang$' : 'degrees' ,
136
+ '.*\.Pow$' : 'watts'
137
+ },
138
+ 'analog-channel-names' : {
139
+ "Channel1.Anlg" : "F1Y1" ,
140
+ "Channel2.Anlg" : "F1Y2" ,
141
+ "Channel3.Anlg" : "F1Y3" ,
142
+ "Channel4.Anlg" : "F1X1" ,
143
+ "Channel5.Anlg" : "F1X2" ,
144
+ "Channel6.Anlg" : "F1Z1" ,
145
+ "Channel7.Anlg" : "F2Y1" ,
146
+ "Channel8.Anlg" : "F2Y2" ,
147
+ "Channel9.Anlg" : "F2Y3" ,
148
+ "Channel10.Anlg" : "F2X1" ,
149
+ "Channel11.Anlg" : "F2X2" ,
150
+ "Channel12.Anlg" : "F2Z1" ,
151
+ "Channel13.Anlg" : "Front_Left_EMG" ,
152
+ "Channel14.Anlg" : "Front_Left_AccX" ,
153
+ "Channel15.Anlg" : "Front_Left_AccY" ,
154
+ "Channel16.Anlg" : "Front_Left_AccZ" ,
155
+ "Channel17.Anlg" : "Back_Left_EMG" ,
156
+ "Channel18.Anlg" : "Back_Left_AccX" ,
157
+ "Channel19.Anlg" : "Back_Left_AccY" ,
158
+ "Channel20.Anlg" : "Back_Left_AccZ" ,
159
+ "Channel21.Anlg" : "Front_Right_EMG" ,
160
+ "Channel22.Anlg" : "Front_Right_AccX" ,
161
+ "Channel23.Anlg" : "Front_Right_AccY" ,
162
+ "Channel24.Anlg" : "Front_Right_AccZ" ,
163
+ "Channel25.Anlg" : "Back_Right_EMG" ,
164
+ "Channel26.Anlg" : "Back_Right_AccX" ,
165
+ "Channel27.Anlg" : "Back_Right_AccY" ,
166
+ "Channel28.Anlg" : "Back_Right_AccZ" ,
167
+ }
169
168
}
170
- # TODO : Add names for analog columns in DFlow because you can't name
171
- # analog signals uniquely in dflow
172
169
173
170
def create_sample_mocap_file (self ):
174
171
"""
@@ -199,8 +196,9 @@ def create_sample_mocap_file(self):
199
196
# This generates the slightly variable sampling periods seen in the
200
197
# time stamp column.
201
198
deviations = (self .dflow_mocap_max_period_deviation *
202
- np .random .choice ([- 1.0 , 1.0 ]) *
203
- np .random .random (self .cortex_number_of_samples ))
199
+ np .random .uniform (- 1.0 , 1.0 ,
200
+ self .cortex_number_of_samples ))
201
+
204
202
variable_periods = (self .cortex_sample_period *
205
203
np .ones (self .cortex_number_of_samples ) +
206
204
deviations )
@@ -262,19 +260,20 @@ def create_sample_record_file(self):
262
260
np .random .random (self .dflow_record_number_of_samples )
263
261
264
262
self .record_data_frame = pandas .DataFrame (record_data )
263
+ # TODO : Pandas 0.11.0 does not have a cols argument.
264
+ # http://pandas.pydata.org/pandas-docs/version/0.10.1/generated/pandas.Series.to_csv.html
265
265
self .record_data_frame .to_csv (self .path_to_record_data_file ,
266
266
sep = '\t ' , float_format = '%1.6f' ,
267
267
index = False , cols = ['Time' ,
268
268
'LeftBeltSpeed' ,
269
269
'RightBeltSpeed' ])
270
-
271
270
event_template = "#\n # EVENT {} - COUNT {}\n #\n "
272
271
273
272
time = self .record_data_frame ['Time' ]
274
273
275
- event_times = {'A' : np . random . choice ( time ) ,
276
- 'B' : np . random . choice ( time ) ,
277
- 'C' : np . random . choice ( time ) }
274
+ event_times = {'A' : time [ 333 ] ,
275
+ 'B' : time [ 784 ] ,
276
+ 'C' : time [ 955 ] }
278
277
279
278
# This loops through the record file and inserts the events.
280
279
new_lines = ''
@@ -445,9 +444,7 @@ def test_load_mocap_data(self):
445
444
dflow_data ._store_hbm_column_labels (dflow_data .mocap_column_labels )
446
445
raw_mocap_data = dflow_data ._load_mocap_data ()
447
446
448
- testing .assert_allclose (raw_mocap_data .sort (axis = 1 ).values ,
449
- self .mocap_data_frame .sort (axis = 1 ).values ,
450
- atol = 1e-6 )
447
+ compare_data_frames (raw_mocap_data , self .mocap_data_frame , atol = 1e-6 )
451
448
452
449
# TODO : Add some missing values into the HBM columns of
453
450
# self.mocap_data_frame and make sure they get replaced with NaN.
@@ -456,8 +453,7 @@ def test_load_mocap_data(self):
456
453
457
454
expected = self .mocap_data_frame [self .mocap_labels_without_hbm ]
458
455
459
- testing .assert_allclose (raw_mocap_data .sort (axis = 1 ).values ,
460
- expected .sort (axis = 1 ).values , atol = 1e-6 )
456
+ compare_data_frames (raw_mocap_data , expected , atol = 1e-6 )
461
457
462
458
def test_extract_events_from_record_file (self ):
463
459
pass
@@ -466,9 +462,8 @@ def test_load_record_data(self):
466
462
dflow_data = DFlowData (record_tsv_path = self .path_to_record_data_file )
467
463
raw_record_data = dflow_data ._load_record_data ()
468
464
469
- testing .assert_allclose (raw_record_data .sort (axis = 1 ).values ,
470
- self .record_data_frame .sort (axis = 1 ).values ,
471
- atol = 1e-6 )
465
+ compare_data_frames (raw_record_data , self .record_data_frame ,
466
+ atol = 1e-6 )
472
467
473
468
def test_resample_record_data (self ):
474
469
dflow_data = DFlowData (self .path_to_mocap_data_file ,
0 commit comments