Skip to main content

c50py: C5.0 decision trees for Python, with native categorical splits

PyPI Python Tests License: MIT Open In Colab

Decision trees were born able to split on categories. Quinlan's ID3, C4.5 and C5.0 ask questions like "is the sector in {mining, tourism, construction}?". scikit-learn's CART only splits on numbers, so categorical columns have to be one-hot encoded first, and one question becomes a staircase of dummy variables.

c50py brings the C5.0 way back to Python, as a scikit-learn estimator:

Same data, CART with one-hot encoding (8 leaves) vs c50py (2 leaves), same test accuracy

Same data, same test accuracy (0.782), both pruned by 5-fold cross-validation. CART needs three dummy variables and 8 leaves; C5.0 asks one question. Reproduce it with examples/make_readme_figure.py.

Install

pip install c50py

Python 3.9+, NumPy and scikit-learn. pandas is optional (DataFrames are used directly) and matplotlib is needed for plot_tree.

Quickstart

import pandas as pd
from sklearn.model_selection import train_test_split
from c50py import C5Classifier

url = "https://raw.githubusercontent.com/daviddiazsolis/c50py/main/examples/titanic.csv"
df = pd.read_csv(url)
X = df[["pclass", "sex", "age", "fare", "embarked"]].astype({"pclass": "category"})
y = df["survived"].map({0: "died", 1: "survived"})
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)

# no one-hot encoding, no imputation: sex, embarked and pclass are used as
# categories, and the missing ages are handled by the tree
clf = C5Classifier(min_samples_leaf=10).fit(X_tr, y_tr)
print(f"test accuracy: {clf.score(X_te, y_te):.3f}")      # 0.794

for rule in clf.export_rules():
    print(rule)
# sex IN {male} => died
# sex NOT IN {male} AND pclass IN {3} AND fare <= 23.3500 => survived
# sex NOT IN {male} AND pclass IN {3} AND fare > 23.3500 => died
# sex NOT IN {male} AND pclass NOT IN {3} => survived

clf.plot_tree(filled=True, rounded=True)   # same look and options as sklearn.tree.plot_tree

A scikit-learn estimator

C5Classifier and C5Regressor pass scikit-learn's check_estimator, so they work with clone, Pipeline, cross_val_score, GridSearchCV and the rest of the ecosystem:

from sklearn.model_selection import GridSearchCV

grid = GridSearchCV(C5Classifier(), {"cf": [0.1, 0.25, 0.5], "min_samples_leaf": [1, 5, 20]}, cv=5)
grid.fit(X_tr, y_tr)
print(grid.best_params_, grid.score(X_te, y_te))

What C5.0 does differently from CART

scikit-learn CART c50py (C5.0)
Categorical columns must be encoded (one-hot, ordinal) split natively: sector in {a, b, c}
Missing values imputed, or one learned direction cases go down both branches with fractional weights, as in C4.5/C5.0
Split criterion Gini or entropy gain ratio, among splits with at least average gain, with C4.5's MDL penalty for numeric thresholds
Pruning cost-complexity (ccp_alpha) pessimistic error pruning with confidence factor cf (C4.5's AddErrs)
Boosting separate AdaBoostClassifier built in: trials=10

Main features

  • Classification and regression: C5Classifier, C5Regressor.
  • Categorical columns detected automatically: pandas category, object, string and bool columns, or object columns holding strings. You can also list them with categorical_features=["region", 3] (names or indices).
  • Missing values (None, np.nan, pd.NA) in training and prediction.
  • Case weights through sample_weight.
  • Boosting with trials > 1.
  • Readable output: export_rules() (one rule per leaf), predict_rule(X) (the rule each case follows) and print_tree().
  • Drawings identical to scikit-learn: plot_tree, export_graphviz and export_text, with the same parameters. Boxes use c50py's own palette so a C5.0 tree is easy to tell apart from a CART tree (palette="sklearn" gives scikit-learn's colours).

Regression

from sklearn.datasets import load_diabetes
from c50py import C5Regressor

X, y = load_diabetes(return_X_y=True, as_frame=True)
reg = C5Regressor(min_samples_leaf=20).fit(X, y)
print(reg.export_rules()[:3])

Key parameters

Parameter Default Meaning
cf 0.25 Confidence factor for pruning; smaller values prune more.
min_samples_leaf 1 (classifier), 2 (regressor) Minimum (weighted) cases in each child.
trials 1 Number of boosted trees (classifier).
categorical_features None Extra columns to treat as categorical, by name or index.
infer_categorical True Detect categorical columns from their dtype or content.
max_categories_exhaustive 12 Up to this many categories every binary subset is evaluated.
max_depth None Optional depth limit.

The Usage Guide covers every option.

How close is it to Quinlan's C5.0?

c50py is a from-scratch Python implementation that follows C4.5/C5.0 in the split criterion, the handling of missing values and the pessimistic pruning. Some parts of the original are not (yet) implemented:

  • splits are binary; categorical splits group categories into two subsets (C5.0's default is one branch per category, with subset grouping as an option);
  • no winnowing (C5.0's built-in feature selection);
  • no rulesets: export_rules() returns the rules of the tree, one per leaf, not C5.0's simplified, independent rules;
  • boosting follows AdaBoost.M1/SAMME reweighting, close to but not identical with C5.0's;
  • no misclassification costs.

Rulesets and winnowing are on the roadmap. Contributions are welcome.

Citation

If you use c50py in research, please cite it (a JOSS submission is in preparation; meanwhile cite the repository):

@software{diaz_solis_c50py,
  author = {D{\'i}az Sol{\'i}s, David},
  title  = {c50py: C5.0 decision trees for Python},
  url    = {https://github.com/daviddiazsolis/c50py},
  year   = {2026}
}

Contributing and license

Bug reports and pull requests are welcome; see CONTRIBUTING.md and the CHANGELOG. c50py is released under the MIT License. The drawing code is adapted from scikit-learn (BSD-3-Clause).

Metadata

Release files for c50py 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for c50py 0.4.2
File Size Uploaded
c50py-0.4.2.tar.gz 49.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for c50py 0.4.2
File Interpreter ABI Platform
c50py-0.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 91.5 kB

Release files / c50py-0.4.2.tar.gz

Download URL c50py-0.4.2.tar.gz
Size 49.4 kB
Tags Source
SHA-256 checksum
How to use checksums
49d3c70944f1a3a0c21996210f43bf6a0a3a7a030de677db39de8e75686d3b83
BLAKE2b-256 checksum
How to use checksums
3380dbebd9b77b9ac2dda22261dcf231852f0e50c5bd7428bb6d876444153559
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / c50py-0.4.2-py3-none-any.whl

Download URL c50py-0.4.2-py3-none-any.whl
Size 42.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c51765c55587dc173edc0aa7119dbb72f4b3835dc1486cedf37734e51cf2d01e
BLAKE2b-256 checksum
How to use checksums
01b92e1efe0856be8cfce9f27d3871be2c01fced9dc29f3f3ed42fa4f84d1777
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

This release

0.4.2 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page