From cdd14347990cfb799985694cadf6794202fd105a Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 19 Jul 2016 16:49:52 +0200 Subject: [PATCH 1/3] Forecasting implementations Implementing more regressions, arima draft (not complete), some more average and error functions. --- Math/Finance/Forecasting/ARIMA.php | 131 +++++++++++++ .../Forecasting/ClassicalDecomposition.php | 87 +++++++++ Math/Finance/Forecasting/Regression.php | 1 - Math/Statistic/Average.php | 66 +++++++ Math/Statistic/Forecast/Error.php | 81 ++++++++ .../Regression/LevelLevelRegression.php | 43 +++++ .../Regression/LevelLogRegression.php | 70 +++++++ .../Regression/LogLevelRegression.php | 70 +++++++ .../Forecast/Regression/LogLogRegression.php | 71 +++++++ .../Regression/MultipleLinearRegression.php | 19 ++ .../RegressionAbstract.php} | 176 +++++++----------- 11 files changed, 701 insertions(+), 114 deletions(-) create mode 100644 Math/Finance/Forecasting/ClassicalDecomposition.php delete mode 100644 Math/Finance/Forecasting/Regression.php create mode 100644 Math/Statistic/Forecast/Regression/LevelLevelRegression.php create mode 100644 Math/Statistic/Forecast/Regression/LevelLogRegression.php create mode 100644 Math/Statistic/Forecast/Regression/LogLevelRegression.php create mode 100644 Math/Statistic/Forecast/Regression/LogLogRegression.php create mode 100644 Math/Statistic/Forecast/Regression/MultipleLinearRegression.php rename Math/Statistic/Forecast/{LinearRegression.php => Regression/RegressionAbstract.php} (65%) diff --git a/Math/Finance/Forecasting/ARIMA.php b/Math/Finance/Forecasting/ARIMA.php index 8b1378917..d1faab950 100644 --- a/Math/Finance/Forecasting/ARIMA.php +++ b/Math/Finance/Forecasting/ARIMA.php @@ -1 +1,132 @@ +getIteration($this->data); + $iteration2 = $this->getIteration($iteration1); + $iteration3 = $this->getIteration($iteration2); + + return $iteration3; + } + + private function getIteration(array $data) : array { + $multiplicativeDecomposition = new ClassicalDecomposition($data, $this->order, ClassicalDecomposition::MULTIPLICATIVE); + $tempDecomposition = $multiplicativeDecomposition->getDecomposition(); + + // 1 + $trendCycleComponent = ClassicalDecomposition::computeTrendCycle($data, $this->order); + $centeredRatios = ClassicalDecomposition::computeDetrendedSeries($this->data, $trendCycleComponent, ClassicalDecomposition::MULTIPLICATIVE); + $prelimSeasonalComponent = Average::totalMovingAverage(Average::totalMovingAverage($centeredRatios, 3, null, true), 3, null, true); + $prelimRemainder = $this->getPrelimRemainder($centeredRatios, $prelimSeasonalComponent); + $modifiedRemainder = $this->removeOutliers($prelimRemainder, 0.5); + $modifiedCenteredRatios = $this->getModifiedCenteredRatios($prelimSeasonalComponent, $modifiedRemainder); + $revisedSeasonalComponent = Average::totalMovingAverage(Average::totalMovingAverage($modifiedCenteredRatios, 3, null, true), 3, null, true); + $prelimSeasonalAdjustedSeries = $this->getPrelimSeasonalAdjustedSeries($revisedSeasonalComponent); + $trendCycleComponent = $this->getTrendCycleEstimation($prelimSeasonalAdjustedSeries); + + // 2 + $centeredRatios = ClassicalDecomposition::computeDetrendedSeries($this->data, $trendCycleComponent, ClassicalDecomposition::MULTIPLICATIVE); + $prelimSeasonalComponent = Average::totalMovingAverage(Average::totalMovingAverage($centeredRatios, 5, null, true), 3, null, true); + $prelimRemainder = $this->getPrelimRemainder($centeredRatios, $prelimSeasonalComponent); + $modifiedRemainder = $this->removeOutliers($prelimRemainder, 0.5); + $modifiedCenteredRatios = $this->getModifiedCenteredRatios($prelimSeasonalComponent, $modifiedRemainder); + $revisedSeasonalComponent = Average::totalMovingAverage(Average::totalMovingAverage($modifiedCenteredRatios, 5, null, true), 3, null, true); + $seasonalAdjustedSeries = $this->getSeasonalAdjustedSeries($revisedSeasonalComponent); + + $remainder = $this->getRemainder($seasonalAdjustedSeries, $trendCycleComponent); + $modifiedRemainder = $this->removeOutliers($remainder, 0.5); + $modifiedData = $this->getModifiedData($trendCycleComponent, $seasonalAdjustedSeries, $modifiedRemainder); + + return $modifiedData; + } + + private function getPrelimRemainder(array $centeredRatios, array $prelimSeasonalComponent) : array { + $remainder = []; + $count = count($prelimSeasonalComponent); + + for($i = 0; $i < $count; $i++) { + // +1 since 3x3 MA + $remainder[] = $centeredRatios[$i+1] / $prelimSeasonalComponent[$i]; + } + + return $remainder; + } + + private function removeOutliers(array $data, float $deviation = 0.5) : array { + $avg = AVerage::arithmeticMean($data); + + foreach($data as $key => $value) { + if($value / $avg - 1 > $deviation) { + $data[$key] = $avg; + } + } + + return $data; + } + + private function getModifiedCenteredRatios(array $seasonal, array $remainder) : array { + $centeredRatio = []; + $count = count($seasonal); + + for($i = 0; $i < $count; $i++) { + // +1 since 3x3 MA + $centeredRatio[] = $remainder[$i+1] * $seasonal[$i]; + } + + return $centeredRatio; + } + + private function getSeasonalAdjustedSeries(array $seasonal) : array { + $adjusted = []; + $count = count($seasonal); + $start = ClassicalDecomposition::getStartOfDecomposition(count($this->data), $count); + + for($i = 0; $i < $count; $i++) { + $adjusted[] = $this->data[$start + $i] / $seasonal[$i]; + } + + return $adjusted; + } + + private function getTrendCycleEstimation(array $seasonal) : array { + $count = count($seasonal); + + if($count >= 12) { + $weight = Average::MAH23; + } elseif($count >= 6) { + $weight = Average::MAH13; + } else { + $weight = Average::MAH9; + } + + // todo: implement + + return $seasonal; + } + + private function getRemainder(array $seasonal, array $trendCycle) { + $remainder = []; + foreach($seasonal as $key => $e) { + $remainder = $e / $trendCycle[$key]; + } + + return $remainder; + } + + private function getModifiedData(array $trendCycleComponent, array $seasonalAdjustedSeries, array $remainder) : array { + $data = []; + $count = count($trendCycleComponent); + + for($i = 0; $i < $count; $i++) { + $data[] = $trendCycleComponent[$i] * $seasonalAdjustedSeries[$i] * $remainder[$i]; + } + + return $data; + } +} \ No newline at end of file diff --git a/Math/Finance/Forecasting/ClassicalDecomposition.php b/Math/Finance/Forecasting/ClassicalDecomposition.php new file mode 100644 index 000000000..be6293d72 --- /dev/null +++ b/Math/Finance/Forecasting/ClassicalDecomposition.php @@ -0,0 +1,87 @@ +mode = $mode; + $this->data = $data; + $this->order = $this->order; + + $this->dataSize = count($data); + } + + public function getDecomposition() : array { + $trendCycleComponent = self::computeTrendCycle($this->data, $this->order); + $detrendedSeries = self::computeDetrendedSeries($this->data, $trendCycleComponent, $this->mode); + $seasonalComponent = $this->computeSeasonalComponent($detrended, $this->order); + $remainderComponent = $this->computeRemainderComponent($trendCycleComponent, $seasonalComponent); + + return [ + 'trendCycleComponent' => $trendCycleComponent, + 'detrendedSeries' => $detrendedSeries, + 'seasonalComponent' => $seasonalComponent, + 'remainderComponent' => $remainderComponent, + ]; + } + + public static function computeTrendCycle(array $data, int $order) : array { + $mMA = Average::totalMovingAverage($data, $order, null true); + + return $this->order % 2 === 0 ? Average::totalMovingAverage($mMa, 2, null, true) : $mMA; + } + + public static function computeDetrendedSeries(array $data, array $trendCycleComponent, int $mode) : array { + $detrended = []; + $count = count($trendCycleComponent); + $start = self::getStartOfDecomposition(count($data), $count); + + for($i = 0; $i < $count; $i++) { + $detrended[] = $mode === self::ADDITIVE ? $data[$start + $i] - $trendCycleComponent[$i] : $data[$start + $i] / $trendCycleComponent[$i]; + } + + return $detrended; + } + + /** + * Moving average can't start at index 0 since it needs to go m indices back for average -> can only start at m + */ + public static function getStartOfDecomposition(int $dataSize, int $trendCycleComponents) : int { + return ($dataSize - $trendCycleComponents) / 2; + } + + private function computeSeasonalComponent() : array { + $seasonalComponent = []; + + for($i = 0; $i < $this->orderSize; $i++) { + $temp = []; + + for($j = $i * $this->order; $j < $count; $j += $this->order) { + $temp[] = $this->data[$j]; + } + + $seasonalComponent[] = Average::arithmeticMean($temp); + } + + return $seasonalComponent; + } + + public static function computeRemainderComponent(array $trendCycleComponent, array $seasonalComponent) : array { + $remainderComponent = []; + $count = count($trendedCycleComponent); + $start = self::getStartOfDecomposition($this->dataSize, $count); + $seasons = count($seasonalComponent); + + for($i = 0; $i < $count; $i++) { + $remainderComponent[] = $this->mode === self::ADDITIVE ? $this->data[$start + $i] - $trendCycleComponent[$i] - $seasonalComponent[$i % $seasons] : $this->data[$start + $i] / ($trendCycleComponent[$i] * $seasonalComponent[$i % $seasons]); + } + + return $remainderComponent; + } +} \ No newline at end of file diff --git a/Math/Finance/Forecasting/Regression.php b/Math/Finance/Forecasting/Regression.php deleted file mode 100644 index 8b1378917..000000000 --- a/Math/Finance/Forecasting/Regression.php +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Math/Statistic/Average.php b/Math/Statistic/Average.php index 6cf1f4c5b..035e73b20 100644 --- a/Math/Statistic/Average.php +++ b/Math/Statistic/Average.php @@ -30,6 +30,18 @@ namespace phpOMS\Math\Statistic; class Average { + const MA3 = [1/3, 1/3]; + const MA5 = [0.2, 0.2, 0.2]; + const MA2x12 = [5/6, 5/6, 5/6, 5/6, 5/6, 5/6, 0.42]; + const MA3x3 = [1/3, 2/9, 1/9]; + const MA3x5 = [0.2, 0.2, 2/15, 4/6]; + const MAS15 = [0.231, 0.209, 0.144, 2/3, 0.009, -0.016, -0.019, -0.009] + const MAS21 = [0.171, 0.163, 0.134, 0.37, 0.51, 0.017, -0.006, -0.014, -0.014, -0.009, -0.003]; + const MAH5 = [0.558, 0.294, -0.73]; + const MAH9 = [0.330, 0.267, 0.119, -0.010, -0.041]; + const MAH13 = [0.240, 0.214, 0.147, 0.66, 0, -0.028, -0.019]; + const MAH23 = [0.148, 0.138, 0.122, 0.097, 0.068, 0.039, 0.013, -0.005, -0.015, -0.016, -0.011, -0.004]; + /** * Calculate weighted average. * @@ -80,6 +92,60 @@ class Average return $x[$count - 1] + $h * ($x[$count - 1] - $x[0]) / ($count - 1); } + /** + * Moving average or order m. + * + * @param array $x Dataset + * @param int $t Current period + * @param int $periods Periods to use for average + * + * @return float + * + * @todo: allow counter i also to go into the future... required for forecast how? should be doable! + * + * @throws + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function movingAverage(array $x, int $t, int $order, array $weight = null, bool $symmetric = false) : float { + $periods = (int) ($order / 2); + if($t < $periods || ($count = count($x)) < $periods) || ($symmetric && $t + $periods < $count)) { + throw new \Exception('Periods'); + } + + if(!isset($weight)) { + $weight = array_fill(0, $count, 1); + } + + $sum = 0.0; + $end = $symmetric ? $periods - 1 : 0; + $end = $order % 2 === 0 ? $end - 1 : $end; + + for($i = -($periods - 2); $i < $end; $i++) { + $sum += $weight[$t-1+$i] * $x[$t-1+$i]; + } + + return 1/ $order * $sum; + } + + /** + * t = 3 and p = 3 means -1 0 +1, t = 4 and p = 2 means -1 0 + * periods should be replaced with order than it's possible to test for even or odd m + * todo: maybe floor()? + */ + public static function totalMovingAverage(array $x, int $order, array $weight = null, bool $symmetric = false) : array { + $periods = (int) ($order / 2); + $count = count($x) - ($symmetric ? $periods : 0); + $avg = []; + + for($i = $periods; $i < $count; $i++) { + $avg[] = self::movingAverage($x, $i, $order, $weight, $symmetric); + } + + return $avg; + } + /** * Calculate the mode. * diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index c13ab6d4c..644280674 100644 --- a/Math/Statistic/Forecast/Error.php +++ b/Math/Statistic/Forecast/Error.php @@ -139,6 +139,87 @@ class Error return sqrt(Average::arithmeticMean(self::square($errors))); } + /** + * Goodness of fit. + * + * Evaluating how well the observed data fit the linear regression model + * + * @param array $observed Obersved y values + * @param array $forecasted Forecasted y values + * + * @return float + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function getCoefficientOfDetermination(array $observed, array $forecasted) : float + { + $countO = count($observed); + $countF = count($forecasted); + $sum1 = 0; + $sum2 = 0; + $meanY = Average::arithmeticMean($observed); + + for ($i = 0; $i < $countF; $i++) { + $sum1 += ($forecasted[$i] - $meanY) ** 2; + } + + for ($i = 0; $i < $countO; $i++) { + $sum2 += ($observed[$i] - $meanY) ** 2; + } + + return $sum1 / $sum2; + } + + /** + * Get sum squared error (SSE). + * + * @param array $errors Errors + * + * @return float + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function getSumSquaredError(array $errors) : float { + $error = 0.0; + + foreach($errors as $e) { + $error += $e*$e; + } + + return $error; + } + + public static function getRBarSquared(float $R, int $observations, int $predictors) : float { + return 1 - (1 - $R * ($observations - 1) / ($observations - $predictors - 1); + } + + /** + * Get Aike's information criterion (AIC) + * + */ + public static function getAkaikeInformationCriterion(float $sse, int $observations, int $predictors) : float { + return $observations * log($sse / $observations) + 2 * ($predictors + 2); + } + + /** + * Get corrected Aike's information criterion (AIC) + * + * Correction for small amount of observations + */ + public static function getCorrectedAkaikeInformationCriterion(float aic, int $observations, int $predictors) : float { + return $aic + (2*($predictors +2)*($predictors + 3)) / ($observations - $predictors - 3); + } + + /** + * Get Bayesian information criterion (BIC) + * + */ + public static function getSchwarzBayesianInformationCriterion(float $sse, int $observations, int $predictors) : float { + return $observations * log($sse / $observations) + ($predictors +2)*log($observations); + } + /** * Get mean absolute percentage error (MAPE). * diff --git a/Math/Statistic/Forecast/Regression/LevelLevelRegression.php b/Math/Statistic/Forecast/Regression/LevelLevelRegression.php new file mode 100644 index 000000000..8c4d91221 --- /dev/null +++ b/Math/Statistic/Forecast/Regression/LevelLevelRegression.php @@ -0,0 +1,43 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ + +namespace phpOMS\Math\Statistic\Forecast\Regression; + +use phpOMS\Math\Statistic\Average; +use phpOMS\Math\Statistic\Forecast\ForecastIntervalMultiplier; +use phpOMS\Math\Statistic\MeasureOfDispersion; + +/** + * Regression class. + * + * @category Framework + * @package phpOMS\DataStorage\Database + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class LevelLevelRegression extends RegressionAbstract +{ + public static function getSlope(float $b1, float $y, float $x) : float { + return $b1; + } + + public static function getElasticity(float $b1, float $y, float $x): float { + return $b1 * $y / $x; + } +} \ No newline at end of file diff --git a/Math/Statistic/Forecast/Regression/LevelLogRegression.php b/Math/Statistic/Forecast/Regression/LevelLogRegression.php new file mode 100644 index 000000000..e64c3aedb --- /dev/null +++ b/Math/Statistic/Forecast/Regression/LevelLogRegression.php @@ -0,0 +1,70 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ + +namespace phpOMS\Math\Statistic\Forecast\Regression; + +use phpOMS\Math\Statistic\Average; +use phpOMS\Math\Statistic\Forecast\ForecastIntervalMultiplier; +use phpOMS\Math\Statistic\MeasureOfDispersion; + +/** + * Regression class. + * + * @category Framework + * @package phpOMS\DataStorage\Database + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class LevelLogRegression extends RegressionAbstract +{ + /** + * Get linear regression based on scatter plot. + * + * y = b0 + b1 * x + * + * @param array $x Obersved x values + * @param array $y Observed y values + * + * @return array [b0 => ?, b1 => ?] + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function getRegression(array $x, array $y) : array + { + if(($c = count($x)) != count($y)) { + throw new \Exception('Dimension'); + } + + for($i = 0; $i < $c; $i++) { + $x[$i] = log($x[i]); + } + + return parent::getRegression($x, $y); + } + + public static function getSlope(float $b1, float $y, float $x) : float { + return $b1 / $x; + } + + public static function getElasticity(float $b1, float $y, float $x): float { + return $b1 / $x; + } + +} \ No newline at end of file diff --git a/Math/Statistic/Forecast/Regression/LogLevelRegression.php b/Math/Statistic/Forecast/Regression/LogLevelRegression.php new file mode 100644 index 000000000..d7d2946a1 --- /dev/null +++ b/Math/Statistic/Forecast/Regression/LogLevelRegression.php @@ -0,0 +1,70 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ + +namespace phpOMS\Math\Statistic\Forecast\Regression; + +use phpOMS\Math\Statistic\Average; +use phpOMS\Math\Statistic\Forecast\ForecastIntervalMultiplier; +use phpOMS\Math\Statistic\MeasureOfDispersion; + +/** + * Regression class. + * + * @category Framework + * @package phpOMS\DataStorage\Database + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class LogLevelRegression extends RegressionAbstract +{ + /** + * Get linear regression based on scatter plot. + * + * y = b0 + b1 * x + * + * @param array $x Obersved x values + * @param array $y Observed y values + * + * @return array [b0 => ?, b1 => ?] + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function getRegression(array $x, array $y) : array + { + if(($c = count($x)) != count($y)) { + throw new \Exception('Dimension'); + } + + for($i = 0; $i < $c; $i++) { + $y[$i] = log($y[i]); + } + + return parent::getRegression($x, $y); + } + + public static function getSlope(float $b1, float $y, float $x) : float { + return $b1 * $y; + } + + public static function getElasticity(float $b1, float $y, float $x): float { + return $b1 * $x; + } + +} \ No newline at end of file diff --git a/Math/Statistic/Forecast/Regression/LogLogRegression.php b/Math/Statistic/Forecast/Regression/LogLogRegression.php new file mode 100644 index 000000000..0ee514aff --- /dev/null +++ b/Math/Statistic/Forecast/Regression/LogLogRegression.php @@ -0,0 +1,71 @@ + + * @author Dennis Eichhorn + * @copyright 2013 Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ + +namespace phpOMS\Math\Statistic\Forecast\Regression; + +use phpOMS\Math\Statistic\Average; +use phpOMS\Math\Statistic\Forecast\ForecastIntervalMultiplier; +use phpOMS\Math\Statistic\MeasureOfDispersion; + +/** + * Regression class. + * + * @category Framework + * @package phpOMS\DataStorage\Database + * @author OMS Development Team + * @author Dennis Eichhorn + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class LogLogRegression extends RegressionAbstract +{ + /** + * Get linear regression based on scatter plot. + * + * y = b0 + b1 * x + * + * @param array $x Obersved x values + * @param array $y Observed y values + * + * @return array [b0 => ?, b1 => ?] + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function getRegression(array $x, array $y) : array + { + if(($c = count($x)) != count($y)) { + throw new \Exception('Dimension'); + } + + for($i = 0; $i < $c; $i++) { + $x[$i] = log($x[i]); + $y[$i] = log($y[i]); + } + + return parent::getRegression($x, $y); + } + + public static function getSlope(float $b1, float $y, float $x) : float { + return $b1 * $y / $x; + } + + public static function getElasticity(float $b1, float $y, float $x): float { + return $b1; + } + +} \ No newline at end of file diff --git a/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php b/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php new file mode 100644 index 000000000..29372af19 --- /dev/null +++ b/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php @@ -0,0 +1,19 @@ +setArray($x); + $XT = $X->transpose(); + + $Y = new Matrix(count($y)); + $Y->setArray($y); + + + return $XT->mult($X)->inverse()->mult($XT)->mult($Y)->toArray(); + } + + public static function getVariance() : float {} + + public static function getPredictionInterval() : array {} +} \ No newline at end of file diff --git a/Math/Statistic/Forecast/LinearRegression.php b/Math/Statistic/Forecast/Regression/RegressionAbstract.php similarity index 65% rename from Math/Statistic/Forecast/LinearRegression.php rename to Math/Statistic/Forecast/Regression/RegressionAbstract.php index dec77f498..ff1a3188f 100644 --- a/Math/Statistic/Forecast/LinearRegression.php +++ b/Math/Statistic/Forecast/Regression/RegressionAbstract.php @@ -1,38 +1,6 @@ - - * @author Dennis Eichhorn - * @copyright 2013 Dennis Eichhorn - * @license OMS License 1.0 - * @version 1.0.0 - * @link http://orange-management.com - */ + - * @author Dennis Eichhorn - * @license OMS License 1.0 - * @link http://orange-management.com - * @since 1.0.0 - */ -class LinearRegression -{ +class RegressionAbstract { /** * Get linear regression based on scatter plot. * @@ -46,90 +14,17 @@ class LinearRegression * @since 1.0.0 * @author Dennis Eichhorn */ - public static function getLinearRegresseion(array $x, array $y) : array + public static function getRegression(array $x, array $y) : array { + if(count($x) != count($y)) { + throw new \Exception('Dimension'); + } + $b1 = self::getBeta1($x, $y); return ['b0' => self::getBeta0($x, $y, $b1), 'b1' => $b1]; } - - /** - * Get linear regression parameter beta 1. - * - * @param array $x Obersved x values - * @param array $y Observed y values - * - * @return float - * - * @since 1.0.0 - * @author Dennis Eichhorn - */ - private static function getBeta1(array $x, array $y) : float - { - $count = count($x); - $meanX = Average::arithmeticMean($x); - $meanY = Average::arithmeticMean($y); - - $sum1 = 0; - $sum2 = 0; - - for ($i = 0; $i < $count; $i++) { - $sum1 += ($y[$i] - $meanY) * ($x[$i] - $meanX); - $sum2 += ($x[$i] - $meanX) ** 2; - } - - return $sum1 / $sum2; - } - - /** - * Get linear regression parameter beta 0. - * - * @param array $x Obersved x values - * @param array $y Observed y values - * @param float $b1 Beta 1 - * - * @return float - * - * @since 1.0.0 - * @author Dennis Eichhorn - */ - private static function getBeta0(array $x, array $y, float $b1) : float - { - return Average::arithmeticMean($y) - $b1 * Average::arithmeticMean($x); - } - - /** - * Goodness of fit. - * - * Evaluating how well the observed data fit the linear regression model - * - * @param array $observed Obersved y values - * @param array $forecasted Forecasted y values - * - * @return float - * - * @since 1.0.0 - * @author Dennis Eichhorn - */ - public static function getGoodnessOfFit(array $observed, array $forecasted) : float - { - $countO = count($observed); - $countF = count($forecasted); - $sum1 = 0; - $sum2 = 0; - $meanY = Average::arithmeticMean($observed); - - for ($i = 0; $i < $countF; $i++) { - $sum1 += ($forecasted[$i] - $meanY) ** 2; - } - - for ($i = 0; $i < $countO; $i++) { - $sum2 += ($observed[$i] - $meanY) ** 2; - } - - return $sum1 / $sum2; - } - + /** * Standard error of the regression. * @@ -182,4 +77,59 @@ class LinearRegression return [$forecasted - $interval, $forecasted + $interval]; } + + /** + * Get linear regression parameter beta 1. + * + * @param array $x Obersved x values + * @param array $y Observed y values + * + * @return float + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + private static function getBeta1(array $x, array $y) : float + { + $count = count($x); + $meanX = Average::arithmeticMean($x); + $meanY = Average::arithmeticMean($y); + + $sum1 = 0; + $sum2 = 0; + + for ($i = 0; $i < $count; $i++) { + $sum1 += ($y[$i] - $meanY) * ($x[$i] - $meanX); + $sum2 += ($x[$i] - $meanX) ** 2; + } + + return $sum1 / $sum2; + } + + /** + * Get linear regression parameter beta 0. + * + * @param array $x Obersved x values + * @param array $y Observed y values + * @param float $b1 Beta 1 + * + * @return float + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + private static function getBeta0(array $x, array $y, float $b1) : float + { + return Average::arithmeticMean($y) - $b1 * Average::arithmeticMean($x); + } + + abstract public static function getRegression(array $x, array $y) : array; + + abstract public static function getSlope(float $b1, float $y, float $x) : float; + + abstract public static function getElasticity(float $b1, float $y, float $x): float; + + abstract public static function getBeta0(array $x, array $y, float b1) : float; + + abstract public static function getBeta1(array $x, array $y) : float; } \ No newline at end of file From ab09f71e941d4c816aa80b67de53c291dbed0c7d Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 19 Jul 2016 17:29:28 +0200 Subject: [PATCH 2/3] Pull out and removing nonsense --- Math/Statistic/Average.php | 47 +++++--------------------------------- 1 file changed, 6 insertions(+), 41 deletions(-) diff --git a/Math/Statistic/Average.php b/Math/Statistic/Average.php index 035e73b20..154b6a47e 100644 --- a/Math/Statistic/Average.php +++ b/Math/Statistic/Average.php @@ -114,19 +114,15 @@ class Average throw new \Exception('Periods'); } - if(!isset($weight)) { - $weight = array_fill(0, $count, 1); - } - - $sum = 0.0; $end = $symmetric ? $periods - 1 : 0; $end = $order % 2 === 0 ? $end - 1 : $end; + $start = $t - 1 -($periods - 2); - for($i = -($periods - 2); $i < $end; $i++) { - $sum += $weight[$t-1+$i] * $x[$t-1+$i]; + if(isset($weight)) { + return self::weightedAverage(array_slice($x, $start, $end-$start), array_slice($weight, $start, $end-$start)); + } else { + return self::arithmeticMean(array_slice($x, $start, $end-$start)); } - - return 1/ $order * $sum; } /** @@ -201,7 +197,6 @@ class Average * Example: ([1, 2, 2, 3, 4, 4, 2]) * * @param array $values Values - * @param int $offset Offset for outlier * * @return float * @@ -210,14 +205,8 @@ class Average * @since 1.0.0 * @author Dennis Eichhorn */ - public static function arithmeticMean(array $values, int $offset = 0) + public static function arithmeticMean(array $values) { - sort($values); - - if ($offset > 0) { - $values = array_slice($values, $offset, -$offset); - } - $count = count($values); if ($count === 0) { @@ -244,12 +233,6 @@ class Average */ public static function geometricMean(array $values, int $offset = 0) { - sort($values); - - if ($offset > 0) { - $values = array_slice($values, $offset, -$offset); - } - $count = count($values); if ($count === 0) { @@ -276,18 +259,6 @@ class Average */ public static function harmonicMean(array $values, int $offset = 0) { - sort($values); - - if ($offset > 0) { - $values = array_slice($values, $offset, -$offset); - } - - $count = count($values); - $sum = 0.0; - - foreach ($values as $value) { - if ($value === 0) { - throw new \Exception('Division zero'); } $sum += 1 / $value; @@ -311,12 +282,6 @@ class Average */ public static function angleMean($angles, int $offset = 0) { - sort($angles); - - if ($offset > 0) { - $angles = array_slice($angles, $offset, -$offset); - } - $y = $x = 0; $size = count($angles); From 6c961ea58e701239ace094a89e387338aed00547 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 19 Jul 2016 17:54:11 +0200 Subject: [PATCH 3/3] Creating application routing --- Module/InstallerAbstract.php | 16 +++++++++++++--- System/File/Directory.php | 12 ++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Module/InstallerAbstract.php b/Module/InstallerAbstract.php index 42612a3a6..a38e48ac9 100644 --- a/Module/InstallerAbstract.php +++ b/Module/InstallerAbstract.php @@ -150,9 +150,15 @@ class InstallerAbstract */ private static function initRoutes(InfoManager $info) { - self::installRoutes(ROOT_PATH . '/Web/Routes.php', ROOT_PATH . '/Modules/' . $info->getDirectory() . '/Admin/Routes/http.php'); - self::installRoutes(ROOT_PATH . '/Socket/Routes.php', ROOT_PATH . '/Modules/' . $info->getDirectory() . '/Admin/Routes/socket.php'); - self::installRoutes(ROOT_PATH . '/Console/Routes.php', ROOT_PATH . '/Modules/' . $info->getDirectory() . '/Admin/Routes/console.php'); + $directories = new Directory(ROOT_PATH . '/Modules/' . $info->getDirectory() . '/Admin/Routes'); + + foreach($directories as $key => $subdir) { + if($subdir instanceOf Directory) { + foreach($subdir as $key2 => $file) { + self::installRoutes(ROOT_PATH . '/' . $subdir->getName() . '/' . $file->getName() . '/Routes.php', $file->getPath()); + } + } + } } /** @@ -170,6 +176,10 @@ class InstallerAbstract */ private static function installRoutes(string $destRoutePath, string $srcRoutePath) { + if(!file_exists($destRoutePath)) { + mkdir($destRoutePath); + } + if (file_exists($destRoutePath) && file_exists($srcRoutePath)) { /** @noinspection PhpIncludeInspection */ $appRoutes = include $destRoutePath; diff --git a/System/File/Directory.php b/System/File/Directory.php index 5b36d9c6d..e25913afd 100644 --- a/System/File/Directory.php +++ b/System/File/Directory.php @@ -80,15 +80,11 @@ class Directory extends FileAbstract implements \Iterator, \ArrayAccess parent::index(); foreach (glob($this->path . DIRECTORY_SEPARATOR . $this->filter) as $filename) { - // todo: handle . and ..???!!! - if (is_dir($filename)) { - $file = new Directory($filename); - $file->index(); - } else { - $file = new File($filename); - } + if(strpos($filename, '.') === false) { + $file = is_dir($filename) ? new self($filename) : new File($filename); - $this->add($file); + $this->add($file); + } } }