{"id":22344235,"url":"https://github.com/hi-folks/statistics","last_synced_at":"2026-05-13T08:02:08.743Z","repository":{"id":37742284,"uuid":"445609326","full_name":"Hi-Folks/statistics","owner":"Hi-Folks","description":"PHP package that provides functions for calculating mathematical statistics of numeric data.","archived":false,"fork":false,"pushed_at":"2026-05-12T20:50:54.000Z","size":846,"stargazers_count":400,"open_issues_count":0,"forks_count":29,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-12T21:29:17.592Z","etag":null,"topics":["hacktoberfest","math","php","statistics"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/hi-folks/statistics","language":"PHP","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/Hi-Folks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-07T17:59:48.000Z","updated_at":"2026-05-12T20:51:01.000Z","dependencies_parsed_at":"2026-05-13T08:01:13.383Z","dependency_job_id":null,"html_url":"https://github.com/Hi-Folks/statistics","commit_stats":{"total_commits":136,"total_committers":5,"mean_commits":27.2,"dds":0.2647058823529411,"last_synced_commit":"156f17f834d2db0125b0ca499c26bc35b0e71881"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/Hi-Folks/statistics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fstatistics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fstatistics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fstatistics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fstatistics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hi-Folks","download_url":"https://codeload.github.com/Hi-Folks/statistics/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fstatistics/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32973318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T06:31:55.726Z","status":"ssl_error","status_checked_at":"2026-05-13T06:31:51.336Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["hacktoberfest","math","php","statistics"],"created_at":"2024-12-04T09:09:14.087Z","updated_at":"2026-05-13T08:02:08.735Z","avatar_url":"https://github.com/Hi-Folks.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://repository-images.githubusercontent.com/445609326/e2539776-0f8f-4556-be1d-887ea2368813\" alt=\"PHP package for Statistics\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e\n    Statistics PHP package\n\u003c/h1\u003e\n\n\u003cp align=center\u003e\n    \u003ca href=\"https://packagist.org/packages/hi-folks/statistics\"\u003e\n        \u003cimg src=\"https://img.shields.io/packagist/v/hi-folks/statistics.svg?style=for-the-badge\" alt=\"Latest Version on Packagist\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/hi-folks/statistics\"\u003e\n        \u003cimg src=\"https://img.shields.io/packagist/dt/hi-folks/statistics.svg?style=for-the-badge\" alt=\"Total Downloads\"\u003e\n    \u003c/a\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://github.com/Hi-Folks/statistics/blob/main/.github/workflows/static-code-analysis.yml\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/PHPStan-level%208-brightgreen.svg?style=for-the-badge\" alt=\"Static Code analysis\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/packagist/l/hi-folks/statistics?style=for-the-badge\" alt=\"Packagist License\"\u003e\n    \u003cbr\u003e\n    \u003cimg src=\"https://img.shields.io/packagist/php-v/hi-folks/statistics?style=for-the-badge\" alt=\"Packagist PHP Version Support\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/last-commit/hi-folks/statistics?style=for-the-badge\" alt=\"GitHub last commit\"\u003e\n\u003c/p\u003e\n\n\u003cp align=center\u003e\n    \u003ca href=\"https://github.com/hi-folks/statistics/actions/workflows/run-tests.yml\"\u003e\n        \u003cimg src=\"https://github.com/hi-folks/statistics/actions/workflows/run-tests.yml/badge.svg?branch=main\u0026style=for-the-badge\" alt=\"Tests\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=center\u003e\n    \u003ci\u003e\n        A PHP package for descriptive statistics, normal distribution, outlier detection, and streaming analytics on numeric data.\n    \u003c/i\u003e\n\u003c/p\u003e\n\nThis package provides a comprehensive set of statistical functions for PHP: descriptive statistics (mean, median, mode, standard deviation, variance, quantiles), robust measures (trimmed mean, weighted median, median absolute deviation), distribution modelling (normal distribution with PDF, CDF, and inverse CDF), outlier detection (z-score and IQR-based), z-scores, percentiles, coefficient of variation, frequency tables, correlation, regression (linear, logarithmic, power, and exponential), kernel density estimation, and O(1) memory streaming statistics.\n\nIt works with any numeric dataset — from sports telemetry and sensor data to race results, survey responses, and financial time series.\n\n**Articles and resources:**\n- [Exploring Olympic Downhill Results with PHP Statistics](https://dev.to/robertobutti/exploring-olympic-downhill-results-with-php-statistics-3eo1) — a step-by-step analysis of 2026 Olympic downhill race data\n- [Statistics with PHP](https://dev.to/robertobutti/statistics-with-php-4pfp) — introduction to the package and its core functions\n- [PHP Statistics on Laravel News](https://laravel-news.com/php-statistics)\n\n\u003e This package is inspired by the [Python statistics module](https://docs.python.org/3/library/statistics.html)\n\n## Installation\n\nYou can install the package via composer:\n\n```bash\ncomposer require hi-folks/statistics\n```\n\n## Usage\n\n### Stat class\n\nStat class has methods to calculate an average or typical value from a population or sample.\nThis class provides methods for calculating mathematical statistics of numeric data.\nThe various mathematical statistics are listed below:\n\n\n| Mathematical Statistic | Description |\n| ---------------------- | ----------- |\n| `mean()` | arithmetic mean or \"average\" of data |\n| `fmean()` | floating-point arithmetic mean, with optional weighting and precision |\n| `trimmedMean()` | trimmed (truncated) mean — mean after removing outliers from each side |\n| `median()` | median or \"middle value\" of data |\n| `weightedMedian()` | weighted median — median with weights, where each value has a different importance |\n| `medianLow()` | low median of data |\n| `medianHigh()` | high median of data |\n| `medianGrouped()` | median of grouped data, using interpolation |\n| `mode()` | single mode (most common value) of discrete or nominal data |\n| `multimode()` | list of modes (most common values) of discrete or nominal data |\n| `quantiles()` | cut points dividing the range of a probability distribution into continuous intervals with equal probabilities (supports `exclusive` and `inclusive` methods) |\n| `thirdQuartile()` | 3rd quartile, is the value at which 75 percent of the data is below it |\n| `firstQuartile()` | first quartile, is the value at which 25 percent of the data is below it |\n| `percentile()` | value at any percentile (0–100) with linear interpolation |\n| `rank()` | rank each data point, with configurable tie handling |\n| `percentileRank()` | percentile position of a value within a dataset |\n| `pstdev()` | Population standard deviation |\n| `stdev()` | Sample standard deviation |\n| `sem()` | Standard error of the mean (SEM) — measures precision of the sample mean |\n| `meanAbsoluteDeviation()` | mean absolute deviation (MAD) — average distance from the mean |\n| `medianAbsoluteDeviation()` | median absolute deviation — median distance from the median, robust to outliers |\n| `pvariance()` | variance for a population (supports pre-computed mean via `mu`) |\n| `variance()` | variance for a sample (supports pre-computed mean via `xbar`) |\n| `skewness()` | adjusted Fisher-Pearson sample skewness |\n| `pskewness()` | population (biased) skewness |\n| `kurtosis()` | excess kurtosis (sample formula, 0 for normal distribution) |\n| `coefficientOfVariation()` | coefficient of variation (CV%), relative dispersion as percentage |\n| `zscores()` | z-scores for each value — how many standard deviations from the mean |\n| `outliers()` | outlier detection based on z-score threshold |\n| `iqrOutliers()` | outlier detection based on IQR method (box plot whiskers), robust for skewed data |\n| `geometricMean()` | geometric mean |\n| `harmonicMean()` | harmonic mean |\n| `correlation()` | Pearson’s, Spearman’s rank, or Kendall tau correlation coefficient for two inputs |\n| `kendallTau()` | Kendall tau-b rank correlation coefficient for ordinal association with tie handling |\n| `covariance()` | the sample covariance of two inputs |\n| `linearRegression()` | return the slope and intercept of simple linear regression parameters estimated using ordinary least squares (supports `proportional: true` for regression through the origin) |\n| `logarithmicRegression()` | logarithmic regression — fits `y = a × ln(x) + b`, ideal for diminishing returns patterns (e.g., athletic improvement, learning curves) |\n| `powerRegression()` | power regression — fits `y = a × x^b`, useful for power law relationships |\n| `exponentialRegression()` | exponential regression — fits `y = a × e^(b×x)`, useful for exponential growth or decay |\n| `rSquared()` | coefficient of determination (R²) — proportion of variance explained by linear regression |\n| `confidenceInterval()` | confidence interval for the mean using the normal (z) distribution |\n| `zTest()` | one-sample Z-test — tests whether the sample mean differs significantly from a hypothesized population mean |\n| `tTest()` | one-sample t-test — like z-test but appropriate for small samples where the population standard deviation is unknown |\n| `tTestTwoSample()` | two-sample independent t-test (Welch's) — compares the means of two independent groups without assuming equal variances |\n| `tTestPaired()` | paired t-test — tests whether the mean difference between paired observations is significantly different from zero |\n| `chiSquaredTest()` | chi-squared goodness-of-fit test for observed vs expected category counts |\n| `chiSquaredIndependence()` | chi-squared test of independence for contingency tables |\n| `kde()` | kernel density estimation — returns a closure that estimates the probability density (or CDF) at any point |\n| `kdeRandom()` | random sampling from a kernel density estimate — returns a closure that generates random floats from the KDE distribution |\n\n#### Stat::mean( array $data )\nReturn the sample arithmetic mean of the array _$data_.\nThe arithmetic mean is the sum of the data divided by the number of data points. It is commonly called “the average”, although it is only one of many mathematical averages. It is a measure of the central location of the data.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mean = Stat::mean([1, 2, 3, 4, 4]);\n// 2.8\n$mean = Stat::mean([-1.0, 2.5, 3.25, 5.75]);\n// 2.625\n```\n\n#### Stat::fmean( array $data, array|null $weights = null, int|null $precision = null )\nReturn the arithmetic mean of the array `$data`, as a float, with optional weights and precision control.\nThis function behaves like `mean()` but ensures a floating-point result and supports weighted datasets.\nIf `$weights` is provided, it computes the weighted average. The result is rounded to a given decimal $precision.\nThe result is rounded to `$precision` decimal places. \nIf `$precision` is null, no rounding is applied — this may lead to results with long or unexpected decimal expansions due to the nature of floating-point arithmetic in PHP. Using rounding helps ensure cleaner, more predictable output.\n\n```php \nuse HiFolks\\Statistics\\Stat;\n\n// Unweighted mean (same as mean but always float)\n$fmean = Stat::fmean([3.5, 4.0, 5.25]);\n// 4.25\n\n// Weighted mean\n$fmean = Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1]);\n// 4.1875\n\n// Custom precision\n$fmean = Stat::fmean([3.5, 4.0, 5.25], null, 2);\n// 4.25\n\n$fmean = Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1], 3);\n// 4.188\n\n```\n\nIf the input is empty, or weights are invalid (e.g., length mismatch or sum is zero), an exception is thrown.\nUse this function when you need floating-point accuracy or to apply custom weighting and rounding to your average.\n\n#### Stat::trimmedMean( array $data, float $proportionToCut = 0.1, ?int $round = null )\nReturn the trimmed (truncated) mean of the data. Computes the mean after removing the lowest and highest fraction of values. This is a robust measure of central tendency, less sensitive to outliers than the regular mean.\n\nThe `$proportionToCut` parameter specifies the fraction to trim from **each** side (must be in the range `[0, 0.5)`). For example, `0.1` removes the bottom 10% and top 10%.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mean = Stat::trimmedMean([1, 2, 3, 4, 5, 6, 7, 8, 9, 100], 0.1);\n// 5.5 (outlier 100 and lowest value 1 removed)\n\n$mean = Stat::trimmedMean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0.2);\n// 5.5 (removes 2 values from each side)\n\n$mean = Stat::trimmedMean([1, 2, 3, 4, 5], 0.0);\n// 3.0 (no trimming, same as regular mean)\n```\n\n#### Stat::geometricMean( array $data )\nThe geometric mean indicates the central tendency or typical value of the data using the product of the values (as opposed to the arithmetic mean which uses their sum).\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mean = Stat::geometricMean([54, 24, 36], 1);\n// 36.0\n```\n#### Stat::harmonicMean( array $data )\nThe harmonic mean is the reciprocal of the arithmetic mean() of the reciprocals of the data. For example, the harmonic mean of three values a, b, and c will be equivalent to 3/(1/a + 1/b + 1/c). If one of the values is zero, the result will be zero.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mean = Stat::harmonicMean([40, 60], null, 1);\n// 48.0\n```\n\nYou can also calculate the harmonic weighted mean.\nSuppose a car travels 40 km/hr for 5 km, and when traffic clears, speeds up to 60 km/hr for the remaining 30 km of the journey. What is the average speed?\n\n```php\nuse HiFolks\\Statistics\\Stat;\nStat::harmonicMean([40, 60], [5, 30], 1);\n// 56.0\n```\nwhere:\n- 40, 60:  are the elements\n- 5, 30: are the weights for each element (the first weight is the weight of the first element, the second one is the weight of the second element)\n- 1: is the decimal numbers you want to round\n\n\n#### Stat::median( array $data )\nReturn the median (middle value) of numeric data, using the common “mean of middle two” method.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$median = Stat::median([1, 3, 5]);\n// 3\n$median = Stat::median([1, 3, 5, 7]);\n// 4\n```\n\n#### Stat::weightedMedian( array $data, array $weights, ?int $round = null )\nReturn the weighted median of the data. The weighted median is the value where the cumulative weight reaches 50% of the total weight. This is useful for survey data, financial analysis, or any dataset where observations have different importance.\n\nAll weights must be positive numbers and the weights array must have the same length as the data array.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$median = Stat::weightedMedian([1, 2, 3], [1, 1, 1]);\n// 2.0 (equal weights, same as regular median)\n\n$median = Stat::weightedMedian([1, 2, 3], [1, 1, 10]);\n// 3.0 (heavy weight on 3 pulls the median)\n\n$median = Stat::weightedMedian([1, 2, 3, 4], [1, 1, 1, 1]);\n// 2.5 (equal weights, even count — averages the two middle values)\n```\n\n#### Stat::medianLow( array $data )\nReturn the low median of numeric data.\nThe low median is always a member of the data set. When the number of data points is odd, the middle value is returned. When it is even, the smaller of the two middle values is returned.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$median = Stat::medianLow([1, 3, 5]);\n// 3\n$median = Stat::medianLow([1, 3, 5, 7]);\n// 3\n```\n\n\n\n#### Stat::medianHigh( array $data )\nReturn the high median of data.\nThe high median is always a member of the data set. When the number of data points is odd, the middle value is returned. When it is even, the larger of the two middle values is returned.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$median = Stat::medianHigh([1, 3, 5]);\n// 3\n$median = Stat::medianHigh([1, 3, 5, 7]);\n// 5\n```\n\n#### Stat::medianGrouped( array $data, float $interval = 1.0 )\nEstimate the median for numeric data that has been grouped or binned around the midpoints of consecutive, fixed-width intervals.\nThe `$interval` parameter specifies the width of each bin (default `1.0`). This function uses interpolation within the median interval, assuming values are evenly distributed across each bin.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$median = Stat::medianGrouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5]);\n// 3.7\n$median = Stat::medianGrouped([1, 3, 3, 5, 7]);\n// 3.25\n$median = Stat::medianGrouped([1, 3, 3, 5, 7], 2);\n// 3.5\n```\n\nFor example, demographic data summarized into ten-year age groups:\n```php\nuse HiFolks\\Statistics\\Stat;\n// 172 people aged 20-30, 484 aged 30-40, 387 aged 40-50, etc.\n$data = array_merge(\n    array_fill(0, 172, 25),\n    array_fill(0, 484, 35),\n    array_fill(0, 387, 45),\n    array_fill(0, 22, 55),\n    array_fill(0, 6, 65),\n);\nround(Stat::medianGrouped($data, 10), 1);\n// 37.5\n```\n\n#### Stat::quantiles( array $data, $n=4, $round=null, $method='exclusive'  )\nDivide data into n continuous intervals with equal probability. Returns a list of n - 1 cut points separating the intervals.\nSet n to 4 for quartiles (the default). Set n to 10 for deciles. Set n to 100 for percentiles which gives the 99 cut points that separate data into 100 equal-sized groups.\n\nThe `$method` parameter controls the interpolation method:\n- `'exclusive'` (default): uses `m = count + 1`. Suitable for sampled data that may have more extreme values beyond the sample.\n- `'inclusive'`: uses `m = count - 1`. Suitable for population data or samples known to include the most extreme values. The minimum value is treated as the 0th percentile and the maximum as the 100th percentile.\n\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$quantiles = Stat::quantiles([98, 90, 70,18,92,92,55,83,45,95,88]);\n// [ 55.0, 88.0, 92.0 ]\n$quantiles = Stat::quantiles([105, 129, 87, 86, 111, 111, 89, 81, 108, 92, 110,100, 75, 105, 103, 109, 76, 119, 99, 91, 103, 129,106, 101, 84, 111, 74, 87, 86, 103, 103, 106, 86,111, 75, 87, 102, 121, 111, 88, 89, 101, 106, 95,103, 107, 101, 81, 109, 104], 10);\n// [81.0, 86.2, 89.0, 99.4, 102.5, 103.6, 106.0, 109.8, 111.0]\n\n// Inclusive method\n$quantiles = Stat::quantiles([1, 2, 3, 4, 5], method: 'inclusive');\n// [2.0, 3.0, 4.0]\n```\n#### Stat::firstQuartile( array $data, $round=null  )\nThe lower quartile, or first quartile (Q1), is the value under which 25% of data points are found when they are arranged in increasing order.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$percentile = Stat::firstQuartile([98, 90, 70,18,92,92,55,83,45,95,88]);\n// 55.0\n```\n\n#### Stat::thirdQuartile( array $data, $round=null  )\nThe upper quartile, or third quartile (Q3), is the value under which 75% of data points are found when arranged in increasing order.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$percentile = Stat::thirdQuartile([98, 90, 70,18,92,92,55,83,45,95,88]);\n// 92.0\n```\n\n#### Stat::percentile( array $data, float $p, ?int $round = null )\nReturn the value at the given percentile of the data, using linear interpolation between adjacent data points (exclusive method, consistent with `quantiles()`).\n\nThe percentile `$p` must be between 0 and 100. Requires at least 2 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$value = Stat::percentile([10, 20, 30, 40, 50, 60, 70, 80, 90, 100], 50);\n// 55.0 (median)\n\n$value = Stat::percentile([10, 20, 30, 40, 50, 60, 70, 80, 90, 100], 90);\n// 91.0\n```\n\n#### Stat::rank( array $data, string $method = Stat::RANK_AVERAGE )\nReturn 1-based ranks for each data point, preserving the original array keys.\n\nThe `$method` parameter controls how tied values are ranked:\n- `Stat::RANK_AVERAGE` (default): tied values receive the average rank.\n- `Stat::RANK_MIN`: tied values receive the lowest rank in the tied group.\n- `Stat::RANK_MAX`: tied values receive the highest rank in the tied group.\n- `Stat::RANK_DENSE`: tied values receive the same rank and ranks do not skip numbers.\n- `Stat::RANK_ORDINAL`: tied values are ranked by their sorted order, preserving input order inside ties.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n$ranks = Stat::rank([10, 20, 20, 30]);\n// [1, 2.5, 2.5, 4]\n\n$ranks = Stat::rank([10, 20, 20, 30], Stat::RANK_DENSE);\n// [1, 2, 2, 3]\n```\n\n#### Stat::percentileRank( array $data, int|float $value, string $kind = Stat::PERCENTILE_RANK_WEAK, ?int $round = null )\nReturn the percentile position of a value within a dataset.\n\nThe `$kind` parameter controls the calculation:\n- `Stat::PERCENTILE_RANK_WEAK` (default): percentage of values less than or equal to the value.\n- `Stat::PERCENTILE_RANK_STRICT`: percentage of values strictly less than the value.\n- `Stat::PERCENTILE_RANK_MEAN`: average of weak and strict percentile ranks.\n- `Stat::PERCENTILE_RANK_RANK`: average percentage rank for exact matches, falling back to mean when the value is absent.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n$rank = Stat::percentileRank([10, 20, 20, 30, 40], 20);\n// 60.0\n\n$rank = Stat::percentileRank([10, 20, 20, 30, 40], 20, Stat::PERCENTILE_RANK_STRICT);\n// 20.0\n```\n\n#### Stat::pstdev( array $data )\nReturn the **Population** Standard Deviation, a measure of the amount of variation or dispersion of a set of values.\nA low standard deviation indicates that the values tend to be close to the mean of the set, while a high standard deviation indicates that the values are spread out over a wider range.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$stdev = Stat::pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]);\n// 0.986893273527251\n$stdev = Stat::pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75], 4);\n// 0.9869\n```\n\n#### Stat::stdev( array $data )\nReturn the **Sample** Standard Deviation, a measure of the amount of variation or dispersion of a set of values.\nA low standard deviation indicates that the values tend to be close to the mean of the set, while a high standard deviation indicates that the values are spread out over a wider range.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$stdev = Stat::stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]);\n// 1.0810874155219827\n$stdev = Stat::stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75], 4);\n// 1.0811\n```\n\n#### Stat::sem( array $data, ?int $round = null )\nReturn the standard error of the mean (SEM). SEM measures how precisely the sample mean estimates the population mean. It decreases as the sample size grows.\n\nFormula: `stdev / sqrt(n)`\n\nRequires at least 2 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$sem = Stat::sem([2, 4, 4, 4, 5, 5, 7, 9]);\n// 0.7559...\n\n$sem = Stat::sem([2, 4, 4, 4, 5, 5, 7, 9], 4);\n// 0.7559\n```\n\n#### Stat::meanAbsoluteDeviation( array $data, ?int $round = null )\nReturn the mean absolute deviation (MAD) — the average of the absolute deviations from the mean.\n\nMAD is a simple, intuitive measure of dispersion: it tells you \"on average, how far values are from the mean\". Unlike standard deviation, it does not square the differences, making it easier to interpret and somewhat less sensitive to outliers.\n\nUse MAD when you want a straightforward, interpretable measure of spread, especially for reporting to non-technical audiences.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mad = Stat::meanAbsoluteDeviation([1, 2, 3, 4, 5]);\n// 1.2\n\n$mad = Stat::meanAbsoluteDeviation([1, 2, 3, 4, 5], 1);\n// 1.2\n```\n\n#### Stat::medianAbsoluteDeviation( array $data, ?int $round = null )\nReturn the median absolute deviation — the median of the absolute deviations from the median.\n\nThis is one of the most **robust measures of dispersion** available. Because it uses the median (not the mean) as the center and takes the median (not the mean) of deviations, it is highly resistant to outliers. Even if up to half the data points are extreme, the median absolute deviation remains stable.\n\nUse it when your data may contain outliers, when you need a robust alternative to standard deviation, or for outlier detection (values far from the median in units of MAD are likely outliers).\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$mad = Stat::medianAbsoluteDeviation([1, 2, 3, 4, 5]);\n// 1.0\n\n// Robust to outliers — the outlier 1000 does not affect the result:\n$mad = Stat::medianAbsoluteDeviation([1, 2, 3, 4, 1000]);\n// 1.0\n```\n\n#### Stat::variance ( array $data, ?int $round = null, int|float|null $xbar = null)\nVariance is a measure of dispersion of data points from the mean.\nLow variance indicates that data points are generally similar and do not vary widely from the mean.\nHigh variance indicates that data values have greater variability and are more widely dispersed from the mean.\n\nTo calculate the variance from a *sample*:\n```php\nuse HiFolks\\Statistics\\Stat;\n$variance = Stat::variance([2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]);\n// 1.3720238095238095\n```\n\nIf you have already computed the mean, you can pass it via `xbar` to avoid recalculation:\n```php\n$data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5];\n$mean = Stat::mean($data);\n$variance = Stat::variance($data, xbar: $mean);\n```\n\nIf you need to calculate the variance on the whole population and not just on a sample you need to use *pvariance* method. You can optionally pass the population mean via `mu`:\n```php\nuse HiFolks\\Statistics\\Stat;\n$variance = Stat::pvariance([0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]);\n// 1.25\n\n// With pre-computed mean:\n$data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25];\n$mu = Stat::mean($data);\n$variance = Stat::pvariance($data, mu: $mu);\n```\n\n\n#### Stat::skewness ( array $data, ?int $round = null )\nSkewness is a measure of the asymmetry of a distribution. The adjusted Fisher-Pearson formula is used, which is the same as Excel's `SKEW()` and Python's `scipy.stats.skew(bias=False)`.\n\nA positive skewness indicates a right-skewed distribution (tail extends to the right), while a negative skewness indicates a left-skewed distribution. A symmetric distribution has a skewness of 0.\n\nRequires at least 3 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$skewness = Stat::skewness([1, 2, 3, 4, 5]);\n// 0.0 (symmetric)\n\n$skewness = Stat::skewness([1, 1, 1, 1, 1, 10]);\n// positive (right-skewed)\n```\n\nIf you need the population (biased) skewness instead of the sample skewness, use `pskewness()`. This is equivalent to `scipy.stats.skew(bias=True)`:\n```php\nuse HiFolks\\Statistics\\Stat;\n$pskewness = Stat::pskewness([1, 1, 1, 1, 1, 10]);\n```\n\n#### Stat::kurtosis ( array $data, ?int $round = null )\nKurtosis measures the \"tailedness\" of a distribution — how much data lives in the extreme tails compared to a normal distribution. This method returns the **excess kurtosis** using the sample formula, which is the same as Excel's `KURT()` and Python's `scipy.stats.kurtosis(bias=False)`.\n\nA normal distribution has excess kurtosis of 0. Positive values (leptokurtic) indicate heavier tails and more outliers. Negative values (platykurtic) indicate lighter tails and fewer outliers.\n\nRequires at least 4 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$kurtosis = Stat::kurtosis([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);\n// negative (platykurtic, lighter tails than normal)\n\n$kurtosis = Stat::kurtosis([1, 2, 2, 2, 2, 2, 2, 2, 2, 50]);\n// positive (leptokurtic, heavier tails due to outlier)\n```\n\n#### Stat::coefficientOfVariation( array $data, ?int $round = null, bool $population = false )\nThe coefficient of variation (CV) is the ratio of the standard deviation to the mean, expressed as a percentage. It measures relative variability and is useful for comparing dispersion across datasets with different units or scales.\n\nBy default it uses the sample standard deviation. Pass `population: true` to use the population standard deviation instead.\n\nRequires at least 2 data points (sample) or 1 (population). Throws if the mean is zero.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$cv = Stat::coefficientOfVariation([10, 20, 30, 40, 50]);\n// ~52.70 (sample)\n\n$cv = Stat::coefficientOfVariation([10, 20, 30, 40, 50], round: 2);\n// 52.7\n\n$cv = Stat::coefficientOfVariation([10, 20, 30, 40, 50], population: true);\n// ~47.14 (population)\n```\n\n#### Stat::zscores( array $data, ?int $round = null )\nReturn the z-score for each value in the dataset. A z-score indicates how many standard deviations a value is from the mean. Z-scores are useful for standardizing data, comparing values from different distributions, and identifying outliers.\n\nThe z-scores of any dataset always sum to zero, and values beyond ±2 or ±3 are typically considered unusual or outliers.\n\nRequires at least 2 data points and non-zero standard deviation.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$zscores = Stat::zscores([2, 4, 4, 4, 5, 5, 7, 9]);\n// array of z-scores, one per value\n\n$zscores = Stat::zscores([2, 4, 4, 4, 5, 5, 7, 9], 2);\n// z-scores rounded to 2 decimal places\n```\n\n#### Stat::outliers( array $data, float $threshold = 3.0 )\nReturn values from the dataset that are outliers based on z-score threshold. A value is considered an outlier if its absolute z-score exceeds the threshold.\n\nThe default threshold of 3.0 is a widely used convention — in a normal distribution, about 99.7% of values fall within 3 standard deviations of the mean, so values beyond that are rare. Use a lower threshold (e.g. 2.0) for stricter detection, or a higher one for more lenient filtering.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$outliers = Stat::outliers([1, 2, 3, 4, 5, 6, 7, 8, 9, 100]);\n// [100]\n\n$outliers = Stat::outliers([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1.0);\n// values more than 1 stdev from the mean\n```\n\n#### Stat::iqrOutliers( array $data, float $factor = 1.5 )\nReturn values that are outliers based on the Interquartile Range (IQR) method. A value is an outlier if it falls below `Q1 - factor * IQR` or above `Q3 + factor * IQR`. This is the same method used for box plot whiskers.\n\nUnlike z-score based detection, the IQR method is **robust** — it does not assume a normal distribution and is not influenced by extreme values themselves. This makes it the preferred choice for skewed data or when the dataset may already contain outliers that would distort the mean and standard deviation.\n\nUse `factor: 1.5` (default) for mild outliers, or `factor: 3.0` for extreme outliers only.\n\n**Example: Ski downhill race times**\n\nIn a ski downhill race, most athletes finish between 108–116 seconds. A time of 200s (e.g. a crash/DNF) or 50s (e.g. a timing error) would be flagged as outliers:\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$times = [110.2, 112.5, 108.9, 115.3, 111.7, 114.0, 109.8, 113.6, 200.0, 50.0];\n$outliers = Stat::iqrOutliers($times);\n// [200.0, 50.0] — the crash and the timing error are detected\n\n$extremeOnly = Stat::iqrOutliers($times, 3.0);\n// only the most extreme values\n```\n\n#### Stat::covariance ( array $x , array $y )\nCovariance, static method, returns the sample covariance of two inputs *$x* and *$y*.\nCovariance is a measure of the joint variability of two inputs.\n\n```php\n$covariance = Stat::covariance(\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [1, 2, 3, 1, 2, 3, 1, 2, 3]\n);\n// 0.75\n```\n\n```php\n$covariance = Stat::covariance(\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [9, 8, 7, 6, 5, 4, 3, 2, 1]\n);\n// -7.5\n```\n\n#### Stat::correlation ( array $x , array $y, string $method = 'linear' )\nReturn the Pearson’s correlation coefficient for two inputs. Pearson’s correlation coefficient r takes values between -1 and +1. It measures the strength and direction of the linear relationship, where +1 means very strong, positive linear relationship, -1 very strong, negative linear relationship, and 0 no linear relationship.\n\nUse `$method = 'ranked'` for Spearman’s rank correlation, which measures monotonic relationships (not just linear). Spearman’s correlation is computed by applying Pearson’s formula to the ranks of the data.\n\nUse `$method = 'kendall'` for Kendall tau-b correlation, which measures ordinal association by comparing concordant and discordant pairs. Kendall tau-b is often useful for ordinal data, small samples, and datasets with ties.\n\n```php\n$correlation = Stat::correlation(\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [1, 2, 3, 4, 5, 6, 7, 8, 9]\n);\n// 1.0\n```\n\n```php\n$correlation = Stat::correlation(\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [9, 8, 7, 6, 5, 4, 3, 2, 1]\n);\n// -1.0\n```\n\nSpearman’s rank correlation (non-linear but monotonic relationship):\n```php\n$correlation = Stat::correlation(\n    [1, 2, 3, 4, 5],\n    [1, 4, 9, 16, 25],\n    'ranked'\n);\n// 1.0\n```\n\nKendall tau-b rank correlation with ties:\n```php\n$correlation = Stat::correlation(\n    [12, 2, 1, 12, 2],\n    [1, 4, 7, 1, 0],\n    'kendall'\n);\n// -0.47140452079103173\n```\n\n#### Stat::kendallTau ( array $x , array $y, ?int $round = null )\nReturn Kendall's tau-b rank correlation coefficient for two inputs.\n\n```php\n$correlation = Stat::kendallTau(\n    [12, 2, 1, 12, 2],\n    [1, 4, 7, 1, 0],\n    4\n);\n// -0.4714\n```\n\n#### Stat::linearRegression ( array $x , array $y , bool $proportional = false )\nReturn the slope and intercept of simple linear regression  parameters estimated using ordinary least squares.\nSimple linear regression describes the relationship between an independent variable *$x* and a dependent variable *$y* in terms of a linear function.\n\n```php\n$years = [1971, 1975, 1979, 1982, 1983];\n$films_total = [1, 2, 3, 4, 5]\nlist($slope, $intercept) = Stat::linearRegression(\n    $years,\n    $films_total\n);\n// 0.31\n// -610.18\n```\nWhat happens in 2022, according to the samples above?\n\n```php\nround($slope * 2022 + $intercept);\n// 17.0\n```\n\nWhen `proportional` is `true`, the regression line is forced through the origin (intercept = 0). This is useful when the relationship between *$x* and *$y* is known to be proportional:\n\n```php\nlist($slope, $intercept) = Stat::linearRegression(\n    [1, 2, 3, 4, 5],\n    [2, 4, 6, 8, 10],\n    proportional: true,\n);\n// $slope = 2.0\n// $intercept = 0.0\n```\n\n#### Stat::logarithmicRegression( array $x, array $y )\nFit a logarithmic model **y = a × ln(x) + b**. Returns `[a, b]`.\n\nThis model naturally captures diminishing returns — fast initial change that gradually flattens. It is useful for data where early gains are large but improvement slows over time, such as athletic performance trends, learning curves, or market saturation.\n\nAll x values must be positive (you cannot take the logarithm of zero or negative numbers).\n\nInternally, this transforms x to ln(x) and applies linear regression, so it leverages the same robust ordinary least squares implementation.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n// Simulated weekly running paces (seconds/km) — diminishing improvement\n$weeks = [1, 2, 3, 4, 5, 6, 7, 8];\n$paces = [350, 342, 337, 333, 330, 328, 326, 325];\n\n[$a, $b] = Stat::logarithmicRegression($weeks, $paces);\n// $a = -12.33 (pace drops by 12.33 sec per unit of ln(week))\n// $b = 350.2\n\n// Predict pace at week 12:\n$predicted = $a * log(12) + $b;\n// ~320 seconds = 5:20/km\n```\n\nCompare with linear regression to see which fits better:\n\n```php\n// R² for logarithmic model (transform x first)\n$logWeeks = array_map(fn($v) =\u003e log($v), $weeks);\n$r2Log = Stat::rSquared($logWeeks, $paces);\n// 0.9987\n\n// R² for linear model\n$r2Linear = Stat::rSquared($weeks, $paces);\n// 0.9176\n\n// Logarithmic wins — the data has diminishing returns\n```\n\n#### Stat::powerRegression( array $x, array $y )\nFit a power model **y = a × x^b**. Returns `[a, b]`.\n\nPower regression is useful for data following power law relationships (e.g., scaling laws, allometric relationships). Both x and y values must be positive.\n\nInternally, this linearizes as ln(y) = ln(a) + b × ln(x) and applies linear regression.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n// Data following y = 3 * x^2\n$x = [1, 2, 3, 4, 5];\n$y = [3, 12, 27, 48, 75];\n\n[$a, $b] = Stat::powerRegression($x, $y);\n// $a = 3.0\n// $b = 2.0 (the exponent)\n```\n\n#### Stat::exponentialRegression( array $x, array $y )\nFit an exponential model **y = a × e^(b×x)**. Returns `[a, b]`.\n\nExponential regression is useful for data with exponential growth (positive b) or decay (negative b), such as population growth, compound interest, or radioactive decay. All y values must be positive.\n\nInternally, this linearizes as ln(y) = ln(a) + b × x and applies linear regression.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n// Data following y = 2 * e^(0.5*x)\n$x = [1, 2, 3, 4, 5];\n$y = [3.30, 5.44, 8.96, 14.78, 24.36];\n\n[$a, $b] = Stat::exponentialRegression($x, $y);\n// $a ≈ 2.0\n// $b ≈ 0.5\n```\n\n#### Stat::rSquared( array $x, array $y, bool $proportional = false, ?int $round = null )\nReturn the coefficient of determination (R²) — the proportion of variance in the dependent variable explained by the linear regression model. Values range from 0 (no explanatory power) to 1 (perfect fit).\n\nRequires at least 2 data points and arrays of the same length.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n$r2 = Stat::rSquared([1, 2, 3, 4, 5], [2, 4, 6, 8, 10]);\n// 1.0 (perfect linear relationship)\n\n$r2 = Stat::rSquared(\n    [1971, 1975, 1979, 1982, 1983],\n    [1, 2, 3, 4, 5],\n    round: 2,\n);\n// 0.96\n```\n\nWith proportional regression (through the origin):\n\n```php\n$r2 = Stat::rSquared(\n    [1, 2, 3, 4, 5],\n    [2, 4, 6, 8, 10],\n    proportional: true,\n);\n// 1.0\n```\n\nTo compute R² for non-linear models, transform the data the same way the regression method does:\n\n```php\n// R² for logarithmic regression\n$logX = array_map(fn($v) =\u003e log($v), $x);\n$r2 = Stat::rSquared($logX, $y);\n\n// R² for power regression\n$logX = array_map(fn($v) =\u003e log($v), $x);\n$logY = array_map(fn($v) =\u003e log($v), $y);\n$r2 = Stat::rSquared($logX, $logY);\n\n// R² for exponential regression\n$logY = array_map(fn($v) =\u003e log($v), $y);\n$r2 = Stat::rSquared($x, $logY);\n```\n\n#### Stat::confidenceInterval( array $data, float $confidenceLevel = 0.95, ?int $round = null )\nReturn the confidence interval for the mean using the normal (z) distribution.\n\nComputes: `mean ± z * (stdev / √n)`, where the z-critical value is derived from the inverse normal CDF.\n\nRequires at least 2 data points. The confidence level must be between 0 and 1 exclusive.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n[$lower, $upper] = Stat::confidenceInterval([2, 4, 4, 4, 5, 5, 7, 9]);\n// 95% CI: [3.52, 6.48] (approximately)\n\n[$lower, $upper] = Stat::confidenceInterval([2, 4, 4, 4, 5, 5, 7, 9], confidenceLevel: 0.99);\n// 99% CI: wider interval\n\n[$lower, $upper] = Stat::confidenceInterval([2, 4, 4, 4, 5, 5, 7, 9], round: 2);\n// [3.52, 6.48]\n```\n\n#### Stat::zTest( array $data, float $populationMean, Alternative $alternative = Alternative::TwoSided, ?int $round = null )\nPerform a one-sample Z-test for the mean. Tests whether the sample mean differs significantly from a hypothesized population mean using the normal distribution.\n\nReturns an associative array with `zScore` and `pValue`. The alternative hypothesis can be `TwoSided` (default), `Greater`, or `Less`.\n\nRequires at least 2 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\Alternative;\n\n$result = Stat::zTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0);\n// ['zScore' =\u003e 2.6457..., 'pValue' =\u003e 0.0081...]\n\n$result = Stat::zTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0, alternative: Alternative::Greater);\n// one-tailed test: is the sample mean greater than 3?\n\n$result = Stat::zTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0, round: 4);\n// ['zScore' =\u003e 2.6458, 'pValue' =\u003e 0.0081]\n```\n\n#### Stat::tTest( array $data, float $populationMean, Alternative $alternative = Alternative::TwoSided, ?int $round = null )\nPerform a one-sample t-test for the mean. Tests whether the sample mean differs significantly from a hypothesized population mean using the Student's t-distribution. Unlike the z-test, the t-test is appropriate for small samples where the population standard deviation is unknown.\n\nReturns an associative array with `tStatistic`, `pValue`, and `degreesOfFreedom`. The alternative hypothesis can be `TwoSided` (default), `Greater`, or `Less`.\n\nRequires at least 2 data points.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\Alternative;\n\n$result = Stat::tTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0);\n// ['tStatistic' =\u003e 2.6457..., 'pValue' =\u003e 0.0331..., 'degreesOfFreedom' =\u003e 7]\n\n$result = Stat::tTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0, alternative: Alternative::Greater);\n// one-tailed test: is the sample mean greater than 3?\n\n$result = Stat::tTest([2, 4, 4, 4, 5, 5, 7, 9], populationMean: 3.0, round: 4);\n// ['tStatistic' =\u003e 2.6458, 'pValue' =\u003e 0.0331, 'degreesOfFreedom' =\u003e 7]\n```\n\n#### Stat::tTestTwoSample( array $data1, array $data2, Alternative $alternative = Alternative::TwoSided, ?int $round = null )\nPerform a two-sample independent t-test (Welch's t-test). Compares the means of two independent groups without assuming equal variances. Uses the Welch–Satterthwaite approximation for degrees of freedom.\n\nReturns an associative array with `tStatistic`, `pValue`, and `degreesOfFreedom`. The alternative hypothesis can be `TwoSided` (default), `Greater`, or `Less`.\n\nRequires at least 2 data points in each sample.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\Alternative;\n\n// Compare two groups\n$group1 = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99];\n$group2 = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98];\n$result = Stat::tTestTwoSample($group1, $group2);\n// ['tStatistic' =\u003e 1.6245..., 'pValue' =\u003e 0.1444..., 'degreesOfFreedom' =\u003e 6.84...]\n\n// One-tailed test: is group1 mean greater than group2 mean?\n$result = Stat::tTestTwoSample($group1, $group2, alternative: Alternative::Greater);\n\n// Groups can have different sizes\n$result = Stat::tTestTwoSample([1, 2, 3, 4, 5, 6, 7, 8], [3, 4, 5], round: 4);\n```\n\n#### Stat::tTestPaired( array $data1, array $data2, Alternative $alternative = Alternative::TwoSided, ?int $round = null )\nPerform a paired t-test. Tests whether the mean difference between paired observations (e.g. before/after measurements on the same subjects) is significantly different from zero.\n\nReturns an associative array with `tStatistic`, `pValue`, and `degreesOfFreedom`. Both arrays must have the same length.\n\nRequires at least 2 paired observations.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\Alternative;\n\n// Before and after treatment measurements\n$before = [200, 190, 210, 220, 215, 205, 195, 225];\n$after  = [192, 186, 198, 212, 208, 198, 188, 215];\n$result = Stat::tTestPaired($before, $after);\n// ['tStatistic' =\u003e 5.715..., 'pValue' =\u003e 0.0007..., 'degreesOfFreedom' =\u003e 7]\n\n// One-tailed: did the treatment decrease the values?\n$result = Stat::tTestPaired($before, $after, alternative: Alternative::Greater);\n\n$result = Stat::tTestPaired($before, $after, round: 4);\n```\n\n#### Stat::chiSquaredTest( array $observed, ?array $expected = null, ?int $round = null )\nPerform a chi-squared goodness-of-fit test. Use it when you want to check whether observed category counts differ from expected counts.\n\nIf `$expected` is omitted, a uniform distribution is assumed. Returns an associative array with `chiSquared`, `pValue`, and `degreesOfFreedom`.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n// Are these category counts different from a uniform distribution?\n$result = Stat::chiSquaredTest([8, 12, 10], round: 4);\n// ['chiSquared' =\u003e 0.8, 'pValue' =\u003e 0.6703, 'degreesOfFreedom' =\u003e 2]\n\n// Compare observed counts to expected counts\n$result = Stat::chiSquaredTest([18, 32, 50], [20, 30, 50], round: 4);\n// ['chiSquared' =\u003e 0.3333, 'pValue' =\u003e 0.8465, 'degreesOfFreedom' =\u003e 2]\n```\n\n#### Stat::chiSquaredIndependence( array $table, ?int $round = null )\nPerform a chi-squared test of independence on a contingency table. Use it when you want to check whether two categorical variables are associated.\n\nReturns an associative array with `chiSquared`, `pValue`, `degreesOfFreedom`, and the expected-count table.\n\n```php\nuse HiFolks\\Statistics\\Stat;\n\n$table = [\n    [10, 20, 30],\n    [6, 9, 17],\n];\n\n$result = Stat::chiSquaredIndependence($table, round: 4);\n// [\n//     'chiSquared' =\u003e 0.2716,\n//     'pValue' =\u003e 0.873,\n//     'degreesOfFreedom' =\u003e 2,\n//     'expected' =\u003e [[10.4348, 18.913, 30.6522], [5.5652, 10.087, 16.3478]],\n// ]\n```\n\n#### Stat::kde ( array $data , float $h , KdeKernel $kernel = KdeKernel::Normal , bool $cumulative = false )\nCreate a continuous probability density function (or cumulative distribution function) from discrete sample data using Kernel Density Estimation.\nReturns a `Closure` that can be called with any point to estimate the density (or CDF value).\n\nSupported kernels: `KdeKernel::Normal` (alias `KdeKernel::Gauss`), `KdeKernel::Logistic`, `KdeKernel::Sigmoid`, `KdeKernel::Rectangular` (alias `KdeKernel::Uniform`), `KdeKernel::Triangular`, `KdeKernel::Parabolic` (alias `KdeKernel::Epanechnikov`), `KdeKernel::Quartic` (alias `KdeKernel::Biweight`), `KdeKernel::Triweight`, `KdeKernel::Cosine`.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\KdeKernel;\n\n$data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2];\n$f = Stat::kde($data, h: 1.5);\n$f(2.5);\n// estimated density at x = 2.5\n```\n\nUsing a different kernel:\n\n```php\n$f = Stat::kde($data, h: 1.5, kernel: KdeKernel::Triangular);\n$f(2.5);\n```\n\nCumulative distribution function:\n\n```php\n$F = Stat::kde($data, h: 1.5, cumulative: true);\n$F(2.5);\n// estimated CDF at x = 2.5 (probability that a value is \u003c= 2.5)\n```\n\n#### Stat::kdeRandom ( array $data , float $h , KdeKernel $kernel = KdeKernel::Normal , ?int $seed = null )\nGenerate random samples from a Kernel Density Estimate.\nReturns a `Closure` that, when called, produces a random float drawn from the KDE distribution defined by the data and bandwidth.\n\nSupported kernels: `KdeKernel::Normal` (alias `KdeKernel::Gauss`), `KdeKernel::Logistic`, `KdeKernel::Sigmoid`, `KdeKernel::Rectangular` (alias `KdeKernel::Uniform`), `KdeKernel::Triangular`, `KdeKernel::Parabolic` (alias `KdeKernel::Epanechnikov`), `KdeKernel::Quartic` (alias `KdeKernel::Biweight`), `KdeKernel::Triweight`, `KdeKernel::Cosine`.\n\n```php\nuse HiFolks\\Statistics\\Stat;\nuse HiFolks\\Statistics\\Enums\\KdeKernel;\n\n$data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2];\n$rand = Stat::kdeRandom($data, h: 1.5, seed: 8675309);\n$samples = [];\nfor ($i = 0; $i \u003c 10; $i++) {\n    $samples[] = round($rand(), 1);\n}\n// [2.5, 3.3, -1.8, 7.3, -2.1, 4.6, 4.4, 5.9, -3.2, -1.6]\n```\n\nUsing a different kernel:\n\n```php\n$rand = Stat::kdeRandom($data, h: 1.5, kernel: KdeKernel::Triangular, seed: 42);\n$rand();\n```\n\n### Freq class\nWith *Statistics* package you can calculate frequency table.\nA frequency table lists the frequency of various outcomes in a sample.\nEach entry in the table contains the frequency or count of the occurrences of values within a particular group or interval.\n\n\n#### Freq::frequencies( array $data )\n```php\nuse HiFolks\\Statistics\\Freq;\n\n$fruits = ['🍈', '🍈', '🍈', '🍉','🍉','🍉','🍉','🍉','🍌'];\n$freqTable = Freq::frequencies($fruits);\nprint_r($freqTable);\n```\nYou can see the frequency table as an array:\n```\nArray\n(\n    [🍈] =\u003e 3\n    [🍉] =\u003e 5\n    [🍌] =\u003e 1\n)\n```\n#### Freq::relativeFrequencies( array $data )\nYou can retrieve the frequency table in relative format (percentage):\n```php\n$freqTable = Freq::relativeFrequencies($fruits, 2);\nprint_r($freqTable);\n```\nYou can see the frequency table as an array with percentage of the occurrences:\n```\nArray\n(\n    [🍈] =\u003e 33.33\n    [🍉] =\u003e 55.56\n    [🍌] =\u003e 11.11\n)\n```\n\n#### Freq::frequencyTableBySize( array $data , $size)\n\nIf you want to create a frequency table based on class (ranges of values) you can use frequencyTableBySize.\nThe first parameter is the array, and the second one is the size of classes.\n\nCalculate the frequency table with classes. Each group size is 4\n```php\n$data = [1,1,1,4,4,5,5,5,6,7,8,8,8,9,9,9,9,9,9,10,10,11,12,12,\n    13,14,14,15,15,16,16,16,16,17,17,17,18,18, ];\n$result = \\HiFolks\\Statistics\\Freq::frequencyTableBySize($data, 4);\nprint_r($result);\n/*\nArray\n(\n    [1] =\u003e 5\n    [5] =\u003e 8\n    [9] =\u003e 11\n    [13] =\u003e 9\n    [17] =\u003e 5\n)\n */\n```\n\n#### Freq::frequencyTable()\n\nIf you want to create a frequency table based on class (ranges of values) you can use frequencyTable.\nThe first parameter is the array, and the second one is the number of classes.\n\nCalculate the frequency table with 5 classes.\n```php\n$data = [1,1,1,4,4,5,5,5,6,7,8,8,8,9,9,9,9,9,9,10,10,11,12,12,\n    13,14,14,15,15,16,16,16,16,17,17,17,18,18, ];\n$result = \\HiFolks\\Statistics\\Freq::frequencyTable($data, 5);\nprint_r($result);\n/*\nArray\n(\n    [1] =\u003e 5\n    [5] =\u003e 8\n    [9] =\u003e 11\n    [13] =\u003e 9\n    [17] =\u003e 5\n)\n */\n```\n\n\n### Statistics class\n\nThe methods provided by the `Freq` and the `Stat` classes are mainly **static** methods.\nIf you prefer to use an object instance for calculating statistics you can choose to use an instance of the `Statistics` class.\nSo for calling the statistics methods, you can use your object instance of the `Statistics` class.\n\nFor example for calculating the mean, you can obtain the `Statistics` object via the `make()` static method, and then use the new object `$stat` like in the following example:\n\n```php\n$stat = HiFolks\\Statistics\\Statistics::make(\n    [3,5,4,7,5,2]\n);\necho $stat-\u003evaluesToString(5) . PHP_EOL;\n// 2,3,4,5,5\necho \"Mean              : \" . $stat-\u003emean() . PHP_EOL;\n// Mean              : 4.3333333333333\necho \"Count             : \" . $stat-\u003ecount() . PHP_EOL;\n// Count             : 6\necho \"Median            : \" . $stat-\u003emedian() . PHP_EOL;\n// Median            : 4.5\necho \"First Quartile  : \" . $stat-\u003efirstQuartile() . PHP_EOL;\n// First Quartile  : 2.5\necho \"Third Quartile : \" . $stat-\u003ethirdQuartile() . PHP_EOL;\n// Third Quartile : 5\necho \"Mode              : \" . $stat-\u003emode() . PHP_EOL;\n// Mode              : 5\n```\n\n#### Calculate Frequency Table\n\nThe `Statistics` packages have some methods for generating Frequency Table:\n- `frequencies()`: a frequency is the number of times a value of the data occurs;\n- `relativeFrequencies()`: a relative frequency is the ratio (fraction or proportion) of the number of times a value of the data occurs in the set of all outcomes to the total number of outcomes;\n- `cumulativeFrequencies()`: is the accumulation of the previous relative frequencies;\n- `cumulativeRelativeFrequencies()`: is the accumulation of the previous relative ratio.\n\n```php\nuse HiFolks\\Statistics\\Statistics;\n\n$s = Statistics::make(\n    [98, 90, 70,18,92,92,55,83,45,95,88,76]\n);\n$a = $s-\u003efrequencies();\nprint_r($a);\n/*\nArray\n(\n    [18] =\u003e 1\n    [45] =\u003e 1\n    [55] =\u003e 1\n    [70] =\u003e 1\n    [76] =\u003e 1\n    [83] =\u003e 1\n    [88] =\u003e 1\n    [90] =\u003e 1\n    [92] =\u003e 2\n    [95] =\u003e 1\n    [98] =\u003e 1\n)\n */\n\n$a = $s-\u003erelativeFrequencies();\nprint_r($a);\n/*\nArray\n(\n    [18] =\u003e 8.3333333333333\n    [45] =\u003e 8.3333333333333\n    [55] =\u003e 8.3333333333333\n    [70] =\u003e 8.3333333333333\n    [76] =\u003e 8.3333333333333\n    [83] =\u003e 8.3333333333333\n    [88] =\u003e 8.3333333333333\n    [90] =\u003e 8.3333333333333\n    [92] =\u003e 16.666666666667\n    [95] =\u003e 8.3333333333333\n    [98] =\u003e 8.3333333333333\n)\n */\n\n```\n## `NormalDist` class\n\nThe `NormalDist` class provides an easy way to work with normal distributions in PHP. It allows you to calculate probabilities and densities for a given mean (μ\\muμ) and standard deviation (σ\\sigmaσ).\n\n### Key features\n\n- Define a normal distribution with mean (μ\\muμ) and standard deviation (σ\\sigmaσ).\n- Calculate the **Probability Density Function (PDF)** to evaluate the relative likelihood of a value.\n- Calculate the **Cumulative Distribution Function (CDF)** to determine the probability of a value or lower.\n- Calculate the **Inverse Cumulative Distribution Function (inv_cdf)** to find the value for a given probability.\n\n------\n\n### Class constructor\n\n```php\n$normalDist = new NormalDist(float $mu = 0.0, float $sigma = 1.0);\n```\n\n- `$mu`: The mean (default = `0.0`).\n- `$sigma`: The standard deviation (default = `1.0`).\n- Throws an exception if `$sigma` is non-positive.\n\n------\n\n### Methods\n\n#### Properties: mean, sigma, and variance\n\nYou can access the distribution parameters via getter methods:\n\n```php\n$normalDist = new NormalDist(100, 15);\n$normalDist-\u003egetMean();             // 100.0\n$normalDist-\u003egetSigma();            // 15.0\n$normalDist-\u003egetMedian();           // 100.0 (equals mean for normal dist)\n$normalDist-\u003egetMode();             // 100.0 (equals mean for normal dist)\n$normalDist-\u003egetVariance();         // 225.0 (sigma squared)\n$normalDist-\u003egetVarianceRounded(2); // 225.0\n```\n\nFrom samples:\n\n```php\n$normalDist = NormalDist::fromSamples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5]);\n$normalDist-\u003egetVarianceRounded(5); // 0.25767\n```\n\n------\n\n#### Creating a normal distribution instance from sample data\n\nThe `fromSamples()` static method creates a normal distribution instance with mu and sigma parameters estimated from the sample data.\n\nExample:\n\n```php\n$samples = [2.5, 3.1, 2.1, 2.4, 2.7, 3.5];\n$normalDist = NormalDist::fromSamples($samples);\n$normalDist-\u003egetMeanRounded(5); // 2.71667\n$normalDist-\u003egetSigmaRounded(5); // 0.50761\n```\n\n#### Generate random samples `samples($n, $seed)`\n\nGenerates `$n` random samples from the normal distribution using the Box-Muller transform. An optional `$seed` parameter allows reproducible results.\n\n```php\n$normalDist = new NormalDist(100, 15);\n\n// Generate 5 random samples\n$samples = $normalDist-\u003esamples(5);\n// e.g. [98.3, 112.7, 89.1, 105.4, 101.2]\n\n// Reproducible results with a seed\n$samples = $normalDist-\u003esamples(1000, seed: 42);\n```\n\n------\n\n#### Z-score `zscore($x)`\n\nComputes the standard score describing `$x` in terms of the number of standard deviations above or below the mean: `(x - mu) / sigma`.\n\n```php\n$normalDist = new NormalDist(100, 15);\necho $normalDist-\u003ezscore(130);          // 2.0 (two std devs above mean)\necho $normalDist-\u003ezscore(85);           // -1.0 (one std dev below mean)\necho $normalDist-\u003ezscoreRounded(114, 3); // 0.933\n```\n\n------\n\n#### Probability Density Function `pdf($x)`\n\nCalculates the **Probability Density Function** at a given value xxx:\n\n```php\n$normalDist-\u003epdf(float $x): float\n```\n\n- Input: the value `$x` at which to evaluate the PDF.\n- Output: the relative likelihood of `$x` in the distribution.\n\nExample:\n\n```php\n$normalDist = new NormalDist(10.0, 2.0);\necho $normalDist-\u003epdf(12.0); // Output: 0.12098536225957168\n```\n\n------\n\n#### Cumulative Distribution Function `cdf($x)`\n\nCalculates the **Cumulative Distribution Function** at a given value `$x`:\n\n```php\n$normalDist-\u003ecdf(float $x): float\n```\n- Input: the value `$x` at which to evaluate the CDF.\n- Output: the probability that a random variable `$x` is less than or equal to `$x`.\n\nExample:\n\n```php\n$normalDist = new NormalDist(10.0, 2.0);\necho $normalDist-\u003ecdf(12.0); // Output: 0.8413447460685429\n```\n\nCalculating both, CDF and PDF:\n\n```php\n$normalDist = new NormalDist(10.0, 2.0);\n\n// Calculate PDF at x = 12\n$pdf = $normalDist-\u003epdf(12.0);\necho \"PDF at x = 12: $pdf\\n\"; // Output: 0.12098536225957168\n\n// Calculate CDF at x = 12\n$cdf = $normalDist-\u003ecdf(12.0);\necho \"CDF at x = 12: $cdf\\n\"; // Output: 0.8413447460685429\n```\n\n------\n\n#### Inverse Cumulative Distribution Function `invCdf($p)`\n\nComputes the **Inverse Cumulative Distribution Function** (also known as the quantile function or percent-point function). Given a probability `$p`, it finds the value `$x` such that `cdf($x) = $p`.\n\n```php\n$normalDist-\u003einvCdf(float $p): float\n```\n\n- Input: a probability `$p` in the range (0, 1) exclusive.\n- Output: the value `$x` where `cdf($x) = $p`.\n- Throws an exception if `$p` is not in (0, 1).\n\nExample:\n\n```php\n$normalDist = new NormalDist(0.0, 1.0);\n\n// Find the value at the 95th percentile of a standard normal distribution\necho $normalDist-\u003einvCdfRounded(0.95, 5); // Output: 1.64485\n\n// The median of a standard normal distribution\necho $normalDist-\u003einvCdf(0.5); // Output: 0.0\n```\n\nThe `invCdf()` method is useful for:\n- **Confidence intervals**: find critical values for a given confidence level.\n- **Hypothesis testing**: determine thresholds for statistical significance.\n- **Percentile calculations**: find the value corresponding to a specific percentile.\n\nRound-trip example with `cdf()`:\n\n```php\n$normalDist = new NormalDist(100, 15);\n\n// inv_cdf(0.5) equals the mean\necho $normalDist-\u003einvCdf(0.5); // Output: 100.0\n\n// Round-trip: cdf(invCdf(p)) ≈ p\necho $normalDist-\u003ecdfRounded($normalDist-\u003einvCdf(0.25), 2); // Output: 0.25\n```\n\n------\n\n#### Quantiles `quantiles($n)`\n\nDivides the normal distribution into `$n` continuous intervals with equal probability. Returns a list of `$n - 1` cut points separating the intervals.\nSet `$n` to 4 for quartiles (the default), `$n` to 10 for deciles, or `$n` to 100 for percentiles.\n\n```php\n$normalDist = new NormalDist(0.0, 1.0);\n\n// Quartiles (default)\n$normalDist-\u003equantiles();    // [-0.6745, 0.0, 0.6745]\n\n// Deciles\n$normalDist-\u003equantiles(10);  // 9 cut points\n\n// Percentiles\n$normalDist-\u003equantiles(100); // 99 cut points\n```\n\n------\n\n#### Overlapping coefficient `overlap($other)`\n\nComputes the overlapping coefficient (OVL) between two normal distributions. Measures the agreement between two normal probability distributions. Returns a value between 0.0 and 1.0 giving the overlapping area in the two underlying probability density functions.\n\n```php\n$n1 = new NormalDist(2.4, 1.6);\n$n2 = new NormalDist(3.2, 2.0);\necho $n1-\u003eoverlapRounded($n2, 4); // 0.8035\n\n// Identical distributions overlap completely\n$n3 = new NormalDist(0, 1);\necho $n3-\u003eoverlap($n3); // 1.0\n```\n\n------\n\n#### Combining a normal distribution via `add()` method\n\nThe `add()` method allows you to combine a NormalDist instance with either a constant or another NormalDist object.\nThis operation supports mathematical transformations and the combination of distributions.\n\nThe use cases are:\n- Shifting a distribution: add a constant to shift the mean, useful in translating data.\n- Combining distributions: combine independent or jointly normally distributed variables, commonly used in statistics and probability.\n\n```php\n$birth_weights = NormalDist::fromSamples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5]);\n$drug_effects = new NormalDist(0.4, 0.15);\n$combined = $birth_weights-\u003eadd($drug_effects);\n\n$combined-\u003egetMeanRounded(1); // 3.1\n$combined-\u003egetSigmaRounded(1); // 0.5\n\n$birth_weights-\u003egetMeanRounded(5); // 2.71667\n$birth_weights-\u003egetSigmaRounded(5); // 0.50761\n```\n\n#### Scaling a normal distribution by a costant via `multiply()` method\n\nThe `multiply()` method for NormalDist multiplies both the mean (mu) and standard deviation (sigma) by a constant.\nThis method is useful for rescaling distributions, such as when changing measurement units.\nThe standard deviation is scaled by the absolute value of the constant to ensure it remains non-negative.\n\nThe method does not modify the existing object but instead returns a new NormalDist instance with the updated values.\n\nUse Cases:\n- Rescaling distributions: useful when changing units (e.g., from meters to kilometers, or Celsius to Farenhait).\n- Transforming data: apply proportional scaling to statistical data.\n\n```php\n$tempFebruaryCelsius = new NormalDist(5, 2.5); # Celsius\n$tempFebFahrenheit = $tempFebruaryCelsius-\u003emultiply(9 / 5)-\u003eadd(32); # Fahrenheit\n$tempFebFahrenheit-\u003egetMeanRounded(1); // 41.0\n$tempFebFahrenheit-\u003egetSigmaRounded(1); // 4.5\n```\n\n\n#### Subtracting from a normal distribution via `subtract()` method\n\nThe `subtract()` method is the counterpart to `add()`. It subtracts a constant or another NormalDist instance from this distribution.\n\n- A constant (float): shifts the mean down, leaving sigma unchanged.\n- A NormalDist instance: subtracts the means and combines the variances.\n\n```php\n$nd = new NormalDist(100, 15);\n$shifted = $nd-\u003esubtract(32);\n$shifted-\u003egetMean();  // 68.0\n$shifted-\u003egetSigma(); // 15.0 (unchanged)\n```\n\n#### Dividing a normal distribution by a constant via `divide()` method\n\nThe `divide()` method is the counterpart to `multiply()`. It divides both the mean (mu) and standard deviation (sigma) by a constant.\n\n```php\n// Convert Fahrenheit back to Celsius: (F - 32) / (9/5)\n$tempFahrenheit = new NormalDist(41, 4.5);\n$tempCelsius = $tempFahrenheit-\u003esubtract(32)-\u003edivide(9 / 5);\n$tempCelsius-\u003egetMeanRounded(1);  // 5.0\n$tempCelsius-\u003egetSigmaRounded(1); // 2.5\n```\n\n------\n\n### References for NormalDist\n\nThis class is inspired by Python’s `statistics.NormalDist` and aims to provide similar functionality for PHP users. (Work in Progress)\n\n## `StudentT` class\n\nThe `StudentT` class represents the Student’s t-distribution, which is used for hypothesis testing and confidence intervals when the population standard deviation is unknown, especially with small sample sizes. As the degrees of freedom increase, the t-distribution approaches the standard normal distribution.\n\n### Creating a StudentT instance\n\n```php\nuse HiFolks\\Statistics\\StudentT;\n\n$t = new StudentT(df: 10); // 10 degrees of freedom\n```\n\n### Probability Density Function (PDF)\n\n```php\n$t = new StudentT(5);\n$t-\u003epdf(0);        // ≈ 0.37961 (peak of the distribution)\n$t-\u003epdf(2.0);      // density at t=2\n$t-\u003epdfRounded(0); // 0.38\n```\n\n### Cumulative Distribution Function (CDF)\n\n```php\n$t = new StudentT(5);\n$t-\u003ecdf(0);    // 0.5 (symmetric around zero)\n$t-\u003ecdf(2.0);  // ≈ 0.94874\n$t-\u003ecdfRounded(2.0); // 0.949\n```\n\n### Inverse CDF (Quantile Function)\n\n```php\n$t = new StudentT(10);\n$t-\u003einvCdf(0.975);  // ≈ 2.228 (critical value for 95% two-sided test)\n$t-\u003einvCdf(0.5);    // 0.0 (median)\n$t-\u003einvCdfRounded(0.975, 3); // 2.228\n```\n\n## StreamingStat (Experimental)\n\n\u003e **Note**: `StreamingStat` is experimental in version 1.x. It will be released as stable in version 2. If you want to provide feedback, we are happy to hear from you — please open an issue at https://github.com/Hi-Folks/statistics/issues.\n\n`StreamingStat` computes descriptive statistics in a single pass with O(1) memory, ideal for large datasets or generator-based streams.\n\n```php\nuse HiFolks\\Statistics\\StreamingStat;\n\n$s = new StreamingStat();\n$s-\u003eadd(1)-\u003eadd(2)-\u003eadd(3)-\u003eadd(4)-\u003eadd(5);\n\n$s-\u003ecount();     // 5\n$s-\u003esum();       // 15.0\n$s-\u003emin();       // 1.0\n$s-\u003emax();       // 5.0\n$s-\u003emean();      // 3.0\n$s-\u003evariance();  // 2.5\n$s-\u003estdev();     // 1.5811...\n$s-\u003eskewness();  // 0.0\n$s-\u003ekurtosis();  // -1.2\n```\n\n| Method | Description | Min n |\n|---|---|---|\n| `count()` | Number of values added | 0 |\n| `sum()` | Sum of all values | 1 |\n| `min()` | Minimum value | 1 |\n| `max()` | Maximum value | 1 |\n| `mean(?int $round = null)` | Arithmetic mean | 1 |\n| `variance(?int $round = null)` | Sample variance | 2 |\n| `pvariance(?int $round = null)` | Population variance | 1 |\n| `stdev(?int $round = null)` | Sample standard deviation | 2 |\n| `pstdev(?int $round = null)` | Population standard deviation | 1 |\n| `skewness(?int $round = null)` | Sample skewness (adjusted Fisher-Pearson) | 3 |\n| `pskewness(?int $round = null)` | Population skewness | 3 |\n| `kurtosis(?int $round = null)` | Excess kurtosis (sample) | 4 |\n\nAll methods throw `InvalidDataInputException` when insufficient data is available.\n\n## Utility classes\n\nThe package includes utility classes under `HiFolks\\Statistics\\Utils` for common array and formatting operations.\n\n### `Arr` — array helpers\n\n```php\nuse HiFolks\\Statistics\\Utils\\Arr;\n```\n\n#### Arr::extract( array $data, array $columns )\n\nExtract one or more columns from an array of associative arrays. Returns one array per requested column.\n\n```php\n$runners = [\n    ['name' =\u003e 'Alice', 'age' =\u003e 30, 'score' =\u003e 95],\n    ['name' =\u003e 'Bob',   'age' =\u003e 25, 'score' =\u003e 87],\n];\n\n[$ages, $scores] = Arr::extract($runners, ['age', 'score']);\n// $ages = [30, 25], $scores = [95, 87]\n```\n\n#### Arr::partition( array $data, string $field, string $operator, mixed $value )\n\nSplit an array of associative arrays into `[$matching, $nonMatching]` groups based on a condition. Supported operators: `==`, `!=`, `\u003e`, `\u003c`, `\u003e=`, `\u003c=`.\n\n```php\n[$men, $women] = Arr::partition($runners, 'gender', '==', 'M');\n[$seniors, $others] = Arr::partition($runners, 'age', '\u003e=', 40);\n```\n\n#### Arr::toString( array $data, bool|int $sample = false )\n\nJoin array values into a comma-separated string. Pass an integer to limit to the first N values.\n\n#### Arr::stripZeroes( array $data )\n\nRemove zero values from the array.\n\n### `Format` — time formatting\n\n```php\nuse HiFolks\\Statistics\\Utils\\Format;\n```\n\n#### Format::secondsToTime( int|float $seconds )\n\nConvert seconds to a human-readable time string.\n\n```php\nFormat::secondsToTime(4845);  // \"1:20:45\"\n```\n\n#### Format::timeToSeconds( string $time )\n\nParse a time string back to total seconds.\n\n```php\nFormat::timeToSeconds('1:20:45');  // 4845\n```\n\n#### Format::secondsToHms( int|float $seconds )\n\nConvert seconds to an associative array with `hours`, `minutes`, `seconds` keys.\n\n```php\nFormat::secondsToHms(4845);  // ['hours' =\u003e 1, 'minutes' =\u003e 20, 'seconds' =\u003e 45]\n```\n\n#### Format::hmsToSeconds( int $hours, int $minutes, int $seconds )\n\nConvert hours, minutes, and seconds to total seconds.\n\n```php\nFormat::hmsToSeconds(1, 20, 45);  // 4845\n```\n\n## Testing\n\n```bash\ncomposer run test           Runs the test script\ncomposer run test-coverage  Runs the test-coverage script\ncomposer run format         Runs the format script\ncomposer run static-code    Runs the static-code script\ncomposer run all-check      Runs the all-check script\n```\n\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## Contributing\n\nPlease see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.\n\n## Security Vulnerabilities\n\nPlease review [our security policy](../../security/policy) on how to report security vulnerabilities.\n\n## Credits\n\n- [Roberto B.](https://github.com/roberto-butti)\n- [All Contributors](../../contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhi-folks%2Fstatistics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhi-folks%2Fstatistics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhi-folks%2Fstatistics/lists"}