|
2 | 2 |
|
3 | 3 | **Optimal Binning Histograms for Python** |
4 | 4 |
|
5 | | -Khisto is a Python library for creating histograms and cumulative distribution functions (ECDFs) using the **Khiops optimal binning algorithm**. Unlike standard histograms that use fixed-width bins or simple heuristics, Khisto automatically determines the optimal number of bins and their variable widths to best represent the underlying data distribution. |
6 | | - |
7 | | -It provides a core Array API for data analysis and drop-in replacements for **Matplotlib** and **Plotly** plotting functions. |
| 5 | +Khisto is a Python library for creating histograms using the **Khiops optimal binning algorithm**. Unlike standard histograms that use fixed-width bins or simple heuristics, Khisto automatically determines the optimal number of bins and their variable widths to best represent the underlying data distribution. |
8 | 6 |
|
9 | 7 | ## Features |
10 | 8 |
|
11 | 9 | - **Optimal Binning**: Uses the MODL (Minimum Description Length) principle to find the best discretization. |
12 | 10 | - **Variable-Width Bins**: Captures dense regions with fine bins and sparse regions with wider bins. |
13 | | -- **Array API Support**: Works with NumPy, PyTorch, JAX, CuPy, and more (via `narwhals` and `array-api-compat`). |
14 | | -- **Plotting Integrations**: |
15 | | - - **Matplotlib**: `khisto.matplotlib.hist` (drop-in replacement for `plt.hist`) |
16 | | - - **Plotly**: `khisto.plotly.histogram` (compatible with Plotly Express) |
| 11 | +- **NumPy Compatible**: Drop-in replacement for `numpy.histogram`. |
| 12 | +- **Matplotlib Integration**: `khisto.matplotlib.hist` works like `plt.hist`. |
| 13 | +- **Minimal Dependencies**: Only requires NumPy (matplotlib optional for plotting). |
17 | 14 |
|
18 | 15 | ## Installation |
19 | 16 |
|
20 | | -### From PyPI |
21 | | - |
22 | | -Install the core library: |
23 | | - |
24 | 17 | ```bash |
25 | 18 | pip install khisto |
26 | 19 | ``` |
27 | 20 |
|
28 | | -Install with plotting support: |
| 21 | +With matplotlib support: |
29 | 22 |
|
30 | 23 | ```bash |
31 | | -# For Matplotlib support |
32 | 24 | pip install "khisto[matplotlib]" |
33 | | - |
34 | | -# For Plotly support |
35 | | -pip install "khisto[plotly]" |
36 | | - |
37 | | -# For everything |
38 | | -pip install "khisto[all]" |
39 | 25 | ``` |
40 | 26 |
|
41 | | -## Usage |
42 | | - |
43 | | -### 1. Core Array API |
| 27 | +## Quick Start |
44 | 28 |
|
45 | | -Compute optimal histograms and ECDFs directly from arrays. |
| 29 | +### NumPy-like API |
46 | 30 |
|
47 | 31 | ```python |
48 | 32 | import numpy as np |
49 | | -from khisto.array import histogram, ecdf |
| 33 | +from khisto import histogram |
50 | 34 |
|
51 | 35 | # Generate data |
52 | 36 | data = np.random.normal(0, 1, 1000) |
53 | 37 |
|
54 | | -# Compute optimal histogram (returns density values and bin edges) |
55 | | -values, edges = histogram(data, granularity="best") |
| 38 | +# Compute optimal histogram (drop-in replacement for np.histogram) |
| 39 | +hist, bin_edges = histogram(data) |
56 | 40 |
|
57 | | -# Compute ECDF |
58 | | -cdf = ecdf(data) |
59 | | -print(cdf(0.0)) # Evaluate CDF at x=0 |
60 | | -``` |
| 41 | +# With density normalization |
| 42 | +density, bin_edges = histogram(data, density=True) |
| 43 | + |
| 44 | +# Limit maximum number of bins |
| 45 | +hist, bin_edges = histogram(data, max_bins=10) |
61 | 46 |
|
62 | | -### 2. Matplotlib Integration |
| 47 | +# Specify range |
| 48 | +hist, bin_edges = histogram(data, range=(-2, 2)) |
| 49 | +``` |
63 | 50 |
|
64 | | -Use Khisto as a drop-in replacement for `plt.hist`. |
| 51 | +### Matplotlib Integration |
65 | 52 |
|
66 | 53 | ```python |
67 | | -import matplotlib.pyplot as plt |
68 | | -import khisto.matplotlib as khm |
69 | 54 | import numpy as np |
| 55 | +import matplotlib.pyplot as plt |
| 56 | +from khisto.matplotlib import hist |
70 | 57 |
|
71 | 58 | data = np.random.normal(0, 1, 1000) |
72 | 59 |
|
73 | | -# Standard Matplotlib histogram |
74 | | -# plt.hist(data, bins=30) |
75 | | - |
76 | | -# Khisto optimal histogram |
77 | | -khm.hist(data, density=True) |
| 60 | +# Create optimal histogram plot |
| 61 | +n, bins, patches = hist(data) |
| 62 | +plt.show() |
78 | 63 |
|
| 64 | +# With density |
| 65 | +n, bins, patches = hist(data, density=True) |
| 66 | +plt.xlabel('Value') |
| 67 | +plt.ylabel('Density') |
79 | 68 | plt.show() |
80 | 69 | ``` |
81 | 70 |
|
82 | | -### 3. Plotly Integration |
| 71 | +## API Reference |
83 | 72 |
|
84 | | -Create interactive histograms with optimal binning. |
| 73 | +### `khisto.histogram` |
85 | 74 |
|
86 | 75 | ```python |
87 | | -import plotly.express as px |
88 | | -import khisto.plotly as khp |
| 76 | +def histogram( |
| 77 | + a: ArrayLike, |
| 78 | + range: Optional[tuple[float, float]] = None, |
| 79 | + max_bins: Optional[int] = None, |
| 80 | + density: bool = False, |
| 81 | +) -> tuple[ndarray, ndarray] |
| 82 | +``` |
| 83 | + |
| 84 | +Compute an optimal histogram using the Khiops binning algorithm. |
89 | 85 |
|
90 | | -df = px.data.tips() |
| 86 | +**Parameters:** |
| 87 | +- `a`: Input data. The histogram is computed over the flattened array. |
| 88 | +- `range`: The lower and upper range of the bins. Values outside are ignored. |
| 89 | +- `max_bins`: Maximum number of bins. If None, the algorithm selects optimal. |
| 90 | +- `density`: If True, return probability density. If False, return counts. |
91 | 91 |
|
92 | | -# Create histogram |
93 | | -fig = khp.histogram(df, x="total_bill", color="sex") |
94 | | -fig.show() |
| 92 | +**Returns:** |
| 93 | +- `hist`: The values of the histogram (counts or density). |
| 94 | +- `bin_edges`: The bin edges (length = len(hist) + 1). |
| 95 | + |
| 96 | +### `khisto.matplotlib.hist` |
| 97 | + |
| 98 | +```python |
| 99 | +def hist( |
| 100 | + x: ArrayLike, |
| 101 | + range: Optional[tuple[float, float]] = None, |
| 102 | + max_bins: Optional[int] = None, |
| 103 | + density: bool = False, |
| 104 | + histtype: str = "bar", |
| 105 | + orientation: Literal["vertical", "horizontal"] = "vertical", |
| 106 | + log: bool = False, |
| 107 | + color: Optional[str] = None, |
| 108 | + label: Optional[str] = None, |
| 109 | + ax: Optional[Axes] = None, |
| 110 | + **kwargs, |
| 111 | +) -> tuple[ndarray, ndarray, Any] |
95 | 112 | ``` |
96 | 113 |
|
97 | | -## Development |
| 114 | +Plot an optimal histogram using matplotlib. |
| 115 | + |
| 116 | +**Parameters:** |
| 117 | +- `x`: Input data. |
| 118 | +- `range`: The lower and upper range of the bins. |
| 119 | +- `max_bins`: Maximum number of bins. |
| 120 | +- `density`: If True, plot probability density. |
| 121 | +- `histtype`: Type of histogram (`"bar"`, `"step"`, `"stepfilled"`). |
| 122 | +- `orientation`: `"vertical"` or `"horizontal"`. |
| 123 | +- `log`: If True, set log scale on the value axis. |
| 124 | +- `ax`: Matplotlib axes to plot on. |
| 125 | + |
| 126 | +**Returns:** |
| 127 | +- `n`: The histogram values. |
| 128 | +- `bins`: The bin edges. |
| 129 | +- `patches`: The matplotlib patches. |
98 | 130 |
|
99 | | -This project uses [uv](https://github.com/astral-sh/uv) for dependency management and packaging. |
| 131 | +## How It Works |
100 | 132 |
|
101 | | -### Installation from Source |
| 133 | +Khisto uses the Khiops optimal binning algorithm based on the MODL (Minimum Optimal Description Length) principle. Instead of using fixed-width bins like traditional histograms, it: |
102 | 134 |
|
103 | | -1. **Install uv**: |
104 | | - ```bash |
105 | | - curl -LsSf https://astral.sh/uv/install.sh | sh |
106 | | - ``` |
| 135 | +1. Analyzes the data distribution |
| 136 | +2. Finds bin boundaries that minimize information loss |
| 137 | +3. Creates variable-width bins that adapt to data density |
107 | 138 |
|
108 | | -2. **Clone the repository**: |
109 | | - ```bash |
110 | | - git clone https://github.com/yourusername/khisto-python.git |
111 | | - cd khisto-python |
112 | | - ``` |
| 139 | +This results in histograms that better represent the underlying distribution, with finer bins in dense regions and wider bins in sparse regions. |
113 | 140 |
|
114 | | -3. **Sync dependencies**: |
115 | | - Create a virtual environment and install all dependencies (including dev and optional extras): |
116 | | - ```bash |
117 | | - uv sync --all-extras |
118 | | - ``` |
| 141 | +## Development |
119 | 142 |
|
120 | | -4. **Run Tests**: |
121 | | - ```bash |
122 | | - uv run pytest |
123 | | - ``` |
| 143 | +```bash |
| 144 | +# Clone repository |
| 145 | +git clone https://github.com/khiops/khisto-python.git |
| 146 | +cd khisto-python |
124 | 147 |
|
125 | | -### Project Structure |
| 148 | +# Install with dev dependencies |
| 149 | +pip install -e ".[matplotlib]" |
126 | 150 |
|
127 | | -- `src/khisto/array`: Core algorithms and Array API. |
128 | | -- `src/khisto/matplotlib`: Matplotlib plotting backends. |
129 | | -- `src/khisto/plotly`: Plotly plotting backends. |
130 | | -- `src/khiops`: C++ backend source (requires CMake to build if modifying). |
| 151 | +# Run tests |
| 152 | +pytest |
| 153 | +``` |
131 | 154 |
|
132 | 155 | ## License |
133 | 156 |
|
|
0 commit comments