%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%22matplotlib%3E%3D3.9%22%2C%20%22pandas%3E%3D2.2%22%2C%20%22scipy%3E%3D1.14%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(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%20Resample%20a%20sampled%20signal%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%20force%20signal%20is%20sampled%20at%20100%20Hz%20and%20you%20want%20a%201%2C000%20Hz%20time%20grid.%20Will%20ten%20times%20as%20many%20samples%20reveal%20ten%20times%20as%20much%20information%3F%0A%0A%20%20%20%20We%20will%20reproduce%20the%20original%20DataFrame%20example%2C%20inspect%20the%20time%20axis%2C%20and%20distinguish%20upsampling%20from%20new%20measurement.%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).%20The%20examples%20are%20self-contained.%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%20Python%20setup%0A%0A%20%20%20%20The%20included%20helper%20uses%20SciPy%20polyphase%20resampling.%20NumPy%20generates%20a%20reproducible%20demonstration%20and%20pandas%20labels%20the%20time%20axis.%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%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%0A%20%20%20%20return%20mo%2C%20np%2C%20pd%2C%20plt%0A%0A%0A%40app.function%0Adef%20resample(y%2C%20freq_new%2C%20freq_old%3DNone%2C%20limit%3D1000%2C%20method%3D'resample_poly')%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20Resample%20data%20using%20signal.resample_poly%20scipy%20function.%0A%0A%20%20%20%20Parameters%0A%20%20%20%20----------%0A%20%20%20%20y%20%3A%20numpy%20array%20or%20pandas%20dataframe%20column-oriented%0A%20%20%20%20%20%20%20%20All%20columns%20of%20%60y%60%20will%20be%20resampled.%0A%20%20%20%20freq_new%20%3A%20number%0A%20%20%20%20%20%20%20%20Desired%20new%20sampling%20frequency%20of%20%60y2%60%20(in%20Hz).%0A%20%20%20%20freq_old%20%3A%20number%2C%20optional%20(default%20%3D%20None)%0A%20%20%20%20%20%20%20%20Original%20sampling%20frequency%20of%20%60y%60%20(in%20Hz).%20If%20no%20sampling%20frequency%20is%0A%20%20%20%20%20%20%20%20informed%2C%20%60y%60%20is%20assumed%20to%20be%20a%20pandas%20dataframe%20and%20its%20sampling%0A%20%20%20%20%20%20%20%20frequency%20will%20be%20calculated%20from%20its%20index%20column.%0A%20%20%20%20limit%20%3A%20integer%2C%20optional%20(default%20%3D%201000)%0A%20%20%20%20%20%20%20%20Positive%20integer%20limiting%20the%20denominator%20in%20the%20calculation%20of%20a%20fraction%20of%0A%20%20%20%20%20%20%20%20integers%20to%20represent%20the%20ratio%20between%20up-%20and%20down-%20sampling%0A%20%20%20%20%20%20%20%20frequencies%20(freq_new%2Ffreq_old)%20in%20the%20resample%20(for%20an%20irrational%0A%20%20%20%20%20%20%20%20ratio%2C%20this%20number%20might%20affect%20the%20accuracy%20of%20the%20resampling%20but%0A%20%20%20%20%20%20%20%20it%20will%20be%20faster).%0A%20%20%20%20method%20%3A%20string%2C%20optional%20(default%20%3D%20'resample_poly')%0A%20%20%20%20%20%20%20%20Method%20used%20for%20resampling%20data.%0A%20%20%20%20%20%20%20%20The%20resample_poly%20scipy%20function%20is%20the%20only%20one%20implemented%20so%20far.%0A%0A%20%20%20%20Returns%0A%20%20%20%20-------%0A%20%20%20%20y2%20%3A%20numpy%20array%20or%20pandas%20dataframe%0A%20%20%20%20%20%20%20%20Resampled%20data.%20Same%20type%20(numpy%20array%20or%20pandas%20dataframe)%20and%20same%0A%20%20%20%20%20%20%20%20number%20of%20columns%20as%20%60y%60.%20If%20%60y%60%20is%20a%20pandas%20dataframe%2C%20%60y2%60%20will%20have%0A%20%20%20%20%20%20%20%20its%20time%20origin%20preserved%20and%20its%20index%20sampled%20at%20the%20effective%20rational%0A%20%20%20%20%20%20%20%20approximation%20to%20%60freq_new%60%20Hz.%20Boundary%20samples%20use%20zero-padding.%0A%20%20%20%20%20%20%20%20Upsampling%20creates%20interpolated%20samples%2C%20not%20new%20measured%20information.%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%20from%20scipy%20import%20signal%0A%20%20%20%20from%20fractions%20import%20Fraction%0A%20%20%20%20if%20method%20!%3D%20'resample_poly'%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%22Only%20method%3D'resample_poly'%20is%20supported%22)%0A%20%20%20%20is_frame%20%3D%20isinstance(y%2C%20pd.DataFrame)%0A%20%20%20%20origin%20%3D%20float(y.index%5B0%5D)%20if%20is_frame%20and%20len(y)%20else%200.0%0A%20%20%20%20if%20freq_old%20is%20None%3A%0A%20%20%20%20%20%20%20%20if%20not%20is_frame%20or%20len(y)%20%3C%202%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError('Supply%20freq_old%20or%20a%20DataFrame%20with%20a%20numeric%20time%20index')%0A%20%20%20%20%20%20%20%20intervals%20%3D%20np.diff(np.asarray(y.index%2C%20dtype%3Dfloat))%0A%20%20%20%20%20%20%20%20if%20np.any(intervals%20%3C%3D%200)%20or%20not%20np.allclose(intervals%2C%20intervals%5B0%5D%2C%20rtol%3D1e-05%2C%20atol%3D1e-10)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError('Time%20index%20must%20be%20increasing%20and%20uniformly%20sampled')%0A%20%20%20%20%20%20%20%20freq_old%20%3D%201%20%2F%20intervals.mean()%0A%20%20%20%20if%20not%20np.isfinite(freq_old)%20or%20not%20np.isfinite(freq_new)%20or%20min(freq_old%2C%20freq_new)%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError('Sampling%20frequencies%20must%20be%20finite%20and%20positive')%0A%20%20%20%20data%20%3D%20np.asarray(y)%0A%20%20%20%20if%20data.ndim%20not%20in%20(1%2C%202)%20or%20not%20len(data)%20or%20(not%20np.isfinite(data).all())%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError('Data%20must%20be%20a%20nonempty%20finite%20one-%20or%20two-dimensional%20array')%0A%20%20%20%20fr%20%3D%20Fraction(float(freq_new%20%2F%20freq_old)).limit_denominator(limit)%0A%20%20%20%20if%20fr.numerator%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError('Increase%20limit%20to%20represent%20the%20requested%20frequency%20ratio')%0A%20%20%20%20actual_rate%20%3D%20freq_old%20*%20fr.numerator%20%2F%20fr.denominator%0A%20%20%20%20y2%20%3D%20signal.resample_poly(data%2C%20fr.numerator%2C%20fr.denominator%2C%20axis%3D0)%0A%20%20%20%20if%20is_frame%3A%0A%20%20%20%20%20%20%20%20y2%20%3D%20pd.DataFrame(y2%2C%20columns%3Dy.columns%2C%20index%3Dorigin%20%2B%20np.arange(len(y2))%20%2F%20actual_rate)%0A%20%20%20%20%20%20%20%20y2.index.name%20%3D%20y.index.name%0A%20%20%20%20return%20y2%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%20Start%20with%20a%20labelled%20time%20grid%0A%0A%20%20%20%20A%20sampling%20frequency%20in%20hertz%20gives%20samples%20per%20second.%20A%20100-sample%20sequence%20at%20100%20Hz%20has%20sample%20times%200%20through%200.99%20s.%20Its%20sampling-bin%20duration%20is%201%20s%3B%20the%20elapsed%20time%20between%20first%20and%20last%20samples%20is%200.99%20s.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pd)%3A%0A%20%20%20%20rng%20%3D%20np.random.default_rng(7)%0A%20%20%20%20y%20%3D%20pd.DataFrame(rng.standard_normal((100%2C%203))%2C%20columns%3D%5B%22A%22%2C%20%22B%22%2C%20%22C%22%5D)%0A%20%20%20%20y.index%20%3D%20np.arange(len(y))%20%2F%20100%0A%20%20%20%20y.index.name%20%3D%20%22Time%20%5Bs%5D%22%0A%20%20%20%20y.head()%0A%20%20%20%20return%20(y%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**Exercise%201.**%20Predict%20the%20output%20length%20and%20time%20step%20when%20converting%20this%20sequence%20to%201%2C000%20Hz.%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%20The%20ratio%20is%2010%2C%20so%20there%20are%201%2C000%20output%20samples%2C%20spaced%20by%200.001%20s.%20The%20first%20time%20is%200%20and%20the%20last%20is%200.999%20s.%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%20Change%20the%20sampling%20grid%0A%0A%20%20%20%20%60resample_poly%60%20upsamples%2C%20applies%20a%20low-pass%20filter%2C%20and%20downsamples.%20The%20filter%20controls%20aliasing%20when%20reducing%20the%20sampling%20rate.%20Upsampling%20interpolates%3A%20it%20does%20not%20recover%20content%20already%20lost%20during%20acquisition.%20The%20default%20zero%20padding%20can%20affect%20the%20ends%20of%20a%20finite%20record.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(y)%3A%0A%20%20%20%20y2%20%3D%20resample(y%2C%20freq_old%3D100%2C%20freq_new%3D1000)%0A%20%20%20%20y2.head()%0A%20%20%20%20return%20(y2%2C)%0A%0A%0A%40app.cell%0Adef%20_(plt%2C%20y%2C%20y2)%3A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(9%2C%203))%0A%20%20%20%20_ax.plot(y.index%2C%20y%5B%22A%22%5D%2C%20%22o%22%2C%20label%3D%22original%22)%0A%20%20%20%20_ax.plot(y2.index%2C%20y2%5B%22A%22%5D%2C%20label%3D%22resampled%22)%0A%20%20%20%20_ax.set(xlabel%3D%22Time%20%5Bs%5D%22%2C%20ylabel%3D%22Signal%20%5Ba.u.%5D%22)%0A%20%20%20%20_ax.legend()%0A%20%20%20%20_fig%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.**%20What%20is%20the%20Nyquist%20frequency%20after%20downsampling%20a%20signal%20from%201%2C000%20to%20100%20Hz%3F%20Why%20filter%20first%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%20The%20new%20Nyquist%20frequency%20is%2050%20Hz.%20Components%20above%2050%20Hz%20can%20fold%20into%20lower%20frequencies%20unless%20sufficiently%20attenuated%20before%20downsampling.%20Polyphase%20resampling%20includes%20this%20filtering.%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%20Keep%20the%20time%20origin%20and%20report%20the%20effective%20rate%0A%0A%20%20%20%20For%20a%20DataFrame%2C%20the%20helper%20keeps%20the%20first%20timestamp.%20If%20%60freq_old%60%20is%20omitted%2C%20it%20checks%20that%20the%20numeric%20index%20is%20increasing%20and%20uniformly%20sampled.%20A%20rational%20approximation%20represents%20the%20rate%20ratio%3B%20%60limit%60%20controls%20its%20denominator.%20The%20output%20index%20uses%20the%20effective%20rate%20of%20that%20approximation%2C%20rather%20than%20silently%20labelling%20it%20with%20an%20unattained%20requested%20rate.%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%20Checkpoint%20questions%0A%0A%20%20%20%201.%20Can%20upsampling%20undo%20aliasing%20introduced%20during%20acquisition%3F%0A%0A%20%20%20%202.%20Why%20inspect%20the%20first%20and%20last%20part%20of%20the%20output%3F%0A%0A%20%20%20%203.%20Can%20this%20helper%20infer%20a%20rate%20from%20irregular%20timestamps%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.%0A%20%20%20%202.%20Padding%20and%20filtering%20affect%20boundaries.%0A%20%20%20%203.%20No%3B%20provide%20a%20justified%20uniform%20grid%20first.%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.**%20Resample%20240%20samples%20from%20120%20Hz%20to%20150%20Hz.%20Give%20the%20output%20length%20and%20interval.%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%20300%20samples%3B%20interval%20%241%2F150%24%20s.%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.**%20A%20uniformly%20sampled%20DataFrame%20begins%20at%203%20s.%20What%20is%20its%20first%20output%20time%20after%20resampling%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%203%20s.%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%20resampling%20with%20%5Btime%20normalization%5D(https%3A%2F%2Fgithub.com%2Fdemotu%2Ftnorma)%3A%20seconds%20and%20percent%20cycle%20answer%20different%20questions.%0A%0A%20%20%20%20%23%23%23%20Video%20lectures%20on%20the%20Internet%0A%0A%20%20%20%20%5BMIT%20Signals%20and%20Systems%20video%20lectures%5D(https%3A%2F%2Focw.mit.edu%2Fcourses%2Fres-6-007-signals-and-systems-spring-2011%2Fvideo_galleries%2Fvideo-lectures%2F)%20include%20sampling%20and%20reconstruction.%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%5BSciPy%3A%20resample_poly%5D(https%3A%2F%2Fdocs.scipy.org%2Fdoc%2Fscipy%2Freference%2Fgenerated%2Fscipy.signal.resample_poly.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
4154799491ef23e24dd05e9a9bd9d308