diff --git a/CHANGELOG.md b/CHANGELOG.md index d8e24fd..59d174f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog -## 1.0.2 - WIP - +## 1.0.2 - 2024-12-10 +- NormalDist class, with `cdf()` and `pdf()` - Fix deprecations for PHP 8.4 ## 1.0.1 - 2024-11-21 diff --git a/README.md b/README.md index e529fc9..797b6d9 100644 --- a/README.md +++ b/README.md @@ -462,6 +462,92 @@ Array */ ``` +## `NormalDist` class + +The `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σ). + +### Key features + +- Define a normal distribution with mean (μ\muμ) and standard deviation (σ\sigmaσ). +- Calculate the **Probability Density Function (PDF)** to evaluate the relative likelihood of a value. +- Calculate the **Cumulative Distribution Function (CDF)** to determine the probability of a value or lower. + +------ + +### Class constructor + +```php +$normalDist = new NormalDist(float $mu = 0.0, float $sigma = 1.0); +``` + +- `$mu`: The mean (default = `0.0`). +- `$sigma`: The standard deviation (default = `1.0`). +- Throws an exception if `$sigma` is non-positive. + +------ + +### Methods + +#### 1. `pdf($x)` + +Calculates the **Probability Density Function** at a given value xxx: + +```php +$normalDist->pdf(float $x): float +``` + +**Input**: The value `$x` at which to evaluate the PDF. +**Output**: The relative likelihood of `$x` in the distribution. + +Example: + +```php +$normalDist = new NormalDist(10.0, 2.0); +echo $normalDist->pdf(12.0); // Output: 0.12098536225957168 +``` + +------ + +#### 2. `cdf($x)` + +Calculates the **Cumulative Distribution Function** at a given value `$x`: + +```php +$normalDist->cdf(float $x): float +``` +**Input**: The value `$x` at which to evaluate the CDF. +**Output**: The probability that a random variable `$x` is less than or equal to `$x`. +Example: + +```php +$normalDist = new NormalDist(10.0, 2.0); +echo $normalDist->cdf(12.0); // Output: 0.8413447460685429 +``` + +------ + +### Use case example + +```php +$normalDist = new NormalDist(10.0, 2.0); + +// Calculate PDF at x = 12 +$pdf = $normalDist->pdf(12.0); +echo "PDF at x = 12: $pdf\n"; // Output: 0.12098536225957168 + +// Calculate CDF at x = 12 +$cdf = $normalDist->cdf(12.0); +echo "CDF at x = 12: $cdf\n"; // Output: 0.8413447460685429 +``` + +------ + +### References for NormalDist + +This class is inspired by Python’s `statistics.NormalDist` and provides similar functionality for PHP users. + + + ## Testing ```bash diff --git a/src/NormalDist.php b/src/NormalDist.php new file mode 100644 index 0000000..81488dc --- /dev/null +++ b/src/NormalDist.php @@ -0,0 +1,82 @@ +sigma = $sigma; + } + + // Getter for mean (read-only) + public function getMean(): float + { + return $this->mu; + } + + // Getter for standard deviation (read-only) + public function getSigma(): float + { + return $this->sigma; + } + + // A utility function to calculate the probability density function (PDF) + public function pdf(float $x): float + { + $coeff = 1 / (sqrt(2 * M_PI) * $this->sigma); + $exponent = -($x - $this->mu) ** 2 / (2 * $this->sigma ** 2); + + return $coeff * exp($exponent); + } + + public function pdfRounded(float $x, int $precision = 3): float + { + return round($this->pdf($x), $precision); + } + + // Approximate the error function (erf) + private function erf(float $z): float + { + $t = 1 / (1 + 0.5 * abs($z)); + $tau = $t * exp(-$z * $z + - 1.26551223 + + 1.00002368 * $t + + 0.37409196 * $t ** 2 + + 0.09678418 * $t ** 3 + - 0.18628806 * $t ** 4 + + 0.27886807 * $t ** 5 + - 1.13520398 * $t ** 6 + + 1.48851587 * $t ** 7 + - 0.82215223 * $t ** 8 + + 0.17087277 * $t ** 9); + return $z >= 0 ? 1 - $tau : $tau - 1; + } + + + // A utility function to calculate the cumulative density function (CDF) + public function cdf(float $x): float + { + $z = ($x - $this->mu) / ($this->sigma * sqrt(2)); + + return 0.5 * (1 + $this->erf($z)); + } + + public function cdfRounded(float $x, int $precision = 3): float + { + return round($this->cdf($x), $precision); + } + + + +} diff --git a/tests/NormalDistTest.php b/tests/NormalDistTest.php new file mode 100644 index 0000000..dbee801 --- /dev/null +++ b/tests/NormalDistTest.php @@ -0,0 +1,30 @@ +getMean(), + )->toEqual(1060); + expect( + $nd->getSigma(), + )->toEqual(195); + +}); +it('can calculate normal dist cdf', function (): void { + $nd = new NormalDist(1060, 195); + expect( + round($nd->cdf(1200 + 0.5) - $nd->cdf(1100 - 0.5), 3), + )->toEqual(0.184); +}); + +it('can calculate normal dist pdf', function (): void { + $nd = new NormalDist(10, 2); + expect( + $nd->pdfRounded(12, 3), + )->toEqual(0.121); + expect( + $nd->pdfRounded(12, 2), + )->toEqual(0.12); +});