Skip to content
← Back to Functions
Code

Outlier Detector

Flags numeric outliers using IQR or z-score style methods.

Function signature

ogDetectNumericOutliers(values = array(), options = array())

Categories

  • Analytics and Reports

Parameters

valuesNumeric values.optionsOptional keys: method, threshold. Recognized keys: `method`, `threshold`.

Return value

Short public-safe status message.

  • method
  • bounds
  • outliers
  • outlier_count

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Flags numeric outliers using IQR or z-score style methods.
 *
 * @param array $values Numeric values.
 * @param array $options Optional keys: method, threshold.
 * @return array Outlier report with original indexes and reason text.
 */
function ogDetectNumericOutliers($values = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($values)) {
		$result['message'] = 'Values must be an array.';
		return $result;
	}

	if (!is_array($options)) {
		$options = array();
	}

	$method = 'iqr';
	if (!empty($options['method'])) {
		$method = strtolower((string)$options['method']);
	}

	$threshold = 1.5;
	if (isset($options['threshold']) && is_numeric($options['threshold'])) {
		$threshold = (float)$options['threshold'];
	}

	$records = array();
	foreach ($values as $index => $value) {
		if (is_numeric($value)) {
			$records[] = array('index' => $index, 'value' => (float)$value);
		}
	}

	if (count($records) < 4) {
		$result['message'] = 'At least four numeric values are required for outlier detection.';
		return $result;
	}

	$numeric_values = array();
	foreach ($records as $record) {
		$numeric_values[] = $record['value'];
	}
	sort($numeric_values, SORT_NUMERIC);

	$outliers = array();
	$bounds = array();
	if ($method == 'zscore') {
		$average = array_sum($numeric_values) / count($numeric_values);
		$variance_total = 0;
		foreach ($numeric_values as $value) {
			$variance_total += pow($value - $average, 2);
		}
		$standard_deviation = sqrt($variance_total / count($numeric_values));
		if ($standard_deviation == 0) {
			$standard_deviation = 1;
		}
		foreach ($records as $record) {
			$score = abs(($record['value'] - $average) / $standard_deviation);
			if ($score > $threshold) {
				$outliers[] = array('index' => $record['index'], 'value' => $record['value'], 'reason' => 'zscore:' . $score);
			}
		}
		$bounds = array('average' => $average, 'standard_deviation' => $standard_deviation, 'threshold' => $threshold);
	} else {
		$q1_rank = 0.25 * (count($numeric_values) - 1);
		$q1_lower = (int)floor($q1_rank);
		$q1_upper = (int)ceil($q1_rank);
		$q1_weight = $q1_rank - $q1_lower;
		$q1 = $numeric_values[$q1_lower];
		if ($q1_upper != $q1_lower) {
			$q1 = $numeric_values[$q1_lower] + (($numeric_values[$q1_upper] - $numeric_values[$q1_lower]) * $q1_weight);
		}

		$q3_rank = 0.75 * (count($numeric_values) - 1);
		$q3_lower = (int)floor($q3_rank);
		$q3_upper = (int)ceil($q3_rank);
		$q3_weight = $q3_rank - $q3_lower;
		$q3 = $numeric_values[$q3_lower];
		if ($q3_upper != $q3_lower) {
			$q3 = $numeric_values[$q3_lower] + (($numeric_values[$q3_upper] - $numeric_values[$q3_lower]) * $q3_weight);
		}
		$iqr = $q3 - $q1;
		$low = $q1 - ($iqr * $threshold);
		$high = $q3 + ($iqr * $threshold);
		foreach ($records as $record) {
			if ($record['value'] < $low || $record['value'] > $high) {
				$outliers[] = array('index' => $record['index'], 'value' => $record['value'], 'reason' => 'outside_iqr_bounds');
			}
		}
		$bounds = array('q1' => $q1, 'q3' => $q3, 'iqr' => $iqr, 'low' => $low, 'high' => $high);
	}

	$result['success'] = true;
	$result['message'] = 'Outlier detection completed.';
	$result['data'] = array(
		'method' => $method,
		'bounds' => $bounds,
		'outliers' => $outliers,
		'outlier_count' => count($outliers)
	);

	return $result;
}