{"id":18046598,"url":"https://github.com/ltla/cppweightedlowess","last_synced_at":"2025-04-10T04:44:11.144Z","repository":{"id":48510913,"uuid":"377398701","full_name":"LTLA/CppWeightedLowess","owner":"LTLA","description":"A standalone C++ implementation of limma::weightedLowess.","archived":false,"fork":false,"pushed_at":"2024-09-06T00:16:42.000Z","size":1205,"stargazers_count":8,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T06:01:59.966Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ltla.github.io/CppWeightedLowess/","language":"C++","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/LTLA.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":"2021-06-16T06:47:43.000Z","updated_at":"2024-09-06T00:16:46.000Z","dependencies_parsed_at":"2024-07-21T08:31:11.204Z","dependency_job_id":"4eaab3be-a2af-4c52-8569-564d2f243721","html_url":"https://github.com/LTLA/CppWeightedLowess","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LTLA%2FCppWeightedLowess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LTLA%2FCppWeightedLowess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LTLA%2FCppWeightedLowess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LTLA%2FCppWeightedLowess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LTLA","download_url":"https://codeload.github.com/LTLA/CppWeightedLowess/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248161232,"owners_count":21057552,"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":[],"created_at":"2024-10-30T19:08:04.709Z","updated_at":"2025-04-10T04:44:11.119Z","avatar_url":"https://github.com/LTLA.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Weighted LOWESS for C++\n\n![Unit tests](https://github.com/LTLA/CppWeightedLowess/actions/workflows/run-tests.yaml/badge.svg)\n![Documentation](https://github.com/LTLA/CppWeightedLowess/actions/workflows/doxygenate.yaml/badge.svg)\n![Limma comparison](https://github.com/LTLA/CppWeightedLowess/actions/workflows/compare-limma.yaml/badge.svg)\n[![Codecov](https://codecov.io/gh/LTLA/CppWeightedLowess/branch/master/graph/badge.svg?token=GBHWVK9MFY)](https://codecov.io/gh/LTLA/CppWeightedLowess)\n\n## Overview\n\nThis C++ library implements the Locally Weighted Scatterplot Smoothing (LOWESS) method described by Cleveland (1979, 1981).\nLOWESS is a non-parametric smoothing algorithm that is simple and computationally efficient yet can accommodate a wide variety of curves.\nThe libary itself is header-only and thus can be easily used in any C++ project by adding the relevant `#include` directives.\nThis implementation is derived from the [**limma**](https://bioconductor.org/packages/limma/) package and contains some modifications from Cleveland's original code.\nOf particular interest is the ability to treat the weights as frequencies such that they are involved in the window span calculations for each anchor point - hence the name.\n\n## Algorithm details\n\nCleveland's original Fortran implementation is quite simple:\n\n1. Define a set of anchor points, more-or-less evenly spaced throughout the range of x-values.\n2. Perform a local linear regression around each anchor, using only points within a window centered at each anchor.\nIn this regression, points inside the window are weighted based on their distances from the anchor on the x-axis.\n(If additional sets of weights are provided, the product of weights is used for each point.)\nEach anchor's window must be wide enough to contain a given proportion of all points.\n4. Interpolate to obtain smoothed values for all points in between two anchors.\n5. Compute a robustness weight for each point based on the absolute value of its residual from the fitted value.\nPoints with very large residuals are considered outliers and are assigned zero weight.\n6. Repeat steps 1-5 using the computed weights in each anchor's local linear regression.\nThis is iterated several times to eliminate the effect of outliers on the fit.\n\nIn `limma::weightedLowess`, we implemented some (optional) modifications that are also available in this library.\nSee the [`?weightedLowess` documentation](https://rdrr.io/bioc/limma/man/weightedLowess.html) for more details.\n\n- If additional weights are specified, we allow them to be interpreted as frequencies.\nAs a result, the weights are used in determining the width of the smoothing window around each anchor, in addition to their usual role in the local linear regression.\n- The `delta` value can be automatically determined from a pre-specified number of anchor points.\nThis provides a convenient way of controlling the approximation fidelity.\n\nIn this library, we implement some further modifications from `limma::weightedLowess`:\n\n- The number of robustness iterations in this library refers to additional iterations beyond the first fit,\nwhile the number of robustness iterations in `limma::weightedLowess` includes the first fit.\nSo 3 iterations here are equivalent to 4 iterations in `limma::weightedLowess`.\n- We omit the early termination condition when the MAD of the residuals is much lower than the mean.\nThis avoids inappropriate termination of the robustness iterations in pathological scenarios.\n- We provide extra protection for the edge case where the robustness weighting causes the sum of weights in a window to be zero.\nIn such cases, we simply ignore the robustness weights when computing the smoothed value.\n\n## Usage\n\nUsing this library is as simple as including the header file in your source code:\n\n```cpp\n#include \"WeightedLowess/WeightedLowess.hpp\"\n\n// ... standard boilerplate here...\n\nWeightedLowess::Options opt;\nauto results = WeightedLowess::compute(num_points, x, y, opt);\nresults.fitted;\n```\n\nWe can set options via the `WeightedLowess::Options` object:\n\n```cpp\nopt.span = 0.5;\nopt.anchors = 100;\nauto results2 = WeightedLowess::compute(num_points, x, y, opt);\n```\n\nIf users already have an appropriate buffer for the fitted values and robustness weights, they can be filled directly with the results:\n\n```cpp\nstd::vector\u003cdouble\u003e fitted(num_points), rweights(num_points);\nWeightedLowess::compute(num_points, x, y, fitted.data(), rweights.data(), opt);\n```\n\nWe can also pre-compute the window locations from `x` to re-use them with different `y`, e.g., for smoothing different dimensions with a common covariate.\n\n```cpp\nauto xwindows = WeightedLowess::define_windows(num_points, x, opt);\nWeightedLowess::compute(num_points, x, xwindows, y, fitted.data(), resids.data(), opt);\nWeightedLowess::compute(num_points, x, xwindows, y2, fitted2.data(), resids2.data(), opt);\n// etc.\n```\n\nThe `compute()` function assumes that the input x-coordinates are already sorted.\nIf this is not the case, we can use the `SortBy` class to sort the input and unsort the output:\n\n```cpp\n// Permuting the x/y pairs so that x is sorted:\nWeightedLowess::SortBy sorter(num_points, x);\nstd::vector\u003cuint8_t\u003e workspace;\nsorter.permute(x, workspace);\nsorter.permute(y, workspace);\n\nauto res_unsrt = WeightedLowess::compute(num_points, x, y, opt);\n\n// Unpermuting the fitted values and robustness weights to match\n// the original ordering of x/y pairs.\nsorter.unpermute(res_unsrt.fitted.data(), workspace);\nsorter.unpermute(res_unsrt.robust_weights.data(), workspace);\n```\n\nSee the [reference documentation](https://ltla.github.io/CppWeightedLowess) for more details.\n\n## Building projects\n\n### CMake with `FetchContent`\n\nIf you're already using CMake, you can add something like this to your `CMakeLists.txt`:\n\n```cmake\ninclude(FetchContent)\n\nFetchContent_Declare(\n  WeightedLowess \n  GIT_REPOSITORY https://github.com/LTLA/CppWeightedLowess\n  GIT_TAG master # or any version of interest\n)\n\nFetchContent_MakeAvailable(tatami)\n```\n\nAnd then:\n\n```cmake\n# For executables:\ntarget_link_libraries(myexe WeightedLowess)\n\n# For libaries\ntarget_link_libraries(mylib INTERFACE WeightedLowess)\n```\n\n### CMake with `find_package()`\n\nYou can install the library by cloning a suitable version of this repository and running the following commands:\n\n```sh\nmkdir build \u0026\u0026 cd build\ncmake .. -DWEIGHTEDLOWESS_TESTS=OFF\ncmake --build . --target install\n```\n\nThen you can use `find_package()` as usual:\n\n```cmake\nfind_package(ltla_WeightedLowess CONFIG REQUIRED)\ntarget_link_libraries(mylib INTERFACE ltla::WeightedLowess)\n```\n\nBy default, this will use `FetchContent` to fetch all external dependencies.\nIf you want to install them manually, use `-DWEIGHTEDLOWESS_FETCH_EXTERN=OFF`.\nSee [`extern/CMakeLists.txt`](extern/CMakeLists.txt) to find compatible versions of each dependency.\n\n### Manual\n\nIf you're not using CMake, the simple approach is to just copy the files in the [`include/`](include) subdirectory - \neither directly or with Git submodules - and include their path during compilation with, e.g., GCC's `-I`.\nThis requires the external dependencies listed in [`extern/CMakeLists.txt`](extern/CMakeLists.txt), which also need to be made available during compilation.\n\n## References \n\nCleveland, W.S. (1979).\nRobust locally weighted regression and smoothing scatterplots. \n_Journal of the American Statistical Association_ 74(368), 829-836.\n\nCleveland, W.S. (1981). \nLOWESS: A program for smoothing scatterplots by robust locally weighted regression. \n_The American Statistician_ 35(1), 54.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fltla%2Fcppweightedlowess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fltla%2Fcppweightedlowess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fltla%2Fcppweightedlowess/lists"}