import%20marimo%0A%0A__generated_with%20%3D%20%220.23.9%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20from%20scripts.learning_path_i18n%20import%20(%0A%20%20%20%20%20%20%20%20language_switch_markdown%2C%0A%20%20%20%20%20%20%20%20load_catalog%2C%0A%20%20%20%20%20%20%20%20locale_from_argv%2C%0A%20%20%20%20%20%20%20%20navigation_markdown%2C%0A%20%20%20%20)%0A%0A%20%20%20%20locale%20%3D%20locale_from_argv()%0A%20%20%20%20catalog%20%3D%20load_catalog(%2205_custom_functions%22%2C%20locale)%0A%0A%20%20%20%20def%20t(key%2C%20**values)%3A%0A%20%20%20%20%20%20%20%20return%20catalog.text(key%2C%20**values)%0A%0A%20%20%20%20return%20language_switch_markdown%2C%20locale%2C%20mo%2C%20navigation_markdown%2C%20t%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(f%22%23%20%7Bt('title')%7D%5Cn%5Cn%7Bt('intro')%7D%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(language_switch_markdown%2C%20locale%2C%20mo)%3A%0A%20%20%20%20mo.md(language_switch_markdown(%2205_custom_functions%22%2C%20locale))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22choice_guide%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22input_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20from%20scipy%20import%20signal%20as%20scipy_signal%0A%0A%20%20%20%20import%20wandas%20as%20wd%0A%0A%20%20%20%20plt.rcParams%5B%22figure.figsize%22%5D%20%3D%20(12%2C%206)%0A%20%20%20%20return%20np%2C%20plt%2C%20scipy_signal%2C%20wd%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20wd)%3A%0A%20%20%20%20rng%20%3D%20np.random.default_rng(7)%0A%20%20%20%20sampling_rate%20%3D%201000.0%0A%20%20%20%20sample_count%20%3D%20200%0A%20%20%20%20time_axis%20%3D%20np.arange(sample_count%2C%20dtype%3Dnp.float64)%20%2F%20sampling_rate%0A%20%20%20%20signal_data%20%3D%20np.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20np.sin(2%20*%20np.pi%20*%2025%20*%20time_axis)%20%2B%200.02%20*%20rng.normal(size%3Dsample_count)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%200.5%20*%20np.cos(2%20*%20np.pi%20*%2040%20*%20time_axis)%20%2B%200.02%20*%20rng.normal(size%3Dsample_count)%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20).astype(np.float32)%0A%20%20%20%20input_frame%20%3D%20wd.from_numpy(%0A%20%20%20%20%20%20%20%20data%3Dsignal_data%2C%0A%20%20%20%20%20%20%20%20sampling_rate%3Dsampling_rate%2C%0A%20%20%20%20%20%20%20%20metadata%3D%7B%22source%22%3A%20%22lesson_05%22%2C%20%22unit%22%3A%20%22V%22%7D%2C%0A%20%20%20%20%20%20%20%20ch_labels%3D%5B%22left%22%2C%20%22right%22%5D%2C%0A%20%20%20%20%20%20%20%20ch_units%3D%5B%22V%22%2C%20%22V%22%5D%2C%0A%20%20%20%20).with_source_time_offset(0.25)%0A%20%20%20%20return%20input_frame%2C%20signal_data%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(input_frame%2C%20mo%2C%20signal_data%2C%20t)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22input_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20shape%3Dinput_frame.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dtype%3Dsignal_data.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channels%3Dinput_frame.n_channels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20samples%3Dinput_frame.n_samples%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20sampling_rate%3Dinput_frame.sampling_rate%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20duration%3Df%22%7Binput_frame.duration%3A.3f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20offset%3Dinput_frame.source_time_offset.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20labels%3Dinput_frame.labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20units%3D%5Bchannel.unit%20for%20channel%20in%20input_frame.channels%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20metadata%3Dinput_frame.metadata%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22apply_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(input_frame)%3A%0A%20%20%20%20def%20scale_channels(data%2C%20factor)%3A%0A%20%20%20%20%20%20%20%20return%20data%20*%20factor%0A%0A%20%20%20%20def%20same_shape(input_shape)%3A%0A%20%20%20%20%20%20%20%20return%20input_shape%0A%0A%20%20%20%20scaled_frame%20%3D%20input_frame.apply(scale_channels%2C%20output_shape_func%3Dsame_shape%2C%20factor%3D1.5)%0A%20%20%20%20return%20same_shape%2C%20scaled_frame%0A%0A%0A%40app.cell%0Adef%20_(input_frame)%3A%0A%20%20%20%20normalized_frame%20%3D%20input_frame.normalize()%0A%20%20%20%20return%20(normalized_frame%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20input_frame%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20normalized_frame%2C%0A%20%20%20%20np%2C%0A%20%20%20%20same_shape%2C%0A%20%20%20%20scaled_frame%2C%0A%20%20%20%20signal_data%2C%0A%20%20%20%20t%2C%0A)%3A%0A%20%20%20%20import%20dask%0A%0A%20%20%20%20execution_counter%20%3D%20%7B%22calls%22%3A%200%7D%0A%0A%20%20%20%20def%20probe(data)%3A%0A%20%20%20%20%20%20%20%20execution_counter%5B%22calls%22%5D%20%2B%3D%201%0A%20%20%20%20%20%20%20%20return%20data%0A%0A%20%20%20%20with%20dask.config.set(scheduler%3D%22synchronous%22)%3A%0A%20%20%20%20%20%20%20%20lazy_probe_frame%20%3D%20input_frame.apply(%0A%20%20%20%20%20%20%20%20%20%20%20%20probe%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_shape_func%3Dsame_shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dask_pure%3DFalse%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20probe_calls_after_apply%20%3D%20execution_counter%5B%22calls%22%5D%0A%20%20%20%20%20%20%20%20_probe_values%20%3D%20lazy_probe_frame.data%0A%20%20%20%20%20%20%20%20probe_calls_after_data%20%3D%20execution_counter%5B%22calls%22%5D%0A%20%20%20%20assert%20probe_calls_after_apply%20%3D%3D%200%0A%20%20%20%20assert%20probe_calls_after_data%20%3E%20probe_calls_after_apply%0A%0A%20%20%20%20input_data_before%20%3D%20input_frame.data.copy()%0A%20%20%20%20scaled_values%20%3D%20scaled_frame.data%0A%20%20%20%20normalized_values%20%3D%20normalized_frame.data%0A%20%20%20%20assert%20scaled_frame.shape%20%3D%3D%20input_frame.shape%0A%20%20%20%20assert%20scaled_frame.sampling_rate%20%3D%3D%20input_frame.sampling_rate%0A%20%20%20%20assert%20scaled_frame.previous%20is%20input_frame%0A%20%20%20%20assert%20normalized_frame.previous%20is%20input_frame%0A%20%20%20%20assert%20scaled_frame.lineage%20is%20not%20input_frame.lineage%0A%20%20%20%20assert%20scaled_frame.operation_history%5B-1%5D%5B%22operation%22%5D%20%3D%3D%20%22wandas.custom.apply%22%0A%20%20%20%20assert%20normalized_frame.operation_history%5B-1%5D%5B%22operation%22%5D%20%3D%3D%20%22wandas.audio.normalize%22%0A%20%20%20%20assert%20scaled_values.dtype%20%3D%3D%20signal_data.dtype%0A%20%20%20%20assert%20normalized_values.dtype%20%3D%3D%20signal_data.dtype%0A%20%20%20%20assert%20scaled_frame.metadata%20%3D%3D%20input_frame.metadata%0A%20%20%20%20assert%20%5Bchannel.unit%20for%20channel%20in%20scaled_frame.channels%5D%20%3D%3D%20%5B%22V%22%2C%20%22V%22%5D%0A%20%20%20%20assert%20scaled_frame.labels%20%3D%3D%20%5B%22scale_channels(left)%22%2C%20%22scale_channels(right)%22%5D%0A%20%20%20%20np.testing.assert_array_equal(input_data_before%2C%20signal_data)%0A%20%20%20%20assert%20input_frame.labels%20%3D%3D%20%5B%22left%22%2C%20%22right%22%5D%0A%20%20%20%20assert%20input_frame.metadata%20%3D%3D%20%7B%22source%22%3A%20%22lesson_05%22%2C%20%22unit%22%3A%20%22V%22%7D%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22apply_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20materialized_type%3Dtype(scaled_values).__name__%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_shape%3Dinput_frame.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_shape%3Dscaled_frame.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_dtype%3Dsignal_data.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_dtype%3Dscaled_values.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20lazy_calls_after_apply%3Dprobe_calls_after_apply%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20lazy_calls_after_data%3Dprobe_calls_after_data%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20sampling_rate%3Dscaled_frame.sampling_rate%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20metadata%3Dscaled_frame.metadata%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_labels%3Dinput_frame.labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_labels%3Dscaled_frame.labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channel_units%3D%5Bchannel.unit%20for%20channel%20in%20scaled_frame.channels%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20custom_operation%3Dscaled_frame.operation_history%5B-1%5D%5B%22operation%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20standard_operation%3Dnormalized_frame.operation_history%5B-1%5D%5B%22operation%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20lineage_status%3Dscaled_frame.lineage%20is%20not%20None%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20previous_status%3Dscaled_frame.previous%20is%20input_frame%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22runtime_only_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(normalized_frame)%3A%0A%20%20%20%20from%20wandas.pipeline%20import%20RecipePlan%0A%0A%20%20%20%20standard_recipe%20%3D%20RecipePlan.from_frame(normalized_frame%2C%20input_names%3D(%22signal%22%2C))%0A%20%20%20%20standard_recipe_payload%20%3D%20standard_recipe.to_dict()%0A%20%20%20%20return%20RecipePlan%2C%20standard_recipe_payload%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20scaled_frame%2C%20standard_recipe_payload%2C%20t)%3A%0A%20%20%20%20from%20wandas.pipeline%20import%20RecipeExtractionError%20as%20_RecipeExtractionError%0A%20%20%20%20from%20wandas.pipeline%20import%20RecipePlan%20as%20_RecipePlan%0A%0A%20%20%20%20runtime_recipe_error%20%3D%20%22%22%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20_RecipePlan.from_frame(scaled_frame)%0A%20%20%20%20except%20_RecipeExtractionError%20as%20error%3A%0A%20%20%20%20%20%20%20%20runtime_recipe_error%20%3D%20str(error)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20raise%20AssertionError(%22Frame.apply(callable)%20must%20remain%20runtime-only%22)%0A%0A%20%20%20%20portable_operation_ids%20%3D%20%5Bnode%5B%22operation%22%5D%20for%20node%20in%20standard_recipe_payload%5B%22nodes%22%5D%5D%0A%20%20%20%20assert%20%22Frame.apply(callable)%20is%20runtime-only%22%20in%20runtime_recipe_error%0A%20%20%20%20assert%20scaled_frame.operation_history%5B-1%5D%5B%22operation%22%5D%20%3D%3D%20%22wandas.custom.apply%22%0A%20%20%20%20assert%20%22wandas.audio.normalize%22%20in%20portable_operation_ids%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22recipe_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rejected_operation%3Dscaled_frame.operation_history%5B-1%5D%5B%22operation%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20error%3Druntime_recipe_error%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20portable_operations%3Dportable_operation_ids%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20schema%3Dstandard_recipe_payload%5B%22schema%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20version%3Dstandard_recipe_payload%5B%22version%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20node_count%3Dlen(standard_recipe_payload%5B%22nodes%22%5D)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22shape_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(input_frame)%3A%0A%20%20%20%20def%20take_first_half(data)%3A%0A%20%20%20%20%20%20%20%20return%20data%5B%3A%2C%20%3A%20data.shape%5B1%5D%20%2F%2F%202%5D%0A%0A%20%20%20%20def%20half_shape(input_shape)%3A%0A%20%20%20%20%20%20%20%20return%20(input_shape%5B0%5D%2C%20input_shape%5B1%5D%20%2F%2F%202)%0A%0A%20%20%20%20first_half_frame%20%3D%20input_frame.apply(take_first_half%2C%20output_shape_func%3Dhalf_shape)%0A%20%20%20%20semantic_trimmed_frame%20%3D%20input_frame.trim(start%3D0.05%2C%20end%3D0.15)%0A%20%20%20%20return%20first_half_frame%2C%20semantic_trimmed_frame%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(first_half_frame%2C%20input_frame%2C%20mo%2C%20np%2C%20semantic_trimmed_frame%2C%20t)%3A%0A%20%20%20%20assert%20first_half_frame.shape%20%3D%3D%20(2%2C%20100)%0A%20%20%20%20assert%20semantic_trimmed_frame.shape%20%3D%3D%20(2%2C%20100)%0A%20%20%20%20assert%20first_half_frame.sampling_rate%20%3D%3D%20input_frame.sampling_rate%0A%20%20%20%20assert%20semantic_trimmed_frame.sampling_rate%20%3D%3D%20input_frame.sampling_rate%0A%20%20%20%20assert%20first_half_frame.duration%20%3D%3D%200.1%0A%20%20%20%20assert%20semantic_trimmed_frame.duration%20%3D%3D%200.1%0A%20%20%20%20np.testing.assert_allclose(first_half_frame.source_time_offset%2C%20%5B0.25%2C%200.25%5D)%0A%20%20%20%20np.testing.assert_allclose(semantic_trimmed_frame.source_time_offset%2C%20%5B0.30%2C%200.30%5D)%0A%20%20%20%20np.testing.assert_allclose(first_half_frame.source_time%5B%3A%2C%200%5D%2C%20%5B0.25%2C%200.25%5D)%0A%20%20%20%20np.testing.assert_allclose(semantic_trimmed_frame.source_time%5B%3A%2C%200%5D%2C%20%5B0.30%2C%200.30%5D)%0A%20%20%20%20assert%20first_half_frame.metadata%20%3D%3D%20input_frame.metadata%0A%20%20%20%20assert%20semantic_trimmed_frame.metadata%20%3D%3D%20input_frame.metadata%0A%20%20%20%20assert%20first_half_frame.previous%20is%20input_frame%0A%20%20%20%20assert%20semantic_trimmed_frame.previous%20is%20input_frame%0A%20%20%20%20assert%20first_half_frame.operation_history%5B-1%5D%5B%22operation%22%5D%20%3D%3D%20%22wandas.custom.apply%22%0A%20%20%20%20assert%20semantic_trimmed_frame.operation_history%5B-1%5D%5B%22operation%22%5D%20%3D%3D%20%22wandas.frame.time_slice%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22shape_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_shape%3Dfirst_half_frame.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_samples%3Dfirst_half_frame.n_samples%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_sampling_rate%3Dfirst_half_frame.sampling_rate%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_duration%3Df%22%7Bfirst_half_frame.duration%3A.3f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_offset%3Dfirst_half_frame.source_time_offset.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_source_time%3Dfirst_half_frame.source_time%5B%3A%2C%200%5D.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20apply_metadata%3Dfirst_half_frame.metadata%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_shape%3Dsemantic_trimmed_frame.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_samples%3Dsemantic_trimmed_frame.n_samples%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_sampling_rate%3Dsemantic_trimmed_frame.sampling_rate%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_duration%3Df%22%7Bsemantic_trimmed_frame.duration%3A.3f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_offset%3Dsemantic_trimmed_frame.source_time_offset.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_source_time%3Dsemantic_trimmed_frame.source_time%5B%3A%2C%200%5D.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20trim_metadata%3Dsemantic_trimmed_frame.metadata%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22scipy_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(input_frame%2C%20plt%2C%20scipy_signal)%3A%0A%20%20%20%20def%20median_filter_channel_first(data%2C%20kernel_size)%3A%0A%20%20%20%20%20%20%20%20return%20scipy_signal.medfilt(data%2C%20kernel_size%3D(1%2C%20kernel_size))%0A%0A%20%20%20%20median_frame%20%3D%20input_frame.apply(%0A%20%20%20%20%20%20%20%20median_filter_channel_first%2C%0A%20%20%20%20%20%20%20%20output_shape_func%3Dlambda%20input_shape%3A%20input_shape%2C%0A%20%20%20%20%20%20%20%20kernel_size%3D5%2C%0A%20%20%20%20)%0A%20%20%20%20(_fig%2C%20_axes)%20%3D%20plt.subplots(2%2C%201%2C%20figsize%3D(12%2C%207)%2C%20sharex%3DTrue)%0A%20%20%20%20input_frame.plot(ax%3D_axes%5B0%5D%2C%20title%3D%22Input%20signal%22%2C%20overlay%3DTrue)%0A%20%20%20%20median_frame.plot(ax%3D_axes%5B1%5D%2C%20title%3D%22Median%20filtered%20signal%22%2C%20overlay%3DTrue)%0A%20%20%20%20_axes%5B1%5D.set_xlabel(%22Time%20%5Bs%5D%22)%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20median_filter_channel_first%2C%20median_frame%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20input_frame%2C%0A%20%20%20%20median_filter_channel_first%2C%0A%20%20%20%20median_frame%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20scipy_signal%2C%0A%20%20%20%20t%2C%0A)%3A%0A%20%20%20%20input_values%20%3D%20input_frame.data%0A%20%20%20%20filtered_values%20%3D%20median_frame.data%0A%20%20%20%20expected_values%20%3D%20scipy_signal.medfilt(input_values%2C%20kernel_size%3D(1%2C%205))%0A%20%20%20%20channelwise_expected%20%3D%20np.vstack(%0A%20%20%20%20%20%20%20%20%5Bmedian_filter_channel_first(input_values%5Bindex%20%3A%20index%20%2B%201%5D%2C%205)%5B0%5D%20for%20index%20in%20range(input_values.shape%5B0%5D)%5D%0A%20%20%20%20)%0A%20%20%20%20boundary_probe%20%3D%20np.array(%5B%5B1%2C%202%2C%203%2C%204%2C%205%5D%5D%2C%20dtype%3Dinput_values.dtype)%0A%20%20%20%20boundary_result%20%3D%20median_filter_channel_first(boundary_probe%2C%205)%0A%20%20%20%20np.testing.assert_array_equal(filtered_values%2C%20expected_values)%0A%20%20%20%20np.testing.assert_array_equal(filtered_values%2C%20channelwise_expected)%0A%20%20%20%20assert%20filtered_values.shape%20%3D%3D%20input_values.shape%0A%20%20%20%20assert%20filtered_values.dtype%20%3D%3D%20input_values.dtype%0A%20%20%20%20np.testing.assert_array_equal(boundary_result%2C%20scipy_signal.medfilt(boundary_probe%2C%20kernel_size%3D(1%2C%205)))%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22scipy_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axis%3D%22axis%3D1%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20kernel_shape%3D(1%2C%205)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_shape%3Dinput_values.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_shape%3Dfiltered_values.shape%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_dtype%3Dinput_values.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_dtype%3Dfiltered_values.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channel_independent%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20boundary_result%3Dboundary_result.tolist()%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22extension_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20collections.abc%20import%20Mapping%0A%20%20%20%20from%20typing%20import%20Any%0A%0A%20%20%20%20from%20wandas.frames%20import%20ChannelFrame%0A%20%20%20%20from%20wandas.pipeline%20import%20default_recipe_registry%2C%20recipe_definition%2C%20recipe_operation%0A%20%20%20%20from%20wandas.processing%20import%20ChannelIndependentAudioOperation%0A%20%20%20%20from%20wandas.utils.types%20import%20NDArrayReal%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Any%2C%0A%20%20%20%20%20%20%20%20ChannelFrame%2C%0A%20%20%20%20%20%20%20%20ChannelIndependentAudioOperation%2C%0A%20%20%20%20%20%20%20%20Mapping%2C%0A%20%20%20%20%20%20%20%20NDArrayReal%2C%0A%20%20%20%20%20%20%20%20default_recipe_registry%2C%0A%20%20%20%20%20%20%20%20recipe_definition%2C%0A%20%20%20%20%20%20%20%20recipe_operation%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(Any%2C%20ChannelIndependentAudioOperation%2C%20Mapping%2C%20NDArrayReal%2C%20np)%3A%0A%20%20%20%20def%20validate_lesson_gain(params%3A%20Mapping%5Bstr%2C%20Any%5D)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20if%20set(params)%20!%3D%20%7B%22factor%22%7D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(%22gain%20requires%20exactly%20one%20parameter%3A%20factor%22)%0A%20%20%20%20%20%20%20%20factor%20%3D%20params%5B%22factor%22%5D%0A%20%20%20%20%20%20%20%20if%20isinstance(factor%2C%20bool)%20or%20not%20isinstance(factor%2C%20(int%2C%20float))%20or%20not%20np.isfinite(factor)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(%22factor%20must%20be%20a%20finite%20real%20number%22)%0A%0A%20%20%20%20class%20LessonGain(ChannelIndependentAudioOperation%5BNDArrayReal%2C%20NDArrayReal%5D)%3A%0A%20%20%20%20%20%20%20%20name%20%3D%20%22lesson05_gain%22%0A%0A%20%20%20%20%20%20%20%20def%20validate_params(self)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20validate_lesson_gain(self.to_params())%0A%0A%20%20%20%20%20%20%20%20def%20_process(self%2C%20data%3A%20NDArrayReal)%20-%3E%20NDArrayReal%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.asarray(data%2C%20dtype%3Dnp.float64)%20*%20self._config_value(%22factor%22)%0A%0A%20%20%20%20%20%20%20%20def%20calculate_output_dtype(self%2C%20input_dtype%3A%20np.dtype%5BAny%5D%2C%20*input_dtypes%3A%20np.dtype%5BAny%5D)%20-%3E%20np.dtype%5BAny%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.dtype(np.float64)%0A%0A%20%20%20%20return%20LessonGain%2C%20validate_lesson_gain%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22extension_frame_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ChannelFrame%2C%20LessonGain%2C%20recipe_operation%2C%20validate_lesson_gain)%3A%0A%20%20%20%20class%20LessonFrame(ChannelFrame)%3A%0A%20%20%20%20%20%20%20%20%40recipe_operation(%22lesson05.audio.gain%22%2C%20validate_params%3Dvalidate_lesson_gain)%0A%20%20%20%20%20%20%20%20def%20gain(self%2C%20factor%3A%20float)%20-%3E%20%22LessonFrame%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20isinstance(self%2C%20ChannelFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20TypeError(%22LessonFrame.gain%20requires%20a%20ChannelFrame%20(time-domain%20input)%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20operation%20%3D%20LessonGain(self.sampling_rate%2C%20factor%3Dfactor)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20self._apply_operation_instance(operation)%0A%0A%20%20%20%20return%20(LessonFrame%2C)%0A%0A%0A%40app.cell%0Adef%20_(LessonFrame%2C%20default_recipe_registry%2C%20recipe_definition)%3A%0A%20%20%20%20extension_registry%20%3D%20default_recipe_registry().with_operation(recipe_definition(LessonFrame.gain))%0A%20%20%20%20return%20(extension_registry%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22extension_replay_section%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LessonFrame%2C%20np)%3A%0A%20%20%20%20extension_samples%20%3D%20np.array(%5B%5B1%2C%202%2C%203%5D%2C%20%5B4%2C%205%2C%206%5D%5D%2C%20dtype%3Dnp.int16)%0A%20%20%20%20extension_source%20%3D%20LessonFrame.from_numpy(extension_samples%2C%20sampling_rate%3D8000%2C%20metadata%3D%7B%22recording%22%3A%20%22original%22%7D)%0A%20%20%20%20extension_processed%20%3D%20extension_source.gain(factor%3D0.5)%0A%20%20%20%20return%20extension_processed%2C%20extension_samples%2C%20extension_source%0A%0A%0A%40app.cell%0Adef%20_(RecipePlan%2C%20extension_processed%2C%20extension_registry)%3A%0A%20%20%20%20import%20json%0A%0A%20%20%20%20extension_plan%20%3D%20RecipePlan.from_frame(extension_processed%2C%20input_names%3D(%22signal%22%2C)%2C%20registry%3Dextension_registry)%0A%20%20%20%20extension_json%20%3D%20json.dumps(extension_plan.to_dict())%0A%20%20%20%20restored_extension%20%3D%20RecipePlan.from_dict(json.loads(extension_json)%2C%20registry%3Dextension_registry)%0A%20%20%20%20return%20(restored_extension%2C)%0A%0A%0A%40app.cell%0Adef%20_(LessonFrame%2C%20extension_registry%2C%20np%2C%20restored_extension)%3A%0A%20%20%20%20replacement_samples%20%3D%20np.array(%5B%5B10%2C%2020%2C%2030%5D%2C%20%5B40%2C%2050%2C%2060%5D%5D%2C%20dtype%3Dnp.int16)%0A%20%20%20%20extension_replacement%20%3D%20LessonFrame.from_numpy(%0A%20%20%20%20%20%20%20%20replacement_samples%2C%20sampling_rate%3D8000%2C%20metadata%3D%7B%22recording%22%3A%20%22replacement%22%7D%0A%20%20%20%20).with_source_time_offset(0.25)%0A%20%20%20%20extension_replayed%20%3D%20restored_extension.apply(%7B%22signal%22%3A%20extension_replacement%7D%2C%20registry%3Dextension_registry)%0A%20%20%20%20return%20extension_replacement%2C%20extension_replayed%2C%20replacement_samples%0A%0A%0A%40app.cell%0Adef%20_(extension_processed%2C%20extension_replayed)%3A%0A%20%20%20%20original_gain_values%20%3D%20extension_processed.data%0A%20%20%20%20replay_gain_values%20%3D%20extension_replayed.data%0A%20%20%20%20return%20original_gain_values%2C%20replay_gain_values%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20extension_replacement%2C%0A%20%20%20%20extension_replayed%2C%0A%20%20%20%20extension_samples%2C%0A%20%20%20%20extension_source%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20original_gain_values%2C%0A%20%20%20%20replacement_samples%2C%0A%20%20%20%20replay_gain_values%2C%0A%20%20%20%20t%2C%0A)%3A%0A%20%20%20%20np.testing.assert_allclose(original_gain_values%2C%20%5B%5B0.5%2C%201.0%2C%201.5%5D%2C%20%5B2.0%2C%202.5%2C%203.0%5D%5D)%0A%20%20%20%20np.testing.assert_allclose(replay_gain_values%2C%20%5B%5B5.0%2C%2010.0%2C%2015.0%5D%2C%20%5B20.0%2C%2025.0%2C%2030.0%5D%5D)%0A%20%20%20%20assert%20original_gain_values.dtype%20%3D%3D%20replay_gain_values.dtype%20%3D%3D%20np.dtype(np.float64)%0A%20%20%20%20np.testing.assert_array_equal(extension_source.data%2C%20extension_samples)%0A%20%20%20%20np.testing.assert_array_equal(extension_replacement.data%2C%20replacement_samples)%0A%20%20%20%20assert%20extension_source.metadata%20%3D%3D%20%7B%22recording%22%3A%20%22original%22%7D%0A%20%20%20%20assert%20extension_replayed.metadata%20%3D%3D%20extension_replacement.metadata%20%3D%3D%20%7B%22recording%22%3A%20%22replacement%22%7D%0A%20%20%20%20np.testing.assert_array_equal(extension_replayed.source_time_offset%2C%20%5B0.25%2C%200.25%5D)%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20t(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22extension_result%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20original_input%3Dextension_samples.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20original_output%3Doriginal_gain_values.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20replay_input%3Dreplacement_samples.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20replay_output%3Dreplay_gain_values.tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_dtype%3Dextension_samples.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_dtype%3Dreplay_gain_values.dtype%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20metadata%3Dextension_replayed.metadata%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20offset%3Dextension_replayed.source_time_offset.tolist()%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20t)%3A%0A%20%20%20%20mo.md(t(%22summary%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(locale%2C%20mo%2C%20navigation_markdown)%3A%0A%20%20%20%20mo.md(navigation_markdown(%2205_custom_functions%22%2C%20locale))%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
2a95a5681fef0246227797df38c04349