121 the modified sinc smoother implementation#213
Conversation
…-Smoother-Implementation
|
Hmm, it seems like we have some issues regarding installing task where we hit an API rate limit 😢 - any experience with this? It seems like the sphinx documentation didn't create any problems. I assume it is because it uses the taskfile.dev while the check that fails doesn't? Any help would be much appreciated. Edit: After re-running the workflow, the checks passed - don't know entirely why the mentioned error appeared. If any of you know why it might have happened, please let me know |
There was a problem hiding this comment.
Pull Request Overview
This PR implements the Modified Sinc Filter smoother as suggested in issue #121 and refactors the existing Savitzky-Golay filter to use a common base class for FIR (Finite Impulse Response) filters.
Key changes:
- Created a new
_BaseFIRFilterbase class to share common FIR filter functionality - Implemented the Modified Sinc Filter based on Schmid et al. (2022) with configurable parameters and passband-flattening corrections
- Refactored
SavitzkyGolayFilterto inherit from_BaseFIRFilter
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
chemotools/smooth/_base.py |
Added _BaseFIRFilter base class with common fit/transform logic and padding strategies |
chemotools/smooth/_modified_sinc_smoother.py |
New implementation of Modified Sinc Filter with kernel computation based on sinc core, Gaussian-like window, and optional kappa corrections |
chemotools/smooth/_savitzky_golay_filter.py |
Refactored to inherit from _BaseFIRFilter and implement kernel computation via scipy's savgol_coeffs |
tests/smooth/test_modified_sinc.py |
Comprehensive test suite for Modified Sinc Filter covering sklearn compliance, kernel properties, boundary modes, and axis behavior |
chemotools/smooth/__init__.py |
Added ModifiedSincFilter to public API exports |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| class ModifiedSincFilter(_BaseFIRFilter): | ||
| """ | ||
| Modified Sinc smoothing (MS) for denoising while preserving peak positions based on the paper "Why and How Savitzky–Golay Filters Should Be Replaced." |
There was a problem hiding this comment.
Corrected spelling of 'Savitzky–Golay' to 'Savitzky-Golay' for consistency with the filename and other references in the codebase.
|
|
||
| References | ||
| ---------- | ||
| [1] Schmid, M.; Rath, D.; Diebold, U. "Why and How Savitzky–Golay Filters Should Be Replaced." |
There was a problem hiding this comment.
Corrected spelling of 'Savitzky–Golay' to 'Savitzky-Golay' for consistency with the filename and other references in the codebase.
| [1] Schmid, M.; Rath, D.; Diebold, U. "Why and How Savitzky–Golay Filters Should Be Replaced." | |
| [1] Schmid, M.; Rath, D.; Diebold, U. "Why and How Savitzky-Golay Filters Should Be Replaced." |
There was a problem hiding this comment.
Hi @NusretSalli It looks awesome, very nice job, clear, and well structured!!! Thanks a lot!! 😄
| return np.moveaxis(out, -1, ax) | ||
|
|
||
| # --- hooks for subclasses --- | ||
| def _compute_kernel(self) -> np.ndarray: |
There was a problem hiding this comment.
Hi @NusretSalli the base class looks great, just a small comment, what do you think of using the decorator @abstractmethod for the class? a bit like this one (https://github.com/paucablop/chemotools/blob/82826e4d17c03d84a91e3650fb86662eadba3593/chemotools/baseline/_base.py#L114C1-L115C1)
There was a problem hiding this comment.
Yes, this is something that I should have done. I'll add this right away! :)
| xp = self._pad_1d(x, m) | ||
| return np.convolve(xp, self.kernel_, mode="valid") # same length as x | ||
|
|
||
| def _pad_1d(self, x: np.ndarray, m: int) -> np.ndarray: |
There was a problem hiding this comment.
I think this is really nice! 🤩
| if self.alpha <= 0: | ||
| raise ValueError("alpha must be positive.") | ||
|
|
||
| # ---- 1) Eq. 5: index -> normalized coordinate in [-1, 1] ---- |
| dtype=np.float64, | ||
| ) | ||
| # solve the system using linear algebra. | ||
| rhs = np.array([1.0, 0.0, 0.0], dtype=np.float64) |
| @@ -0,0 +1,200 @@ | |||
| # Authors: Nusret Emirhan Salli <nusret.emirhan.salli@gmail.com> | |||
There was a problem hiding this comment.
On top of the file, could you add this?
"""
The :mod:chemotools.smooth._modified_sinc_smoother module implements the Modified Sinc Filter (SGF) transformation.
"""
There was a problem hiding this comment.
I will do that! I totally forgot this.
There was a problem hiding this comment.
Very nice Nusret!! 💪 I checked with the reference and it is great and easy to follow!! Thanks a lot for the clear and good implementation!
| ), | ||
| dtype=np.float64, | ||
| ) | ||
| except Exception: |
There was a problem hiding this comment.
I would like to drop the note that this computation can lead to numerical problems, especially for higher polynomial degrees and window sizes (found to be useful here).
The same problem applies to scipy (see this issue).
For fixing this problem, the algorithm needs to be adapted (see here).
Just a side note, no need for immediate action 😅
This PR tries to close #121 - I don't think it is needed to be squashed before merging, but ultimately, this can be done if needed.
Description of the implementation
Based on the issue, it was suggested that a base FIR class was to be created, since Savitzky-Golay- and the Modified Sinc Smoother both belong to the same type of filter (which is Finite Impulse Response filter). The implementation has been carried out by creating/updating the following files:
chemotools/smooth/_Base.pyhas been updated to include the new class_BaseFIRFilterused for the Savtizky-Golay filter as well as the modified sinc smootherchemotools/smooth/_modified_sinc_smoother.pyhas been created to implement the modified sinc smoother based on the following paper: Schmid, M.; Rath, D.; Diebold, U. "Why and How Savitzky–Golay Filters Should Be Replaced." ACS measurement science Au 2022, 2 (2), 185-196.chemotools/smooth/_savitzky_golay_filter.pyhas been updated to have it use the_BaseFIRFilterclass.tests/smooth/test_modified_sinc.pyhave been added to test the functionality of the smoother.Note: I realized that while the file was created to test Saviztky-Golay filter, it didn't have any. For now I haven't added any, but if needed, I can do that 😃
Results from the implementation
I've gone ahead and tried to test / highlight the changes (or for Savtizky-Golay's case; no changes 😆 ) to make sure the implementations can be used as intended. This has been done by using data from chemotools - namely the fermentation and coffee data.
To make sure that the functionality of Savtizky-Golay works as intended, the implementation from Scipy has been used as a "benchmark". From there I simply extract the kernels from both scipy's Savitzky-Golay filter and mine and compare them. I've also added the kernel for modified sinc smoother (see below):
Zooming in, we can see that the kernels are identical (this have been verified numerically as well but isn't as interesting as figures).
If there is anything, let me know! 😄