Skip to content

Repository files navigation

C4.5 Algorithm - PHP

Packagist Version Tests PHP Version License

A PHP implementation of the C4.5 decision tree algorithm, with support for building a tree from Excel/CSV files or plain PHP arrays, classifying new data, evaluating predictions, and exporting the resulting tree as a string, JSON, array, or Graphviz DOT diagram.

📄 Example spreadsheet

Table of Contents

Features

  • Build C4.5 decision trees from Excel, CSV, or PHP array data.
  • Calculate Gain, Split Info, and Gain Ratio.
  • Classify new records using a built decision tree.
  • Handle missing split-attribute values during classification by falling back to the majority branch.
  • Evaluate a tree against labeled test data.
  • Export trees as:
    • String
    • JSON
    • PHP array
    • Graphviz DOT
  • Use PSR-4 autoloading through Composer.
  • Includes a PHPUnit test suite and GitHub Actions CI.
  • Uses an indexed data lookup internally to reduce repeated full-dataset scans while building trees.

Requirements

PHP 5.x–7.x compatibility: If your project is running PHP 5.x, 6.x, or 7.x, use a package version below 2.0.0.

Version 2.0.0 and later require PHP ^8.1. For older PHP versions, install the latest compatible 1.x release:

composer require medansoftware/c45-algorithm-php:"<2.0.0"

Installation

Install via Composer:

composer require medansoftware/c45-algorithm-php

Quick Start

From an Excel File

$c45 = new Algorithm\C45('examples/example.xlsx', 'PLAY');

$tree = $c45->initialize()->buildTree();

echo $tree->toString();

Or, using the fluent setup:

$c45 = new Algorithm\C45();
$c45->loadFile('examples/example.xlsx');
$c45->setTargetAttribute('PLAY');

$tree = $c45->initialize()->buildTree();

echo $tree->toString();

From a PHP Array

$data = [
    ['OUTLOOK' => 'Sunny',  'TEMPERATURE' => 'Hot',  'HUMIDITY' => 'High',   'WINDY' => 'False', 'PLAY' => 'No'],
    ['OUTLOOK' => 'Sunny',  'TEMPERATURE' => 'Hot',  'HUMIDITY' => 'High',   'WINDY' => 'True',  'PLAY' => 'No'],
    ['OUTLOOK' => 'Cloudy', 'TEMPERATURE' => 'Hot',  'HUMIDITY' => 'High',   'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Rainy',  'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'High',   'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Rainy',  'TEMPERATURE' => 'Cool', 'HUMIDITY' => 'Normal', 'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Rainy',  'TEMPERATURE' => 'Cool', 'HUMIDITY' => 'Normal', 'WINDY' => 'True',  'PLAY' => 'No'],
    ['OUTLOOK' => 'Cloudy', 'TEMPERATURE' => 'Cool', 'HUMIDITY' => 'Normal', 'WINDY' => 'True',  'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Sunny',  'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'High',   'WINDY' => 'False', 'PLAY' => 'No'],
    ['OUTLOOK' => 'Sunny',  'TEMPERATURE' => 'Cool', 'HUMIDITY' => 'Normal', 'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Rainy',  'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'Normal', 'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Sunny',  'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'Normal', 'WINDY' => 'True',  'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Cloudy', 'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'High',   'WINDY' => 'True',  'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Cloudy', 'TEMPERATURE' => 'Hot',  'HUMIDITY' => 'Normal', 'WINDY' => 'False', 'PLAY' => 'Yes'],
    ['OUTLOOK' => 'Rainy',  'TEMPERATURE' => 'Mild', 'HUMIDITY' => 'High',   'WINDY' => 'True',  'PLAY' => 'No'],
];

$input = new Algorithm\C45\DataInput();
$input->setData($data);
$input->setAttributes(['OUTLOOK', 'TEMPERATURE', 'HUMIDITY', 'WINDY', 'PLAY']);

$c45 = new Algorithm\C45();
$c45->c45 = $input;
$c45->setTargetAttribute('PLAY');

$tree = $c45->initialize()->buildTree();

echo $tree->toString();

From a CSV File

$input = new Algorithm\C45\DataInput();
$input->loadCsv('example.csv'); // delimiter defaults to ','

$c45 = new Algorithm\C45();
$c45->c45 = $input;
$c45->setTargetAttribute('PLAY');

$tree = $c45->initialize()->buildTree();

echo $tree->toString();

Classifying New Data

$newData = [
    'OUTLOOK'     => 'Sunny',
    'TEMPERATURE' => 'Hot',
    'HUMIDITY'    => 'High',
    'WINDY'       => 'False',
];

echo $tree->classify($newData); // "No"

Missing Values

If the split attribute required by a tree node is missing (null or an empty string), classification falls back to the branch with the highest number of training instances at that node.

This is useful when prediction data is incomplete:

$newData = [
    'TEMPERATURE' => 'Hot',
    'HUMIDITY'    => 'High',
    'WINDY'       => 'False',
    // OUTLOOK is missing
];

echo $tree->classify($newData);

If a value is present but was never observed during training, the result remains:

unclassified

Evaluating Accuracy

Measure how well a built tree performs against a labeled test set:

$result = $c45->evaluate($tree, $testData);

echo $result['accuracy'];          // e.g. 0.86
echo $result['correct'] . '/' . $result['total'];
print_r($result['misclassified']);

The returned structure is:

[
    'accuracy'      => 0.86,
    'correct'       => 86,
    'total'         => 100,
    'misclassified' => [
        // rows that were classified incorrectly
    ],
]

Output Formats

As String

echo $tree->toString();

As JSON

echo $tree->toJson();

As Array

print_r($tree->toArray());

As Graphviz DOT Diagram

Useful for visualizing the tree with tools like Graphviz Online or the dot CLI:

file_put_contents('tree.dot', $tree->toDot());
dot -Tpng tree.dot -o tree.png

The repository contains a generated example based on the Play Tennis dataset:

Decision tree generated from the Play Tennis dataset

Project Structure

.
├── .github/
│   └── workflows/
│       └── tests.yml
├── examples/
│   ├── example.xlsx
│   ├── tree.dot
│   └── tree.png
├── src/
│   ├── C45.php
│   └── C45/
│       ├── Calculator/
│       ├── DataInput/
│       ├── DataInput.php
│       └── TreeNode.php
├── tests/
├── composer.json
├── phpunit.xml
├── CHANGELOG.md
└── LICENSE

Running Tests

Install dependencies:

composer install

Run the test suite:

composer test

The GitHub Actions workflow runs the PHPUnit suite on PHP 8.1, 8.2, and 8.3 for pushes and pull requests targeting the master and develop branches.

Upgrading from 2.0.0

The public namespace remains Algorithm\C45, but the package now uses Composer PSR-4 autoloading instead of classmap autoloading.

The implementation also introduces stricter parameter and return types. Existing valid usage should continue to work, but invalid argument types may now fail earlier with a TypeError.

The main behavioral change is how incomplete data is handled:

  • Missing values (null or '') are excluded from attribute classes and criteria indexes.
  • During classification, a missing split attribute falls back to the majority branch.
  • An unseen, non-empty attribute value still returns unclassified.

No application-level namespace migration is required.

License

Released under the MIT License.


Reference

Made with ❤️ + ☕ ~ Agung Dirgantara

About

An open-source PHP library implementing the C4.5 decision tree algorithm for classification and data mining applications.

Topics

Resources

Stars

13 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages