%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.9%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22detecta%22%2C%0A%23%20%20%20%20%20%22marimo%22%2C%0A%23%20%20%20%20%20%22matplotlib%22%2C%0A%23%20%20%20%20%20%22numpy%22%2C%0A%23%20%20%20%20%20%22pyversions%22%2C%0A%23%20%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(width%3D%22medium%22)%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%20detect_onset.py%0A%0A%20%20%20%20A%20function%20from%20%5Bdetecta%5D(https%3A%2F%2Fpypi.org%2Fproject%2Fdetecta%2F)%20-%20Python%20module%20to%20detect%20events%20in%20data.%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%20One%20of%20the%20simplest%20methods%20to%20automatically%20detect%20or%20identify%20the%20change%20or%20occurrence%20of%20a%20particular%20event%20in%20the%20data%2C%20for%20example%2C%20its%20beginning%20and%20ending%2C%20or%20simply%20the%20data%20onset%2C%20is%20based%20on%20amplitude%20threshold%2C%20where%20the%20signal%20is%20considered%20to%20be%20'on'%20when%20it%20is%20above%20a%20certain%20threshold.%20This%20threshold%20can%20be%20proportional%20to%20the%20amplitude%20of%20the%20baseline%20(the%20part%20of%20the%20data%20that%20we%20know%20there%20is%20no%20real%20signal%2C%20only%20noise).%0A%0A%20%20%20%20For%20instance%2C%20a%20threshold%20equals%20to%20two%20or%20three%20times%20the%20standard%20deviation%20of%20the%20baseline%20is%20a%20common%20procedure%20employed%20in%20the%20analysis%20of%20electromyographic%20data.%20Other%20way%20to%20set%20the%20threshold%20would%20be%20as%20a%20percentage%20value%20of%20the%20maximum%20or%20peak%20of%20the%20data.%20For%20instance%2C%20in%20movement%20analysis%20it's%20common%20to%20define%20the%20onset%20period%20as%20the%20signal%20above%205%25%20of%20the%20peak%20velocity%20of%20the%20investigated%20movement.%0A%0A%20%20%20%20The%20function%20%60detect_onset.py%60%20(%5Bsource%20code%5D(https%3A%2F%2Fgithub.com%2Fdemotu%2Fdetecta%2Fblob%2Fmaster%2Fdetecta%2Fdetect_onset.py))%20implements%20such%20onset%20detection%20based%20on%20the%20amplitude-threshold%20method%20with%20a%20parameter%20to%20specify%20a%20minimum%20number%20of%20samples%20above%20threshold%20to%20detect%20as%20onset%2C%20other%20parameter%20to%20specify%20the%20minimum%20number%20of%20samples%20(continuous%20or%20not)%20below%20threshold%20that%20will%20be%20ignored%20in%20the%20detection%20of%20data%20greater%20or%20equal%20to%20threshold%20(to%20avoid%20the%20detection%20of%20spikes%20or%20transients%20in%20the%20data)%2C%20and%20a%20second%20threshold%20parameter%20to%20specify%20the%20minimum%20amplitude%20that%20a%20minimum%20number%20of%20samples%20should%20have%20(to%20avoid%20the%20detection%20of%20baseline%20fluctuations%20that%20are%20above%20the%20first%20threshold%20but%20are%20not%20actual%20signals).%0A%0A%20%20%20%20%60detect_onset.py%60%20signature%20is%3A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20inds%20%3D%20detect_onset(x%2C%20threshold%3D0%2C%20n_above%3D1%2C%20n_below%3D0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20threshold2%3DNone%2C%20n_above2%3D1%2C%20show%3DFalse%2C%20ax%3DNone)%0A%20%20%20%20%60%60%60%0A%20%20%20%20Let's%20see%20how%20%60detect_onset.py%60%20works%3B%20first%20let's%20import%20the%20necessary%20Python%20libraries%20and%20configure%20the%20environment.%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%20Installation%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20pip%20install%20%22detecta%5Bplot%5D%22%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Or%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20conda%20install%20-c%20duartexyz%20detecta%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20To%20run%20this%20%5Bmarimo%5D(https%3A%2F%2Fmarimo.io)%20notebook%20in%20an%20isolated%20environment%20with%20all%20its%20dependencies%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20uvx%20marimo%20edit%20--sandbox%20detect_onset.py%0A%20%20%20%20%60%60%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20timeit%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pyversions%0A%0A%20%20%20%20from%20detecta%20import%20detect_onset%0A%0A%20%20%20%20return%20detect_onset%2C%20mo%2C%20np%2C%20pyversions%2C%20timeit%0A%0A%0A%40app.cell%0Adef%20_(pyversions)%3A%0A%20%20%20%20_info%20%3D%20pyversions.versions()%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%20Examples%0A%0A%20%20%20%20Let's%20run%20the%20function%20examples%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20detect_onset(_x%2C%20np.std(_x%5B%3A50%5D)%2C%20n_above%3D10%2C%20n_below%3D0%2C%20show%3DTrue)%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%20Add%20some%20noise%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20np.std(_x%5B%3A50%5D)%2C%20n_above%3D10%2C%20n_below%3D0%2C%20show%3DTrue)%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%20We%20will%20use%20the%20%60n_below%60%20parameter%20to%20not%20detect%20the%20noise%20from%20former%20example%20as%20onsets%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20np.std(_x%5B%3A50%5D)%2C%20n_above%3D10%2C%20n_below%3D2%2C%20show%3DTrue)%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%60detect_onset%60%20works%20with%20missing%20values%20(NaNs)%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_x%20%3D%20%5B0%2C%200%2C%202%2C%200%2C%20np.nan%2C%200%2C%202%2C%203%2C%203%2C%200%2C%201%2C%201%2C%200%5D%0A%20%20%20%20detect_onset(_x%2C%20threshold%3D1%2C%20n_above%3D1%2C%20n_below%3D0%2C%20show%3DTrue)%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%20Consider%20the%20case%20where%20there%20is%20a%20false%20onset%20because%20some%20data%20in%20the%20baseline%20are%20greater%20than%20the%20%60threshold%60%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B11%3A41%5D%20%3D%20np.ones(30)*.3%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20.1%2C%20n_above%3D10%2C%20n_below%3D1%2C%20show%3DTrue)%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%20We%20can't%20increase%20the%20value%20of%20the%20%60threshold%60%20because%20this%20would%20delay%20the%20detection%20of%20the%20actual%20onset%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B11%3A41%5D%20%3D%20np.ones(30)*.3%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20.4%2C%20n_above%3D10%2C%20n_below%3D1%2C%20show%3DTrue)%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%20For%20this%20situation%20we%20can%20use%20the%20second%20threshold%20parameter%2C%20%60threshold2%60%20with%20corresponding%20%60n_above2%60%20parameter%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(200)%2F10%0A%20%20%20%20_x%5B11%3A41%5D%20%3D%20np.ones(30)*.3%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20.1%2C%20n_above%3D10%2C%20n_below%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20threshold2%3D.4%2C%20n_above2%3D5%2C%20show%3DTrue)%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%20Interactive%20example%0A%0A%20%20%20%20Change%20the%20parameters%20and%20see%20the%20detected%20onsets%20in%20the%20data%20of%20the%20former%20example%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20threshold%20%3D%20mo.ui.slider(0%2C%201%2C%20step%3D0.05%2C%20value%3D0.1%2C%20label%3D'threshold'%2C%20show_value%3DTrue)%0A%20%20%20%20n_above%20%3D%20mo.ui.slider(1%2C%2050%2C%20value%3D10%2C%20label%3D'n_above'%2C%20show_value%3DTrue)%0A%20%20%20%20n_below%20%3D%20mo.ui.slider(0%2C%2010%2C%20value%3D1%2C%20label%3D'n_below'%2C%20show_value%3DTrue)%0A%20%20%20%20use_threshold2%20%3D%20mo.ui.checkbox(value%3DTrue%2C%20label%3D'use%20threshold2')%0A%20%20%20%20threshold2%20%3D%20mo.ui.slider(0%2C%201%2C%20step%3D0.05%2C%20value%3D0.4%2C%20label%3D'threshold2'%2C%20show_value%3DTrue)%0A%20%20%20%20n_above2%20%3D%20mo.ui.slider(1%2C%2050%2C%20value%3D5%2C%20label%3D'n_above2'%2C%20show_value%3DTrue)%0A%20%20%20%20mo.hstack(%5Bmo.vstack(%5Bthreshold%2C%20n_above%2C%20n_below%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.vstack(%5Buse_threshold2%2C%20threshold2%2C%20n_above2%5D)%5D%2C%20justify%3D'start'%2C%20gap%3D4)%0A%20%20%20%20return%20n_above%2C%20n_above2%2C%20n_below%2C%20threshold%2C%20threshold2%2C%20use_threshold2%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20detect_onset%2C%0A%20%20%20%20n_above%2C%0A%20%20%20%20n_above2%2C%0A%20%20%20%20n_below%2C%0A%20%20%20%20np%2C%0A%20%20%20%20threshold%2C%0A%20%20%20%20threshold2%2C%0A%20%20%20%20use_threshold2%2C%0A)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.normal(0%2C%20.1%2C%20200)%0A%20%20%20%20_x%5B11%3A41%5D%20%3D%20np.ones(30)*.3%0A%20%20%20%20_x%5B51%3A151%5D%20%2B%3D%20np.hstack((np.linspace(0%2C%201%2C%2050)%2C%20np.linspace(1%2C%200%2C%2050)))%0A%20%20%20%20_x%5B80%3A140%3A20%5D%20%3D%200%0A%20%20%20%20detect_onset(_x%2C%20threshold.value%2C%20n_above%3Dn_above.value%2C%20n_below%3Dn_below.value%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20threshold2%3Dthreshold2.value%20if%20use_threshold2.value%20else%20None%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n_above2%3Dn_above2.value%2C%20show%3DTrue)%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%20Function%20performance%0A%0A%20%20%20%20The%20performance%20of%20the%20%60detect_onset%60%20function%20varies%20with%20the%20data%20and%20parameters.%0A%20%20%20%20Here%20is%20a%20simple%20test%20of%20the%20%60detect_onset.py%60%20performance%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(detect_onset%2C%20np%2C%20timeit)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_x%20%3D%20_rng.standard_normal(10000)%0A%20%20%20%20print('Detection%20of%20onset%20(data%20size%3D%20%25d)%3A'%20%25%20_x.size)%0A%20%20%20%20_number%20%3D%20100%0A%20%20%20%20_t%20%3D%20min(timeit.repeat(lambda%3A%20detect_onset(_x%2C%20threshold%3D0%2C%20n_above%3D10%2C%20n_below%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20threshold2%3D.5%2C%20n_above2%3D5%2C%20show%3DFalse)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20number%3D_number%2C%20repeat%3D5))%20%2F%20_number%0A%20%20%20%20print('%25.3f%20ms%20per%20loop'%20%25%20(1e3*_t))%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
72c4f24d6c84f743aca4a7a25a177caa