{"id":14977858,"url":"https://github.com/mothnik/robust_fourier","last_synced_at":"2026-01-04T08:34:43.796Z","repository":{"id":247141599,"uuid":"824511639","full_name":"MothNik/robust_fourier","owner":"MothNik","description":"Noise- and Outlier-Robust Fourier Transform with Hermite Functions with NumPy and Numba","archived":false,"fork":false,"pushed_at":"2024-09-17T18:12:06.000Z","size":29405,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T12:44:09.663Z","etag":null,"topics":["chebyshev-polynomials","fourier-transform","hermite-functions","hermite-polynomials","least-square-regression","logsumexp","numba","numpy","python","robust-statistics"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MothNik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-05T09:37:20.000Z","updated_at":"2024-09-05T07:16:16.000Z","dependencies_parsed_at":"2024-08-08T22:26:05.247Z","dependency_job_id":"61e1f451-1aad-4928-9a24-b89980e5bea4","html_url":"https://github.com/MothNik/robust_fourier","commit_stats":{"total_commits":14,"total_committers":1,"mean_commits":14.0,"dds":0.0,"last_synced_commit":"e2d3b61404cfcd0030a36c1f8ad6a1e3874fd547"},"previous_names":["mothnik/robust_hermite_ft","mothnik/robust_fourier"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MothNik%2Frobust_fourier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MothNik%2Frobust_fourier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MothNik%2Frobust_fourier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MothNik%2Frobust_fourier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MothNik","download_url":"https://codeload.github.com/MothNik/robust_fourier/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238608268,"owners_count":19500350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chebyshev-polynomials","fourier-transform","hermite-functions","hermite-polynomials","least-square-regression","logsumexp","numba","numpy","python","robust-statistics"],"created_at":"2024-09-24T13:56:27.201Z","updated_at":"2025-10-28T07:30:28.134Z","avatar_url":"https://github.com/MothNik.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `robust_fourier`\n\n[![python-3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/)\n[![python-3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/)\n[![python-3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)\n[![python-3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)\n[![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![code style: isort](https://img.shields.io/badge/code%20style-isort-000000.svg)](https://pycqa.github.io/isort/)\n[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)\n[![codecov](https://codecov.io/gh/MothNik/robust_fourier/branch/10-improve-and-add-coverage-to-CI/graph/badge.svg)](https://codecov.io/gh/MothNik/robust_fourier/branch/10-improve-and-add-coverage-to-CI)\n![tests](https://github.com/MothNik/robust_fourier/actions/workflows/python-package.yml/badge.svg)\n\u003cbr\u003e\u003cbr\u003e\n\nYou want to compute the Fourier transform of a signal, but your signal can be corrupted by outliers? If so, this package is for you even though you will have to say goodbye to the _\"fast\"_ in _Fast Fourier Transform_ 🏃🙅‍♀️\n\n## 🎁 Installation\n\n### 🐍☁️ PyPI\n\nThe package can be installed from PyPI with\n\n```bash\npip install robust_fourier\n```\n\nIf speed matters for you, you can also install the package with the optional dependency\n`numba`\n\n```bash\npip install robust_fourier[fast]\n```\n\n### 🐙📦 GitHub\n\nTo install the package from GitHub, you can simply clone the repository\n\n```bash\ngit clone https://github.com/MothNik/robust_fourier.git\n```\n\nFor the following commands, a `Makefile` is provided to simplify the process. Its use is\noptional, but recommended.\u003cbr\u003e\nFrom within the repositories root directory, the package can be installed for normal use\n\n```bash\n# ⚠️ first, activate your virtual environment, e.g., source venv/bin/activate\n\nmake install\n# equivalent to\npip install --upgrade .\n```\n\nor for development (with all the development dependencies)\n\n```bash\n# ⚠️ first, activate your virtual environment, e.g., source venv/bin/activate\n\nmake install-dev\n# equivalent to\npip install --upgrade .[\"dev\"]\n```\n\n## ⚙️ Setup and 🪛 Development\n\nWhen working in developer mode, an environment variable has to be added to run certain\nscripts.\n\n```\nROBFT_DEVELOPER = true\n```\n\n### 🔎 Code quality\n\nThe following checks for `black`, `isort`, `pyright`, `mypy`, `pycodestyle`, and\n`ruff` - that are also part of the CI pipeline - can be run with\n\n```bash\nmake black-check\nmake isort-check\nmake pyright-check\nmake mypy-check\nmake pycodestyle-check\nmake ruff-check\n\n# or for all at once\nmake check\n\n# equivalent to\nblack --check --diff --color ./auxiliary_scripts ./examples ./src ./tests\nisort --check --diff --color ./auxiliary_scripts ./examples ./src ./tests\npyright ./auxiliary_scripts ./examples ./src ./tests\nmypy ./auxiliary_scripts ./examples ./src ./tests\nruff check ./auxiliary_scripts ./examples ./src ./tests\npycodestyle ./auxiliary_scripts ./examples ./src ./tests --max-line-length=88 --ignore=E203,W503,E704\n```\n\n### ✅❌ Tests\n\nTo run the tests - almost like in the CI pipeline - you can use\n\n```bash\nmake test-xmlcov  # for an XML report\nmake test-htmlcov  # for an HTML report\n\n# equivalent to\npytest --cov=robust_fourier ./tests -n=\"auto\" --cov-report=xml -x --no-jit\npytest --cov=robust_fourier ./tests -n=\"auto\" --cov-report=html -x --no-jit\n```\n\nfor parallelized testing whose coverage report will be stored in the file\n`./coverage.xml` or in the folder `./htmlcov`, respectively.\n\n## 〰️ Hermite functions\n\nBeing the eigenfunctions of the Fourier transform, Hermite functions are excellent\ncandidates for the basis functions for a Least Squares Regression approach to the Fourier\ntransform. However, their evaluation can be a bit tricky.\n\nThe module `hermite_functions` offers a numerically stable way to evaluate Hermite\nfunctions or arbitrary order $n$ and argument - that can be scaled with a factor\n$\\alpha$ and shifted by a constant $\\mu$:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/EX-01-DilatedHermiteFunctions_DifferentScales.png\" width=\"1000px\" /\u003e\n\u003c/p\u003e\n\nAfter a slight modification of the definitions in [[1]](#references), the Hermite\nfunctions can be written as\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/equations/HF-01-Hermite_Functions_TimeSpace_Domain.svg\" /\u003e\n\u003c/p\u003e\n\nwith the Hermite polynomials\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/equations/HF-02-Hermite_Polynomials_TimeSpace_Domain.svg\" /\u003e\n\u003c/p\u003e\n\nWith `robust_fourier`, the Hermite functions can be evaluated for arbitrary orders\nusing the function interface `hermite_function_vander`\n\n```python\nimport numpy as np\nfrom robust_fourier import hermite_function_vander\n\nORDER_MAX = 25  # the maximum order of the Hermite functions\nALPHA = 2.0  # the scaling factor for the x-variable\nMU = -2.0  # the shift of the x-variable\n\nX_FROM = -20.0\nX_TO = 20.0\nNUM_X = 10_001\n\nx_values = np.linspace(start=X_FROM + MU, stop=X_TO + MU, num=NUM_X)\nhermite_vander = hermite_function_vander(\n    x=x_values,\n    n=ORDER_MAX,\n    alpha=ALPHA,\n    x_center=MU,\n    jit=True,  # will only take effect if Numba is installed\n)\n```\n\nBy making use of logarithm tricks, the evaluation that might involve infinitely high\npolynomial values and at the same time infinitely small Gaussians - that are on top of\nthat scaled by an infinitely high factorial - can be computed safely and yield accurate\nresults.\n\nFor doing so, the relation between the dilated and the non-dilated Hermite functions\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/equations/HF-03-Hermite_Functions_Dilated_to_Undilated.svg\" /\u003e\n\u003c/p\u003e\n\nand the recurrence relation for the Hermite functions\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/equations/HF-04-Hermite_Functions_Recurrence_Relation.svg\" /\u003e\n\u003c/p\u003e\n\nare used, but not directly. Instead, the latest evaluated Hermite function is kept at a\nvalue of either -1, 0, or +1 during the recursion and the logarithm of a correction\nfactor is tracked and applied when the respective Hermite function is finally evaluated\nand stored. This approach is based on [[2]](#references).\n\nThe implementation is tested against a symbolic evaluation with `sympy` that uses 200\ndigits of precision and it can be shown that even orders as high as 2,000 can still be\ncomputed even though neither the polynomial, the Gaussian nor the factorial can be\nevaluated for this anymore. The factorial for example would already have overflown for\norders of 170 in `float64`-precision.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/EX-02-DilatedHermiteFunctions_Stability.png\" width=\"1000px\" /\u003e\n\u003c/p\u003e\n\nAs a sanity check, their orthogonality is part of the tests together with a test for\nthe fact that the absolute values of the Hermite functions for real input cannot exceed\nthe value $\\frac{1}{\\sqrt[4]{\\pi\\cdot\\alpha^{2}}}$.\n\nOn top of that `robust_fourier` comes with utility functions to approximate some\nspecial points of the Hermite functions, namely the x-positions of their\n\n- largest root (= outermost zero),\n- largest extrema in the outermost oscillation,\n- the point where they numerically fade to zero, and\n- an approximation of the outermost oscillation (tail) by a conservative Gaussian peak.\n\n```python\nimport numpy as np\nfrom robust_fourier import hermite_approx\n\nORDER = 25  # the order of the Hermite functions\nALPHA = 20.0  # the scaling factor for the x-variable\nMU = 150.0  # the shift of the x-variable\n\nX_FROM = -65.0\nX_TO = 65.0\nNUM_X = 100_001\n\n# 1) the x-positions at which the outermost oscillation fades below machine\n# precision\nx_fadeout = hermite_approx.x_fadeout(\n    n=ORDER,\n    alpha=ALPHA,\n    x_center=MU,\n)\n# 2) the x-positions of the largest zeros\nx_largest_zero = hermite_approx.x_largest_zeros(\n    n=ORDER,\n    alpha=ALPHA,\n    x_center=MU,\n)\n# 3) the x-positions of the largest extrema\nx_largest_extremum = hermite_approx.x_largest_extrema(\n    n=ORDER,\n    alpha=ALPHA,\n    x_center=MU,\n)\n\n# 4) the Gaussian approximation of the outermost oscillation ...\nleft_gaussian, right_gaussian = hermite_approx.get_tail_gauss_fit(\n    n=ORDER,\n    alpha=ALPHA,\n    x_center=MU,\n)\n# ... which is solved for the 50% level\nx_left_fifty_percent = left_gaussian.solve_for_y_fraction(y_fraction=0.5)\nx_right_fifty_percent = right_gaussian.solve_for_y_fraction(y_fraction=0.5)\n# ... but can also be evaluated for all x-values\nx_values = np.linspace(start=X_FROM + MU, stop=X_TO + MU, num=NUM_X)\nleft_gaussian_values = left_gaussian(x=x_values)\nright_gaussian_values = right_gaussian(x=x_values)\n\n# 5) the Gaussian approximation is also solved for the 1% interval as a more\n# realistic (less conservative) approximation of the fadeout point\nx_one_percent = hermite_approx.x_tail_drop_to_fraction(\n    n=ORDER,\n    y_fraction=0.01,\n    alpha=ALPHA,\n    x_center=MU,\n).ravel()\n\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/hermite_functions/EX-04-HermiteFunctions_SpecialPoints.png\" width=\"1000px\" /\u003e\n\u003c/p\u003e\n\n## 🧮 Chebyshev Polynomials\n\nEven though the [Hermite functions](#〰️-hermite-functions) have some nice properties,\nthey are not necessarily the best choice for the Fourier transform. Choosing their\nscaling parameter $\\alpha$ can be a bit tricky.\nTherefore [[3]](#references) suggests using Chebyshev polynomials instead. They are\nonly defined on the interval $[-1, 1]$ and can be scaled and shifted to fit the\ninterval $[\\mu - \\alpha, \\mu + \\alpha]$ like\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/chebyshev_polynomials/equations/CP-01-Chebyshev_Polynomials_Recurrence_Relation_First_Kind.svg\" /\u003e\n\nfor the first kind and\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/chebyshev_polynomials/equations/CP-02-Chebyshev_Polynomials_Recurrence_Relation_Second_Kind.svg\" /\u003e\n\nfor the second kind. In [[3]](#references) the second kind $U$ is used, but the first\nkind $T$ is also implemented in `robust_fourier`\n\n```python\nimport numpy as np\nfrom robust_fourier import chebyshev_polyvander\n\nORDER_MAX = 10  # the maximum order of the Chebyshev polynomials\nALPHA = 0.5  # the scaling factor for the x-variable\nMU = 0.5  # the shift of the x-variable\n\nX_FROM = -0.5\nX_TO = 0.5\nNUM_X = 10_001\n\nx_values = np.linspace(start=X_FROM + MU, stop=X_TO + MU, num=NUM_X)\nchebyshev_vander_first_kind = chebyshev_polyvander(\n    x=x_values,\n    n=ORDER_MAX,\n    alpha=ALPHA,\n    x_center=MU,\n    kind=\"first\",\n    jit=True,  # will only take effect if Numba is installed\n)\n\nchebyshev_vander_second_kind = chebyshev_polyvander(\n    x=x_values,\n    n=ORDER_MAX,\n    alpha=ALPHA,\n    x_center=MU,\n    kind=\"second\",\n    jit=True,  # will only take effect if Numba is installed\n)\n\n# alternatively, both kinds can be computed in one go because this is how they are\n# computed internally to achieve maximum accuracy\n(\n  chebyshev_vander_first_kind,\n  chebyshev_vander_second_kind,\n) = chebyshev_polyvander(\n    x=x_values,\n    n=ORDER_MAX,\n    alpha=ALPHA,\n    x_center=MU,\n    kind=\"both\",\n    jit=True,  # will only take effect if Numba is installed\n)\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/MothNik/robust_fourier/main/docs/chebyshev_polynomials/EX-05-DilatedChebyshevPolynomials_DifferentScales.png\" width=\"1000px\" /\u003e\n\u003c/p\u003e\n\n## 📈 Fourier Transform\n\n🏗️🚧 👷👷‍♂️👷‍♀️🏗️🚧\n\nCurrently under construction. Please check back later.\n\n## 🙏 Acknowledgements\n\nThis package would not have been possible without the - unfortunately apparently\nabandoned - package [`hermite-functions`](https://github.com/Rob217/hermite-functions)\nwhich was a great inspiration for the implementation of the Hermite functions.\n\nOn top of that, I hereby want to thank the anonymous support that patiently listened to\nmy endless talks about the greatness of Hermite functions (even though they cannot keep\nup with her) and that also helped me to give the plots the visual appeal they have now\n🤩.\n\n## 📖 References\n\n- [1] Dobróka M., Szegedi H., and Vass P., Inversion-Based Fourier Transform as a New\n  Tool for Noise Rejection, _Fourier Transforms - High-tech Application and Current Trends_\n  (2017), DOI: [http://dx.doi.org/10.5772/66338](http://dx.doi.org/10.5772/66338)\n- [2] Bunck B. F., A fast algorithm for evaluation of normalized Hermite functions,\n  _BIT Numer Math_ (2009), 49, pp. 281–295, DOI:\n  [https://doi.org/10.1007/s10543-009-0216-1](https://doi.org/10.1007/s10543-009-0216-1)\n- [3] Al Marashly, O., Dobróka, M., Chebyshev polynomial-based Fourier transformation\n  and its use in low pass filter of gravity data, _Acta Geod Geophys_ (2024), 59,\n  pp. 159–181 DOI: [https://doi.org/10.1007/s40328-024-00444-z](https://doi.org/10.1007/s40328-024-00444-z)\n- [4] Hrycak T., Schmutzhard S., Accurate evaluation of Chebyshev polynomials in\n  floating-point arithmetic, _BIT Numer Math_ (2019), 59, pp. 403–416,\n  DOI: [https://doi.org/10.1007/s10543-018-0738-5](https://doi.org/10.1007/s10543-018-0738-5)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmothnik%2Frobust_fourier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmothnik%2Frobust_fourier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmothnik%2Frobust_fourier/lists"}