{"id":20989864,"url":"https://github.com/robtillaart/runningmedian","last_synced_at":"2025-05-14T18:32:21.133Z","repository":{"id":37541181,"uuid":"256252421","full_name":"RobTillaart/RunningMedian","owner":"RobTillaart","description":"Arduino library to determine the running median by means of a circular buffer.","archived":false,"fork":false,"pushed_at":"2024-04-13T09:11:50.000Z","size":65,"stargazers_count":45,"open_issues_count":0,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-08-07T18:27:14.359Z","etag":null,"topics":["arduino","median","running"],"latest_commit_sha":null,"homepage":"","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/RobTillaart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"RobTillaart","custom":"https://www.paypal.me/robtillaart"}},"created_at":"2020-04-16T15:17:50.000Z","updated_at":"2024-04-16T22:03:35.000Z","dependencies_parsed_at":"2023-11-21T16:38:13.253Z","dependency_job_id":"47553391-45cf-4465-83cb-c1478cefd7d1","html_url":"https://github.com/RobTillaart/RunningMedian","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FRunningMedian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FRunningMedian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FRunningMedian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FRunningMedian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/RunningMedian/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225305752,"owners_count":17453440,"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":["arduino","median","running"],"created_at":"2024-11-19T06:26:30.013Z","updated_at":"2024-11-19T06:26:30.633Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart","https://www.paypal.me/robtillaart"],"categories":[],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/RunningMedian/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/RunningMedian/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/RunningMedian/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/RunningMedian/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/RunningMedian/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/RunningMedian.svg)](https://github.com/RobTillaart/RunningMedian/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/RunningMedian/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/RunningMedian.svg?maxAge=3600)](https://github.com/RobTillaart/RunningMedian/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/RunningMedian.svg)](https://registry.platformio.org/libraries/robtillaart/RunningMedian)\n\n\n# RunningMedian\n\nArduino library to determine the running median by means of a circular buffer.\n\n\n## Description\n\nRunning Median looks like a running average with a small but important twist.\nRunning average averages the last N samples while the running median takes \nthe last N samples, sort them and take the middle one, or the average of the \nmiddle two in case the internal buffer size is even.\n\nImportant differences between running average and running median:\n- Running median will return real data (e.g. a real sample from a sensor) \nif one uses an odd size of the buffer (therefore preferred).\nRunning average may return a value that is never sampled.\n- Running median will give zero weight to outliers, and 100% to the middle sample, \nwhereas running average gives the same weight to all samples.\n- Running median will give often constant values for some time.\n- As one knows the values in the buffer one can predict the maximum change of \nthe running median in the next steps in advance.\n- Running median is slower as one needs to keep the values in timed order \nto remove the oldest and keep them sorted to be able to select the median.\n\n\n#### Note: MEDIAN_MAX_SIZE\n\nThe maximum size of the internal buffer is defined by **MEDIAN_MAX_SIZE** and is \nset to 255 (since version 0.3.1). The memory allocated currently is in the order\nof 5 bytes per element plus some overhead, so 255 elements take ~1300 bytes.\nFor an UNO this is quite a bit.\n\nWith larger sizes the performance penalty to keep the internal array sorted \nis large. For most applications a value much lower e.g. 19 is working well, and \nis performance wise O(100x) faster in sorting than 255 elements.\n\n\n#### Note: Configurable Options\n\nThere are several options that can be configured via defines at compile time, those being:\n- **RUNNING_MEDIAN_USE_MALLOC**: bool\n  - true (default): Dynamic memory allocation is used for the buffer.\n  - false: Static buffers of size MEDIAN_MAX_SIZE are used.\n- **MEDIAN_MIN_SIZE**: uint8_t\n  - Dynamic / Static: The buffer stores at least this many items.\n  - should be minimal 3.\n- **MEDIAN_MAX_SIZE**: uint8_t\n  - Dynamic: Not used.\n  - Static: The buffer stores at most this many items.\n\n\n#### Related\n\n- https://github.com/RobTillaart/Correlation\n- https://github.com/RobTillaart/GST - Golden standard test metrics\n- https://github.com/RobTillaart/Histogram\n- https://github.com/RobTillaart/RunningAngle\n- https://github.com/RobTillaart/RunningAverage\n- https://github.com/RobTillaart/RunningMedian\n- https://github.com/RobTillaart/statHelpers - combinations \u0026 permutations\n- https://github.com/RobTillaart/Statistic\n\n\n## Interface\n\n```cpp\n#include \"RunningMedian.h\"\n```\n\n\n#### Constructor\n\n- **RunningMedian(const uint8_t size)** Constructor, dynamically allocates memory.\n- **~RunningMedian()** Destructor.\n- **uint8_t getSize()** returns size of internal array.\n- **uint8_t getCount()** returns current used elements, getCount() \u003c= getSize().\n- **bool isFull()** returns true if the internal buffer is 100% filled.\n\n\n#### Base functions\n\n- **clear()** resets internal buffer and variables, effectively empty the buffer.\n- **add(const float value)** adds a new value to internal buffer, \noptionally replacing the oldest element if the buffer is full.\n- **float getMedian()** returns the median == middle element.\n- **float getAverage()** returns average of **all** the values in the internal buffer.\n- **float getAverage(uint8_t nMedian)** returns average of **the middle n** values. \nThis effectively removes noise from the outliers in the samples.\nThe function is improved in 0.3.8 to correct a bias, see #22.\n- **float getMedianAverage(uint8_t nMedian)** almost same as above, \nexcept it compensates for alignment bias, see #22. \nThis is done by adjusting the nMedian parameter (-1 or +1) if needed.\n- **float getHighest()** get the largest values in the buffer.\n- **float getLowest()** get the smallest value in the buffer.\n- **float getQuantile(const float quantile)** returns the Quantile value from the buffer. \nThis value is often interpolated.\n\n\n#### getMedianAverage(nMedian)\n \n**getAverage(nMedian)** and **getMedianAverage(uint8_t nMedian)** differ.\nWhen nMedian is odd and count is even or vice versa, the middle N are not \nperfectly in the middle. \nBy auto-adjusting nMedian (-1 +1) this balance is restored.\n\nAssume an internal size of 7 elements \\[0..6] then \n- **getAverage(4)** will average element 1, 2, 3, 4\n- **getMedianAverage(4)** will adjust nMedian and average element 2, 3, 4.\n\nThe example **RunningMedian_getMedianAverage.ino** shows the difference.\n\nThe implementation of **getMedianAverage(uint8_t nMedian)** is experimental \nand might change in the future. \nIdea is taking top and bottom elements only for 50% if needed, however that \nimplies at least 2 extra float multiplications.\n\nIt is possible that the name **getMedianAverage(uint8_t nMedian)**\nwill change in the future to be more descriptive.\n\n\n#### Less used functions\n\n- **float getElement(const uint8_t n)** returns the n'th element from the values in time order.\n- **float getSortedElement(const uint8_t n)** returns the n'th element from the values in size order (sorted ascending).\n- **float predict(const uint8_t n)** predict the maximum change of median after n additions, \nn must be smaller than **getSize()/2**.\n\n\n#### SearchMode optimization\n\nSince 0.3.7 the internal sort has been optimized.\nIt is now possible to select between LINEAR (=0) and BINARY (=1) insertion sort.\nPre-0.3.7 used linear insertion sort, and the new linear version is slightly optimized.\nFor larger internal arrays the performance gain of BINARY mode is substantial.\n\n- **void setSearchMode(uint8_t searchMode = 0)** 0 = linear, 1 = binary - see table below.\nOther values will set the searchMode to linear.\n- **uint8_t getSearchMode()** returns the set mode\n\n|  searchMode  |  value  | notes  |\n|:------------:|:-------:|:-------|\n|    LINEAR    |    0    |  fastest for smaller internal buffers (default)\n|    BINARY    |    1    |  faster for larger internal buffers\n\nDepends on the board / clock used where the methods are equally fast.\n\nGive it a try, and let me know your.\n\n\n## Operation\n\nSee examples.\n\n\n## Future\n\n#### Must\n\n- improve documentation.\n\n#### Should\n\n#### Could\n\n- check for optimizations.\n  - get the median without (full) sorting. QuickSelect()\n- move all code to .cpp file\n\n\n## Support\n\nIf you appreciate my libraries, you can support the development and maintenance.\nImprove the quality of the libraries by providing issues and Pull Requests, or\ndonate through PayPal or GitHub sponsors.\n\nThank you,\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtillaart%2Frunningmedian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobtillaart%2Frunningmedian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtillaart%2Frunningmedian/lists"}