Skip to content
← Back to Functions
Code

Simple Trend Analyzer

Calculates direction, percent change, and volatility across a numeric time series.

Function signature

ogAnalyzeSimpleTrend(points = array(), options = array())

Categories

  • Analytics and Reports

Parameters

pointsNumeric values or rows with value/date keys.optionsOptional keys: value_key, date_key, minimum_points. Recognized keys: `date_key`, `minimum_points`, `value_key`.

Return value

Short public-safe status message.

  • count
  • first
  • last
  • minimum
  • maximum
  • average
  • change
  • percent_change
  • direction
  • volatility

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.
 */

/**
 * Calculates direction, percent change, and volatility across a numeric time series.
 *
 * Accepts a simple numeric array or dated rows containing value/date keys. Dated rows
 * are sorted before analysis so the first and last values reflect the actual series order.
 *
 * @param array $points Numeric values or rows with value/date keys.
 * @param array $options Optional keys: value_key, date_key, minimum_points.
 * @return array Trend report with count, change, percent change, direction, and volatility.
 */
function ogAnalyzeSimpleTrend($points = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

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

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

	$value_key = 'value';
	if (!empty($options['value_key'])) {
		$value_key = (string)$options['value_key'];
	}

	$date_key = 'date';
	if (!empty($options['date_key'])) {
		$date_key = (string)$options['date_key'];
	}

	$minimum_points = 2;
	if (!empty($options['minimum_points'])) {
		$minimum_points = (int)$options['minimum_points'];
	}
	if ($minimum_points < 2) {
		$minimum_points = 2;
	}

	$series = array();
	foreach ($points as $index => $point) {
		$value = null;
		$date_sort = $index;
		if (is_array($point)) {
			if (array_key_exists($value_key, $point) && is_numeric($point[$value_key])) {
				$value = (float)$point[$value_key];
			}
			if (array_key_exists($date_key, $point)) {
				$timestamp = strtotime((string)$point[$date_key]);
				if ($timestamp !== false) {
					$date_sort = $timestamp;
				}
			}
		} else {
			if (is_numeric($point)) {
				$value = (float)$point;
			}
		}

		if ($value !== null) {
			$series[] = array(
				'sort' => $date_sort,
				'value' => $value
			);
		}
	}

	if (count($series) < $minimum_points) {
		$result['message'] = 'Not enough numeric points were supplied.';
		return $result;
	}

	usort($series, function($left, $right) {
		if ($left['sort'] == $right['sort']) {
			return 0;
		}
		if ($left['sort'] < $right['sort']) {
			return -1;
		}
		return 1;
	});

	$values = array();
	foreach ($series as $point) {
		$values[] = $point['value'];
	}

	$count = count($values);
	$sum = array_sum($values);
	$average = $sum / $count;
	$first = $values[0];
	$last = $values[$count - 1];
	$change = $last - $first;
	$percent_change = 0;
	if ($first != 0) {
		$percent_change = ($change / abs($first)) * 100;
	}

	$variance_total = 0;
	foreach ($values as $value) {
		$variance_total += pow($value - $average, 2);
	}
	$volatility = sqrt($variance_total / $count);

	$direction = 'flat';
	if ($change > 0) {
		$direction = 'up';
	} elseif ($change < 0) {
		$direction = 'down';
	}

	$result['success'] = true;
	$result['message'] = 'Trend analysis completed.';
	$result['data'] = array(
		'count' => $count,
		'first' => $first,
		'last' => $last,
		'minimum' => min($values),
		'maximum' => max($values),
		'average' => $average,
		'change' => $change,
		'percent_change' => $percent_change,
		'direction' => $direction,
		'volatility' => $volatility
	);

	return $result;
}