%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%22marimo%3E%3D0.23.9%22%2C%20%22numpy%3E%3D2.0%22%2C%20%22pandas%3E%3D2.2%22%2C%20%22xarray%22%2C%20%22urllib3%3E%3D2.2%22%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.25.2%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Read%20Cortex%20Motion%20Analysis%20Corporation%20.trc%20file%0A%0A%20%20%20%20%3E%20Marcos%20Duarte%2C%20Renato%20Naville%20Watanabe%3Cbr%3E%0A%20%20%20%20%3E%20%5BLaboratory%20of%20Biomechanics%20and%20Motor%20Control%5D(https%3A%2F%2Fbmclab.pesquisa.ufabc.edu.br%2F)%3Cbr%3E%0A%20%20%20%20%3E%20Federal%20University%20of%20ABC%2C%20Brazil%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20How%20to%20use%20this%20guide%0A%0A%20%20%20%20A%20marker%20file%20opens%20successfully%2C%20but%20several%20trajectories%20are%20entirely%20empty.%20Should%20they%20be%20removed%2C%20preserved%20as%20NaN%2C%20or%20replaced%3F%0A%0A%20%20%20%20We%20will%20read%20the%20same%20TRC%20data%20as%20a%20flat%20table%2C%20a%20hierarchical%20table%2C%20and%20an%20xarray%20object%2C%20while%20checking%20units%20and%20empty-marker%20handling.%0A%0A%20%20%20%20Predict%20the%20result%20before%20running%20each%20example.%20Try%20the%20**Exercises**%20before%20opening%20their%20worked%20solutions.%20Finish%20with%20the%20**Checkpoint%20questions**%20and%20**Problems**%3B%20the%20latter%20provide%20final%20answers%20only.%0A%0A%20%20%20%20Open%20this%20file%20locally%20in%20marimo%2C%20on%20%5Bmarimo.app%5D(https%3A%2F%2Fmarimo.app%2F)%2C%20or%20in%20%5Bmolab%5D(https%3A%2F%2Fmolab.marimo.io%2Fnotebooks).%20Public%20%60walk1.trc%60%20and%20%60walk.trc%60%20files%20replace%20the%20unavailable%20arm-model%20files%20in%20the%20original%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Motion%20Analysis%20Corporation%20(MAC%2C%20https%3A%2F%2Fwww.motionanalysis.com%2F)%20builds%20motion%20capture%20systems%20and%20their%20software%20(e.g.%2C%20Cortex)%20generates%20files%20in%20ASCII%20and%20binary%20formats%20for%20the%20different%20signals%20(kinematics%2C%20analog%20data%2C%20force%20plate%20data%2C%20etc.).%20Here%20are%20functions%20for%20reading%20most%20of%20the%20files%20saved%20in%20ASCII%20format.%20These%20files%20have%20headers%20with%20few%20lines%20with%20meta%20data%20and%20the%20signals%20are%20stored%20in%20columns%20and%20the%20rows%20for%20the%20different%20frames%20(instants%20of%20time).%0A%0A%20%20%20%20The%20%22.trc%22%20(Track%20Row%20Column)%20file%20in%20ASCII%20contains%20X-Y-Z%20position%20data%20for%20the%20reflective%20markers%20from%20a%20motion%20capture%20trial.%20The%20position%20data%20for%20each%20marker%20is%20organized%20into%203%20columns%20per%20marker%20(X%2C%20Y%20and%20Z%20position)%20with%20each%20row%20being%20a%20new%20frame.%20The%20position%20data%20is%20relative%20to%20the%20global%20coordinate%20system%20of%20the%20capture%20volume%20and%20the%20position%20values%20are%20in%20the%20units%20used%20for%20calibration.%0A%0A%20%20%20%20The%20%22.anc%22%20(Analog%20ASCII%20Row%20Column)%20file%20contains%20ASCII%20analog%20data%20in%20row-column%20format.%20The%20data%20is%20derived%20from%20%22.anb%22%20analog%20binary%20files.%20These%20binary%20%22.anb%22%20files%20are%20generated%20simultaneously%20with%20video%20%22.vc%22%20files%20if%20an%20optional%20analog%20input%20board%20is%20used%20in%20conjunction%20with%20video%20data%20capture.%0A%0A%20%20%20%20The%20%22.cal%22%20file%20contains%20force%20plate%20calibration%20parameters.%0A%0A%20%20%20%20The%20%22.forces%22%20file%20contains%20force%20plate%20data.%20The%20data%20is%20saved%20based%20on%20the%20%22forcepla.cal%22%20file%20of%20the%20trial%20and%20converts%20the%20raw%20force%20plate%20data%20into%20calibrated%20forces.%20The%20units%20used%20are%20Newtons%20and%20Newton-meters%20and%20each%20line%20in%20the%20file%20equates%20to%20one%20analog%20sample.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20from%20notebook_resources%20import%20repo_file%0A%20%20%20%20except%20ModuleNotFoundError%20as%20_error%3A%0A%20%20%20%20%20%20%20%20if%20_error.name%20!%3D%20%22notebook_resources%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%0A%20%20%20%20%20%20%20%20raise%20ModuleNotFoundError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22This%20notebook%20needs%20notebook_resources.py.%20Download%20it%20from%3A%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22https%3A%2F%2Fraw.githubusercontent.com%2FBMClab%2FBMC%2Fmaster%2Ffunctions%2Fnotebook_resources.py%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Save%20it%20in%20the%20same%20folder%20as%20this%20notebook%2C%20then%20rerun%20this%20cell.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22notebook_resources%22%2C%0A%20%20%20%20%20%20%20%20)%20from%20None%0A%20%20%20%20return%20(repo_file%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Python%20setup%0A%0A%20%20%20%20The%20reader%20below%20is%20included%20in%20this%20notebook.%20Pandas%20labels%20the%20tables%20and%20xarray%20labels%20the%20time%2C%20marker%2C%20and%20component%20dimensions.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.function%0Adef%20read_trc(fname%2C%20fname2%3D''%2C%20units%3D''%2C%20dropna%3DFalse%2C%20na%3D0.0%2C%20fmt%3D'multi'%2C%20show_msg%3DTrue)%3A%0A%20%20%20%20%22%22%22Read%20.trc%20file%20format%20from%20Cortex%20MAC.%0A%0A%20%20%20%20This%20function%3A%201.%20Read%20trc%20file%3B%202.%20Can%20delete%20or%20replace%20markers%20(columns)%0A%20%20%20%20with%20empty%20data%3B%203.%20Correct%20number%20of%20markers%20in%20the%20header%20according%20to%0A%20%20%20%20the%20actual%20number%20of%20non-empty%20markers%3B%204.%20Can%20save%20a%20'.trc'%20file%20with%0A%20%20%20%20updated%20information%20and%20data%3B%205.%20Return%20header%20information%20(optional)%20and%0A%20%20%20%20data%20(marker%20position)%20as%20dataframe%20or%20dataarray.%0A%0A%20%20%20%20The%20.trc%20(track%20row%20column)%20file%20in%20ASCII%20contains%20X-Y-Z%20position%0A%20%20%20%20data%20for%20the%20reflective%20markers%20from%20a%20motion%20capture%20trial.%20The%0A%20%20%20%20position%20data%20for%20each%20marker%20is%20organized%20into%203%20columns%20per%20marker%0A%20%20%20%20(X%2C%20Y%20and%20Z%20position)%20with%20each%20row%20being%20a%20new%20frame.%20The%20position%0A%20%20%20%20data%20is%20relative%20to%20the%20global%20coordinate%20system%20of%20the%20capture%20volume%0A%20%20%20%20and%20the%20position%20values%20are%20in%20the%20units%20used%20for%20calibration.%0A%0A%20%20%20%20Parameters%0A%20%20%20%20----------%0A%20%20%20%20fname%20%3A%20string%0A%20%20%20%20%20%20%20%20Full%20file%20name%20of%20the%20.trc%20file%20to%20be%20opened.%0A%20%20%20%20fname2%20%3A%20string%20(default%20%3D%20'')%0A%20%20%20%20%20%20%20%20Full%20file%20name%20of%20the%20.trc%20file%20to%20be%20saved%20with%20updated%20information%0A%20%20%20%20%20%20%20%20and%20data%20if%20desired.%0A%20%20%20%20%20%20%20%20If%20fname2%20is%20''%2C%20no%20file%20is%20saved.%0A%20%20%20%20%20%20%20%20If%20fname2%20is%20'%3D'%2C%20the%20original%20file%20name%20will%20be%20used.%0A%20%20%20%20%20%20%20%20If%20fname2%20is%20a%20string%20with%20length%20between%201%20and%203%20(other%20than%20'%3D')%2C%0A%20%20%20%20%20%20%20%20e.g.%2C%20'_2'%2C%20this%20string%20is%20appended%20to%20the%20original%20file%20name.%0A%20%20%20%20units%20%3A%20string%20(default%20%3D%20'')%0A%20%20%20%20%20%20%20%20Change%20the%20units%20of%20the%20data%20if%20desired.%0A%20%20%20%20%20%20%20%20Accepted%20output%20units%20are%20'm'%20or%20'mm'.%0A%20%20%20%20dropna%20%3A%20bool%20(default%20%3D%20False)%0A%20%20%20%20%20%20%20%20True%3A%20Delete%20a%20marker%20only%20if%20all%20three%20XYZ%20columns%20contain%20no%20data.%0A%20%20%20%20%20%20%20%20Partially%20missing%20markers%20keep%20their%20complete%20XYZ%20group%20and%20NaNs.%0A%20%20%20%20%20%20%20%20False%3A%20preserve%20column%20and%20replace%20column%20values%20by%20parameter%20%60na%60%0A%20%20%20%20%20%20%20%20(see%20below)%20if%20inputed%2C%20otherwise%20maintain%20default%20pandas%20value%20(NaN).%0A%20%20%20%20na%20%3A%20float%20or%20None%20(default%20%3D%200.0)%0A%20%20%20%20%20%20%20%20Value%20to%20replace%20(if%20%60dropna%60%20is%20False)%20column%20values%20if%20this%20column%0A%20%20%20%20%20%20%20%20has%20only%20missing%20or%20NaN%20values.%20Input%20None%20to%20maintain%20default%20pandas%0A%20%20%20%20%20%20%20%20value%20for%20this%20case%20(NaN).%0A%20%20%20%20fmt%20%3A%20string%20(default%20%3D%20'multi')%0A%20%20%20%20%20%20%20%20Format%20of%20the%20output%3A%20'uni'%2C%20'multi'%2C%20'xarray'%0A%20%20%20%20%20%20%20%20'uni'%3A%20returns%20variable%20with%20trc%20header%20info%20plus%20pandas%0A%20%20%20%20%20%20%20%20dataframe%20with%20markerxyz%20as%20labels%20and%20%22Frame%23%22%20and%20%22Time%22%20as%20columns%3B%0A%20%20%20%20%20%20%20%20'multi'%3A%20returns%20variable%20with%20trc%20header%20info%20plus%20multilabel%20pandas%0A%20%20%20%20%20%20%20%20dataframe%20%20with%20%22Marker%22%2C%20%22Coordinate%22%20and%20%22XYZ%22%2C%20as%20labels%20and%20%22Time%22%0A%20%20%20%20%20%20%20%20as%20index%3B%0A%20%20%20%20%20%20%20%20'xarray'%3A%20returns%20variable%20as%20dataarray%20xarray%20and%20trc%20header%20info%20as%0A%20%20%20%20%20%20%20%20attributes%20of%20this%20dataarray.%0A%20%20%20%20show_msg%20%3A%20bool%20(default%20%3D%20True)%0A%20%20%20%20%20%20%20%20Whether%20to%20print%20messages%20about%20the%20execution%20of%20the%20intermediary%20steps%0A%20%20%20%20%20%20%20%20(True)%20or%20not%20(False).%0A%0A%20%20%20%20Returns%0A%20%20%20%20-------%0A%20%20%20%20h%20%3A%20Python%20dictionary%20with%20.trc%20header%20info%20(if%20%60fmt%60%20%3D%20'uni'%20or%20'multi')%0A%20%20%20%20%20%20%20%20keys%3A%20header%20(the%20.trc%20full%20header)%2C%20data_rate%20(Hz)%2C%20camera_rate%20(Hz)%2C%0A%20%20%20%20%20%20%20%20nframes%2C%20nmarkers%2C%20markers%20(names)%2C%20xyz%20(X1%2CY1%2CZ1...)%2C%20units.%0A%20%20%20%20data%20%3A%20pandas%20dataframe%20or%20xarray%20dataarray%0A%20%20%20%20%20%20%20%20Three%20possible%20output%20formats%20according%20to%20the%20%60fmt%60%20option%3A%0A%20%20%20%20%20%20%20%20'uni'%3A%20dataframe%20with%20shape%20(nframes%2C%202%2B3*nmarkers)%20with%20markerxyz%20as%0A%20%20%20%20%20%20%20%20labels%20and%20columns%3A%20Frame%23%2C%20time%20and%20position%20data%2C%20or%0A%20%20%20%20%20%20%20%20'multi'%3A%20fataframe%20with%20shape%20(nframes%2C%203*nmarkers)%20with%20%22Marker%22%2C%0A%20%20%20%20%20%20%20%20%22Coordinate%22%20and%20%22XYZ%22%20as%20labels%2C%20%22Time%22%20as%20index%2C%20and%20data%20position%0A%20%20%20%20%20%20%20%20as%20columns%2C%20or%0A%20%20%20%20%20%20%20%20'xarray'%3A%20dataarray%20with%20dims%3D%5B'time'%2C%20'marker'%2C%20'component'%5D%20and%20trc%0A%20%20%20%20%20%20%20%20header%20info%20as%20attributes%20of%20this%20dataarray.%0A%0A%20%20%20%20Examples%0A%20%20%20%20--------%0A%0A%20%20%20%20Notes%0A%20%20%20%20-----%0A%0A%20%20%20%20%22%22%22%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20csv%0A%20%20%20%20import%20os%0A%20%20%20%20with%20open(file%3Dfname%2C%20mode%3D'rt'%2C%20encoding%3D'utf-8'%2C%20newline%3D'')%20as%20f%3A%0A%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'Opening%20file%20%22%7Bfname%7D%22%20...%20'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20read%20%3D%20csv.reader(f%2C%20delimiter%3D'%5Ct')%0A%20%20%20%20%20%20%20%20header%20%3D%20%5Bnext(read)%20for%20x%20in%20range(5)%5D%0A%20%20%20%20%20%20%20%20nmarkers%20%3D%20int((len(header%5B3%5D)%20-%202)%20%2F%203)%0A%20%20%20%20%20%20%20%20markers%20%3D%20np.asarray(header%5B3%5D)%5Bnp.arange(2%2C%202%20%2B%203%20*%20nmarkers%2C%203)%5D.tolist()%0A%20%20%20%20%20%20%20%20if%20len(markers)%20!%3D%20len(set(markers))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20from%20collections%20import%20Counter%0A%20%20%20%20%20%20%20%20%20%20%20%20d%20%3D%20%7Ba%3A%20%5B''%5D%20%2B%20list(range(2%2C%20b%20%2B%201))%20if%20b%20%3E%201%20else%20''%20for%20a%2C%20b%20in%20Counter(markers).items()%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20markers%20%3D%20%5Bi%20%2B%20str(d%5Bi%5D.pop(0))%20if%20len(d%5Bi%5D)%20else%20i%20for%20i%20in%20markers%5D%0A%20%20%20%20%20%20%20%20markers3%20%3D%20%5Bm%20for%20m%20in%20markers%20for%20i%20in%20range(3)%5D%0A%20%20%20%20%20%20%20%20XYZ%20%3D%20%5Bi%5B0%5D%20for%20i%20in%20header%5B4%5D%5B2%3A5%5D%5D%0A%20%20%20%20%20%20%20%20xyz%20%3D%20%5Bi%5B0%5D.lower()%20for%20i%20in%20header%5B4%5D%5B2%3A5%5D%5D%0A%20%20%20%20%20%20%20%20markersxyz%20%3D%20%5Ba%20%2B%20b%20for%20a%2C%20b%20in%20zip(markers3%2C%20xyz%20*%20nmarkers)%5D%0A%20%20%20%20%20%20%20%20df%20%3D%20pd.read_csv(f%2C%20sep%3D'%5Ct'%2C%20names%3D%5B'Frame%23'%2C%20'Time'%5D%20%2B%20markersxyz%2C%20index_col%3DFalse%2C%20encoding%3D'utf-8'%2C%20engine%3D'c'%2C%20skip_blank_lines%3DTrue)%0A%20%20%20%20%20%20%20%20if%20dropna%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20keep%20%3D%20%5B0%2C%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20start%20in%20range(2%2C%20df.shape%5B1%5D%2C%203)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20df.iloc%5B%3A%2C%20start%3Astart%20%2B%203%5D.isna().to_numpy().all()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20keep.extend(range(start%2C%20start%20%2B%203))%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20df.iloc%5B%3A%2C%20keep%5D.copy()%0A%20%20%20%20%20%20%20%20elif%20na%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20col%20in%20df%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20df.loc%5B%3A%2C%20col%5D.isnull().sum()%20%3D%3D%20df.shape%5B0%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df.loc%5B%3A%2C%20col%5D%20%3D%20na%0A%20%20%20%20%20%20%20%20nmarkers%20%3D%20int((df.shape%5B1%5D%20-%202)%20%2F%203)%0A%20%20%20%20%20%20%20%20if%20header%5B2%5D%5B3%5D%20!%3D%20str(nmarkers)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20Number%20of%20markers%20changed%20from%20%7Bheader%5B2%5D%5B3%5D%7D%20to%20%7Bnmarkers%7D.')%0A%20%20%20%20%20%20%20%20%20%20%20%20header%5B2%5D%5B3%5D%20%3D%20str(nmarkers)%0A%20%20%20%20%20%20%20%20header%5B3%5D%20%3D%20%5B''%20if%20c%5B-1%5D%20in%20%5B'y'%2C%20'z'%5D%20else%20c%5B%3A-1%5D%20if%20c%5B-1%5D%20in%20%5B'x'%5D%20else%20c%20for%20c%20in%20df.columns.values.tolist()%5D%20%2B%20%5B''%5D%0A%20%20%20%20%20%20%20%20markers%20%3D%20np.asarray(header%5B3%5D)%5Bnp.arange(2%2C%202%20%2B%203%20*%20nmarkers%2C%203)%5D.tolist()%0A%20%20%20%20%20%20%20%20n3%20%3D%20np.repeat(range(1%2C%20nmarkers%20%2B%201)%2C%203).tolist()%0A%20%20%20%20%20%20%20%20XYZs%20%3D%20%5Ba%20%2B%20str(b)%20for%20a%2C%20b%20in%20zip(XYZ%20*%20nmarkers%2C%20n3)%5D%0A%20%20%20%20%20%20%20%20header%5B4%5D%20%3D%20%5B''%2C%20''%5D%20%2B%20XYZs%0A%20%20%20%20%20%20%20%20if%20units%20in%20(%22m%22%2C%20%22mm%22)%20and%20units%20!%3D%20header%5B2%5D%5B4%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20df.astype(%7Bcolumn%3A%20float%20for%20column%20in%20df.columns%5B2%3A%5D%7D)%0A%20%20%20%20%20%20%20%20if%20units%20%3D%3D%20'm'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20header%5B2%5D%5B4%5D%20%3D%3D%20'mm'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df.iloc%5B%3A%2C%202%3A%5D%20%3D%20df.iloc%5B%3A%2C%202%3A%5D%20%2F%201000%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20header%5B2%5D%5B4%5D%20%3D%20'm'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print('%20Units%20changed%20from%20%7B%7D%20to%20%7B%7D'.format('%22mm%22'%2C%20'%22m%22'))%0A%20%20%20%20%20%20%20%20elif%20units%20%3D%3D%20'mm'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20header%5B2%5D%5B4%5D%20%3D%3D%20'm'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df.iloc%5B%3A%2C%202%3A%5D%20%3D%20df.iloc%5B%3A%2C%202%3A%5D%20*%201000%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20header%5B2%5D%5B4%5D%20%3D%20'mm'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print('%20Units%20changed%20from%20%7B%7D%20to%20%7B%7D'.format('%22m%22'%2C%20'%22mm%22'))%0A%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print('done.')%0A%20%20%20%20if%20fname2%3A%0A%20%20%20%20%20%20%20%20if%20fname2%20%3D%3D%20'%3D'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20fname2%20%3D%20fname%0A%20%20%20%20%20%20%20%20elif%20len(fname2)%20%3C%3D%203%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20name%2C%20extension%20%3D%20os.path.splitext(fname)%0A%20%20%20%20%20%20%20%20%20%20%20%20fname2%20%3D%20name%20%2B%20fname2%20%2B%20extension%0A%20%20%20%20%20%20%20%20write_trc(fname2%2C%20header%2C%20df%2C%20show_msg)%0A%20%20%20%20h%20%3D%20%7B'header'%3A%20header%2C%20'data_rate'%3A%20float(header%5B2%5D%5B0%5D)%2C%20'camera_rate'%3A%20float(header%5B2%5D%5B1%5D)%2C%20'nframes'%3A%20int(header%5B2%5D%5B2%5D)%2C%20'nmarkers'%3A%20int(header%5B2%5D%5B3%5D)%2C%20'markers'%3A%20markers%2C%20'xyz'%3A%20XYZs%2C%20'units'%3A%20header%5B2%5D%5B4%5D%2C%20'fname'%3A%20fname%2C%20'fname2'%3A%20fname2%7D%0A%20%20%20%20if%20fmt.lower()%20%3D%3D%20'uni'%3A%0A%20%20%20%20%20%20%20%20return%20(h%2C%20df)%0A%20%20%20%20elif%20fmt.lower()%20%3D%3D%20'multi'%3A%0A%20%20%20%20%20%20%20%20df.drop(labels%3D'Frame%23'%2C%20axis%3D1%2C%20inplace%3DTrue)%0A%20%20%20%20%20%20%20%20df.set_index('Time'%2C%20inplace%3DTrue)%0A%20%20%20%20%20%20%20%20df.index.name%20%3D%20'Time'%0A%20%20%20%20%20%20%20%20cols%20%3D%20%5Bs%5B%3A-1%5D%20for%20s%20in%20df.columns.str.replace('.'%2C%20'_'%2C%20regex%3DFalse)%5D%0A%20%20%20%20%20%20%20%20df.columns%20%3D%20%5Bcols%2C%20XYZ%20*%20int(df.shape%5B1%5D%20%2F%203)%2C%20XYZs%5D%0A%20%20%20%20%20%20%20%20df.columns.set_names(names%3D%5B'Marker'%2C%20'Coordinate'%2C%20'XYZ'%5D%2C%20level%3D%5B0%2C%201%2C%202%5D%2C%20inplace%3DTrue)%0A%20%20%20%20%20%20%20%20return%20(h%2C%20df)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20import%20xarray%20as%20xr%0A%20%20%20%20%20%20%20%20name%20%3D%20'Position%20data'%0A%20%20%20%20%20%20%20%20dims%20%3D%20%5B'time'%2C%20'marker'%2C%20'component'%5D%0A%20%20%20%20%20%20%20%20time%20%3D%20df.values%5B%3A%2C%201%5D%0A%20%20%20%20%20%20%20%20component%20%3D%20XYZ%0A%20%20%20%20%20%20%20%20coords%20%3D%20%5Btime%2C%20markers%2C%20component%5D%0A%20%20%20%20%20%20%20%20da%20%3D%20xr.DataArray(data%3Ddf.values%5B%3A%2C%202%3A%5D.reshape(-1%2C%20nmarkers%2C%203%2C%20order%3D'C')%2C%20dims%3Ddims%2C%20coords%3Dcoords%2C%20name%3Dname%2C%20attrs%3Dh)%0A%20%20%20%20%20%20%20%20da%5B'time'%5D.attrs%5B'units'%5D%20%3D%20's'%0A%20%20%20%20%20%20%20%20da%5B'component'%5D.attrs%5B'units'%5D%20%3D%20h%5B'units'%5D%0A%20%20%20%20%20%20%20%20return%20da%0A%0A%0A%40app.function%0Adef%20write_trc(fname%2C%20header%2C%20df%2C%20show_msg%3DTrue)%3A%0A%20%20%20%20%22%22%22Write%20.trc%20file%20format%20from%20Cortex%20MAC.%0A%0A%20%20%20%20See%20the%20read_forces.py%20function.%0A%0A%20%20%20%20Parameters%0A%20%20%20%20----------%0A%20%20%20%20fname%20%3A%20string%0A%20%20%20%20%20%20%20%20Full%20file%20name%20of%20the%20.trc%20file%20to%20be%20saved.%0A%20%20%20%20header%20%3A%20list%20of%20lists%0A%20%20%20%20%20%20%20%20header%20for%20the%20.trc%20file%0A%20%20%20%20df%20%3A%20pandas%20dataframe%0A%20%20%20%20%20%20%20%20dataframe%20with%20data%20for%20the%20.trc%20file%20(with%20frame%20and%20time%20columns)%0A%20%20%20%20show_msg%20%3A%20bool%20(default%20%3D%20True)%0A%20%20%20%20%20%20%20%20Whether%20to%20print%20messages%20about%20the%20execution%20of%20the%20intermediary%20steps%0A%20%20%20%20%20%20%20%20(True)%20or%20not%20(False).%0A%20%20%20%20%22%22%22%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20csv%0A%20%20%20%20import%20os%0A%20%20%20%20with%20open(file%3Dfname%2C%20mode%3D'wt'%2C%20encoding%3D'utf-8'%2C%20newline%3D'')%20as%20f%3A%0A%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'Saving%20file%20%22%7Bfname%7D%22%20...%20'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20f.writelines(('%5Ct'.join(line)%20%2B%20'%5Cn'%20for%20line%20in%20header))%0A%20%20%20%20%20%20%20%20f.write('%5Cn')%0A%20%20%20%20%20%20%20%20data%20%3D%20df.to_csv(header%3DNone%2C%20index%3DNone%2C%20sep%3D'%5Ct'%2C%20lineterminator%3D'%5Cn'%2C%20float_format%3D'%25.6f')%0A%20%20%20%20%20%20%20%20f.write(data.replace('%5Cn'%2C%20'%5Ct%5Cn'))%0A%20%20%20%20%20%20%20%20if%20show_msg%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print('done.')%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20print(read_trc.__doc__)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(repo_file)%3A%0A%20%20%20%20fname%20%3D%20str(repo_file(%22data%2Fwalk1.trc%22))%0A%20%20%20%20return%20(fname%2C)%0A%0A%0A%40app.cell%0Adef%20_(fname)%3A%0A%20%20%20%20(h%2C%20_df)%20%3D%20read_trc(fname%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'uni')%0A%20%20%20%20_df.head()%0A%20%20%20%20return%20(h%2C)%0A%0A%0A%40app.cell%0Adef%20_(h)%3A%0A%20%20%20%20h%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20**Exercise%201.**%20A%20TRC%20file%20declares%2028%20markers.%20How%20many%20numeric%20columns%20does%20a%20flat%20table%20have%20when%20frame%20number%20and%20time%20are%20included%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%2F%2F%2F%20details%20%7C%20Solution%20to%20Exercise%201%0A%0A%20%20%20%20There%20are%20%2428%5Ctimes3%2B2%3D86%24%20columns.%20A%20table%20indexed%20by%20time%20and%20containing%20only%20marker%20coordinates%20has%2084%20data%20columns.%0A%20%20%20%20%2F%2F%2F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fname)%3A%0A%20%20%20%20(h_1%2C%20_df)%20%3D%20read_trc(fname%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'multi')%0A%20%20%20%20_df.head()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fname)%3A%0A%20%20%20%20_da%20%3D%20read_trc(fname%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'xarray')%0A%20%20%20%20_da%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(repo_file)%3A%0A%20%20%20%20fname_1%20%3D%20str(repo_file(%22data%2Fwalk.trc%22))%0A%20%20%20%20return%20(fname_1%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Empty%20markers%20are%20a%20data%20decision%0A%0A%20%20%20%20The%20second%20file%20contains%20extra%20empty%20trajectories.%20%60dropna%3DTrue%60%20removes%20a%20marker%20only%20when%20all%20three%20XYZ%20columns%20are%20empty.%20Partially%20missing%20XYZ%20groups%20stay%20intact.%20%60dropna%3DFalse%2C%20na%3DNone%60%20preserves%20NaNs%2C%20while%20%60na%3D0%60%20fills%20entirely%20empty%20columns%20with%20zeros.%20A%20filled%20zero%20is%20not%20a%20newly%20measured%20position%3B%20partial%20gaps%20remain%20NaN.%20Keep%20this%20distinction%20when%20plotting%20or%20fitting%20a%20model.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20**Exercise%202.**%20Which%20option%20best%20preserves%20an%20entirely%20missing%20trajectory%20as%20missing%20data%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%2F%2F%2F%20details%20%7C%20Solution%20to%20Exercise%202%0A%0A%20%20%20%20Use%20%60dropna%3DFalse%2C%20na%3DNone%60.%20Dropping%20removes%20the%20trajectory%3B%20%60na%3D0%60%20assigns%20a%20numerical%20coordinate%20that%20can%20be%20confused%20with%20a%20real%20measurement.%0A%20%20%20%20%2F%2F%2F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fname_1)%3A%0A%20%20%20%20(h_2%2C%20_df)%20%3D%20read_trc(fname_1%2C%20fname2%3D''%2C%20dropna%3DFalse%2C%20na%3DNone%2C%20fmt%3D'multi')%0A%20%20%20%20_df.head()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fname_1)%3A%0A%20%20%20%20(h_3%2C%20_df)%20%3D%20read_trc(fname_1%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'multi')%0A%20%20%20%20_df.head()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fname_1)%3A%0A%20%20%20%20_da%20%3D%20read_trc(fname_1%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'xarray')%0A%20%20%20%20_da%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(repo_file)%3A%0A%20%20%20%20(h_4%2C%20_data)%20%3D%20read_trc(str(repo_file('data%2Fwalk.trc'))%2C%20fname2%3D''%2C%20dropna%3DFalse%2C%20na%3D0.0%2C%20fmt%3D'uni')%0A%20%20%20%20_data%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(repo_file)%3A%0A%20%20%20%20(h_5%2C%20_data)%20%3D%20read_trc(str(repo_file('data%2Fwalk.trc'))%2C%20fname2%3D''%2C%20dropna%3DFalse%2C%20na%3D0.0%2C%20fmt%3D'multi')%0A%20%20%20%20_data%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(repo_file)%3A%0A%20%20%20%20(h_6%2C%20_data)%20%3D%20read_trc(str(repo_file('data%2Fwalk.trc'))%2C%20fname2%3D''%2C%20dropna%3DTrue%2C%20na%3D0.0%2C%20fmt%3D'multi')%0A%20%20%20%20_data%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Checkpoint%20questions%0A%0A%20%20%20%201.%20Does%20reading%20a%20file%20confirm%20that%20its%20header%20is%20correct%3F%0A%0A%20%20%20%202.%20What%20is%20the%20advantage%20of%20named%20xarray%20dimensions%3F%0A%0A%20%20%20%203.%20Does%20this%20guide%20overwrite%20the%20source%20file%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%2F%2F%2F%20details%20%7C%20Checkpoint%20answers%0A%0A%20%20%20%201.%20No%3B%20compare%20counts%2C%20units%2C%20and%20time%20intervals%20with%20the%20data.%0A%20%20%20%202.%20Selections%20can%20refer%20explicitly%20to%20time%2C%20marker%2C%20and%20component.%0A%20%20%20%203.%20No%3B%20all%20example%20calls%20use%20%60fname2%3D%22%5C%22%60.%0A%20%20%20%20%2F%2F%2F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Problems%0A%0A%20%20%20%20Try%20these%20independently%3B%20open%20a%20panel%20only%20to%20check%20your%20final%20answer.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20**Problem%201.**%20Convert%20a%20marker%20coordinate%20of%201%2C250%20mm%20into%20metres.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%2F%2F%2F%20details%20%7C%20Answer%20to%20Problem%201%0A%0A%20%20%20%201.25%20m.%0A%20%20%20%20%2F%2F%2F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20**Problem%202.**%20How%20many%20coordinates%20per%20frame%20are%20stored%20for%2012%20markers%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%2F%2F%2F%20details%20%7C%20Answer%20to%20Problem%202%0A%0A%20%20%20%2036.%0A%20%20%20%20%2F%2F%2F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Go%20deeper%0A%0A%20%20%20%20Compare%20outputs%20of%20%60fmt%3D%22uni%22%60%2C%20%60fmt%3D%22multi%22%60%2C%20and%20%60fmt%3D%22xarray%22%60%20without%20changing%20the%20recording.%0A%0A%20%20%20%20%23%23%23%20Video%20lectures%20on%20the%20Internet%0A%0A%20%20%20%20%5BOpenSim%20video%20tutorials%5D(https%3A%2F%2Fwww.youtube.com%2F%40OpenSimVideos)%20show%20how%20motion-capture%20files%20enter%20biomechanics%20workflows.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20References%0A%0A%20%20%20%20-%20%5BOpenSim%3A%20marker%20data%20in%20TRC%20files%5D(https%3A%2F%2Fopensimconfluence.atlassian.net%2Fwiki%2Fspaces%2FOpenSim%2Fpages%2F53089972%2FMarker%2B.trc%2BFiles).%0A%0A%20%20%20%20-%20%5Bxarray%20data%20structures%5D(https%3A%2F%2Fdocs.xarray.dev%2Fen%2Fstable%2Fuser-guide%2Fdata-structures.html).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
87a3e49e42caa10ad69986e43f412116