{"id":23749433,"url":"https://github.com/sigma-py/smoothfit","last_synced_at":"2025-08-10T14:19:49.959Z","repository":{"id":37580018,"uuid":"127912691","full_name":"sigma-py/smoothfit","owner":"sigma-py","description":"Smooth data fitting in N dimensions.","archived":false,"fork":false,"pushed_at":"2023-03-10T11:42:19.000Z","size":1009,"stargazers_count":55,"open_issues_count":5,"forks_count":14,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-23T05:44:02.332Z","etag":null,"topics":["data-fitting","mathematics","python"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sigma-py.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2018-04-03T13:29:18.000Z","updated_at":"2025-03-10T02:43:38.000Z","dependencies_parsed_at":"2024-12-31T15:29:09.667Z","dependency_job_id":null,"html_url":"https://github.com/sigma-py/smoothfit","commit_stats":{"total_commits":2,"total_committers":2,"mean_commits":1.0,"dds":0.5,"last_synced_commit":"72a759946f340f9d908c7e9e7f0cdc2bc8ad6a3f"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/sigma-py/smoothfit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigma-py%2Fsmoothfit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigma-py%2Fsmoothfit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigma-py%2Fsmoothfit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigma-py%2Fsmoothfit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sigma-py","download_url":"https://codeload.github.com/sigma-py/smoothfit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigma-py%2Fsmoothfit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269734993,"owners_count":24466769,"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","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["data-fitting","mathematics","python"],"created_at":"2024-12-31T15:18:55.195Z","updated_at":"2025-08-10T14:19:49.915Z","avatar_url":"https://github.com/sigma-py.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/nschloe/smoothfit\"\u003e\u003cimg alt=\"smoothfit\" src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/logo/logo.svg\" width=\"60%\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n[![PyPi Version](https://img.shields.io/pypi/v/smoothfit.svg?style=flat-square)](https://pypi.org/project/smoothfit)\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/smoothfit.svg?style=flat-square)](https://pypi.org/pypi/smoothfit/)\n[![GitHub stars](https://img.shields.io/github/stars/nschloe/smoothfit.svg?style=flat-square\u0026logo=github\u0026label=Stars\u0026logoColor=white)](https://github.com/nschloe/smoothfit)\n[![PyPi downloads](https://img.shields.io/pypi/dm/smoothfit.svg?style=flat-square)](https://pypistats.org/packages/smoothfit)\n\n[![Discord](https://img.shields.io/static/v1?logo=discord\u0026label=chat\u0026message=on%20discord\u0026color=7289da\u0026style=flat-square)](https://discord.gg/hnTJ5MRX2Y)\n\nGiven experimental data, it is often desirable to produce a function whose\nvalues match the data to some degree. This package implements a robust approach\nto data fitting based on the minimization problem\n\n```math\n\\|\\lambda\\Delta f\\|^2_{L^2(\\Omega)} + \\sum_i (f(x_i) - y_i)^2 \\to\\min\n```\n\n(A similar idea is used in for data smoothing in signal processing; see, e.g.,\nsection 8.3 in [this\ndocument](http://eeweb.poly.edu/iselesni/lecture_notes/least_squares/least_squares_SP.pdf).)\n\nUnlike [polynomial\nregression](https://en.wikipedia.org/wiki/Polynomial_regression) or\n[Gauss-Newton](https://en.wikipedia.org/wiki/Gauss%E2%80%93Newton_algorithm\u003cPaste\u003e),\nsmoothfit makes no assumptions about the function other than that it is smooth.\n\nThe generality of the approach makes it suitable for function whose domain is\nmultidimensional, too.\n\n### Pics or it didn't happen\n\n#### Runge's example\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/runge.webp\" width=\"60%\"\u003e\n\n[Runge's example function](https://en.wikipedia.org/wiki/Runge%27s_phenomenon) is a\ntough nut for classical polynomial regression.\n\nIf there is no noise in the input data, the parameter `lmbda` can be chosen quite small\nsuch that all data points are approximated well. Note that there are no oscillations in\nthe output function `u`.\n\n```python\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport smoothfit\n\na = -1.5\nb = +1.5\n\n# plot original function\nx = np.linspace(a, b, 201)\nplt.plot(x, 1 / (1 + 25 * x ** 2), \"-\", color=\"0.8\", label=\"1 / (1 + 25 * x**2)\")\n\n# sample points\nx0 = np.linspace(-1.0, 1.0, 21)\ny0 = 1 / (1 + 25 * x0 ** 2)\nplt.plot(x0, y0, \"xk\")\n\n# smoothfit\nbasis, coeffs = smoothfit.fit1d(x0, y0, a, b, 1000, degree=1, lmbda=1.0e-6)\nplt.plot(basis.mesh.p[0], coeffs[basis.nodal_dofs[0]], \"-\", label=\"smooth fit\")\n\nplt.ylim(-0.1)\nplt.grid()\nplt.show()\n```\n\n#### Runge's example with noise\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/runge-noise-lambda.webp\" width=\"60%\"\u003e\n\nIf the data is noisy, `lmbda` needs to be chosen more carefully. If too small, the\napproximation tries to resolve _all_ data points, resulting in many small oscillations.\nIf it's chosen too large, no details are resolved, not even those of the underlying\ndata.\n\n```python\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport smoothfit\n\na = -1.5\nb = +1.5\n\n# plot original function\nx = np.linspace(a, b, 201)\nplt.plot(x, 1 / (1 + 25 * x ** 2), \"-\", color=\"0.8\", label=\"1 / (1 + 25 * x**2)\")\n\n# 21 sample points\nrng = np.random.default_rng(0)\nn = 51\nx0 = np.linspace(-1.0, 1.0, n)\ny0 = 1 / (1 + 25 * x0 ** 2)\ny0 += 1.0e-1 * (2 * rng.random(n) - 1)\nplt.plot(x0, y0, \"xk\")\n\nlmbda = 5.0e-2\nbasis, coeffs = smoothfit.fit1d(x0, y0, a, b, 1000, degree=1, lmbda=lmbda)\nplt.plot(basis.mesh.p[0], coeffs[basis.nodal_dofs[0]], \"-\", label=\"smooth fit\")\n\nplt.grid()\nplt.show()\n```\n\n#### Few samples\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/smoothfit-samples.svg\" width=\"40%\"\u003e\n\n```python\nimport numpy as np\nimport smoothfit\n\nx0 = np.array([0.038, 0.194, 0.425, 0.626, 1.253, 2.500, 3.740])\ny0 = np.array([0.050, 0.127, 0.094, 0.2122, 0.2729, 0.2665, 0.3317])\nu = smoothfit.fit1d(x0, y0, 0, 4, 1000, degree=1, lmbda=1.0)\n```\n\nSome noisy example data taken from\n[Wikipedia](https://en.wikipedia.org/wiki/Gauss%E2%80%93Newton_algorithm#Example).\n\n#### A two-dimensional example\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/2d.png\" width=\"40%\"\u003e\n\n```python\nimport meshzoo\nimport numpy as np\nimport smoothfit\n\nn = 200\nrng = np.random.default_rng(123)\nx0 = rng.random((n, 2)) - 0.5\ny0 = np.cos(np.pi * np.sqrt(x0.T[0] ** 2 + x0.T[1] ** 2))\n\n# create a triangle mesh for the square\npoints, cells = meshzoo.rectangle_tri(\n    np.linspace(-1.0, 1.0, 32), np.linspace(-1.0, 1.0, 32)\n)\n\nbasis, u = smoothfit.fit(x0, y0, points, cells, lmbda=1.0e-4, solver=\"dense-direct\")\n\n# Write the function to a file\nbasis.mesh.save(\"out.vtu\", point_data={\"u\": u})\n```\n\nThis example approximates a function from _R\u003csup\u003e2\u003c/sup\u003e_ to _R_ (without noise in the\nsamples). Note that the absence of noise the data allows us to pick a rather small\n`lmbda` such that all sample points are approximated well.\n\n### Comparison with other approaches\n\n#### Polynomial fitting/regression\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/runge-polyfit.webp\" width=\"60%\"\u003e\n\nThe classical approach to data fitting is [polynomial\nregression](https://en.wikipedia.org/wiki/Polynomial_regression). Polynomials are\nchosen because they are very simple, can be evaluated quickly, and [can be made to fit\nany function very closely](https://en.wikipedia.org/wiki/Stone–Weierstrass_theorem).\n\nThere are, however, some fundamental problems:\n\n- Your data might not actually fit a polynomial of low degree.\n- [Runge's phenomenon](//en.wikipedia.org/wiki/Runge%27s_phenomenon).\n\nThis above plot highlights the problem with oscillations.\n\n#### Fourier smoothing\n\n\u003cimg src=\"https://raw.githubusercontent.com/sigma-py/smoothfit/main/plots/fourier.svg\" width=\"60%\"\u003e\n\nOne approach to data fitting with smoothing is to create a function with all data\npoints, and simply cut off the high frequencies after Fourier transformation.\n\nThis approach is fast, but only works for evenly spaced samples.\n\n\u003e [For equidistant curve fitting there is nothing else that could compete with the\n\u003e Fourier series.](https://youtu.be/avSHHi9QCjA?t=1543)\n\u003e -- Cornelius Lanczos\n\n```python\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\nrng = np.random.default_rng(0)\n\n# original function\nx0 = np.linspace(-1.0, 1.0, 1000)\ny0 = 1 / (1 + 25 * x0 ** 2)\nplt.plot(x0, y0, color=\"k\", alpha=0.2)\n\n# create sample points\nn = 51\nx1 = np.linspace(-1.0, 1.0, n)  # only works if samples are evenly spaced\ny1 = 1 / (1 + 25 * x1 ** 2) + 1.0e-1 * (2 * rng.random(x1.shape[0]) - 1)\nplt.plot(x1, y1, \"xk\")\n\n# Cut off the high frequencies in the transformed space and transform back\nX = np.fft.rfft(y1)\nX[5:] = 0.0\ny2 = np.fft.irfft(X, n)\n#\nplt.plot(x1, y2, \"-\", label=\"5 lowest frequencies\")\n\nplt.grid()\nplt.show()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigma-py%2Fsmoothfit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsigma-py%2Fsmoothfit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigma-py%2Fsmoothfit/lists"}