Skip to content

Commit f9fb405

Browse files
authored
Merge pull request #12 from KhiopsML/json
Refactor histogram computation: remove cli_adapter, add backend tests
2 parents cb458d2 + f5940e3 commit f9fb405

9 files changed

Lines changed: 604 additions & 845 deletions

File tree

docs/core/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ Main Modules
1111
:recursive:
1212
:nosignatures:
1313

14-
cli_adapter
14+
backend

sandbox/khisto_demo.ipynb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@
453453
},
454454
{
455455
"cell_type": "code",
456-
"execution_count": 13,
456+
"execution_count": null,
457457
"id": "1190f8aa",
458458
"metadata": {},
459459
"outputs": [
@@ -470,26 +470,26 @@
470470
" Granularity 3: 4 bins\n",
471471
" Granularity 4: 7 bins\n",
472472
" Granularity 5: 8 bins\n",
473-
" Granularity 6: 7 bins ← BEST\n"
473+
" Granularity 7: 7 bins ← BEST\n"
474474
]
475475
}
476476
],
477477
"source": [
478-
"from khisto.core import compute_histogram\n",
478+
"from khisto.core import compute_histograms\n",
479479
"\n",
480480
"# Get all granularity levels\n",
481-
"results = compute_histogram(data)\n",
481+
"results = compute_histograms(data)\n",
482482
"\n",
483483
"print(f\"Number of granularity levels: {len(results)}\")\n",
484484
"print(\"\\nGranularity levels:\")\n",
485485
"for r in results:\n",
486486
" marker = \" ← BEST\" if r.is_best else \"\"\n",
487-
" print(f\" Granularity {r.granularity}: {len(r.frequency)} bins{marker}\")"
487+
" print(f\" Granularity {r.granularity}: {len(r.frequencies)} bins{marker}\")"
488488
]
489489
},
490490
{
491491
"cell_type": "code",
492-
"execution_count": 14,
492+
"execution_count": null,
493493
"id": "bf2ba150",
494494
"metadata": {},
495495
"outputs": [
@@ -512,8 +512,8 @@
512512
"\n",
513513
"for i, r in enumerate(results[:n_levels]):\n",
514514
" ax = axes[i]\n",
515-
" ax.stairs(r.frequency, r.bin_edges, fill=True, alpha=0.7)\n",
516-
" title = f\"Granularity {r.granularity} ({len(r.frequency)} bins)\"\n",
515+
" ax.stairs(r.frequencies, r.bin_edges, fill=True, alpha=0.7)\n",
516+
" title = f\"Granularity {r.granularity} ({len(r.frequencies)} bins)\"\n",
517517
" if r.is_best:\n",
518518
" title += \" ★ BEST\"\n",
519519
" ax.set_facecolor(\"#ffffee\")\n",

src/khisto/array/histogram/api.py

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
import numpy as np
1212
from numpy.typing import ArrayLike, NDArray
1313

14-
from khisto.core import HistogramResult, compute_histogram
14+
from khisto.core import HistogramResult, compute_histograms
1515

1616

1717
def _select_histogram(
18-
results: list[HistogramResult],
18+
histogram_results: list[HistogramResult],
1919
max_bins: Optional[int] = None,
2020
) -> HistogramResult:
2121
"""Select the appropriate histogram from the list of results.
@@ -35,38 +35,20 @@ def _select_histogram(
3535
if max_bins is not None:
3636
# Find the finest granularity that respects max_bins
3737
selected = None
38-
for r in results:
38+
for r in histogram_results:
3939
if len(r) <= max_bins:
4040
selected = r
4141
else:
4242
break
4343
# If no histogram respects the constraint, use the coarsest one
44-
return selected if selected is not None else results[0]
44+
return selected if selected is not None else histogram_results[0]
4545
else:
4646
# Return the best (optimal) histogram
47-
for r in results:
47+
for r in reversed(histogram_results):
4848
if r.is_best:
4949
return r
5050
# Fallback to finest granularity if no best is marked
51-
return results[-1]
52-
53-
54-
def _apply_cumulative(
55-
hist_values: NDArray[np.float64],
56-
bin_edges: NDArray[np.float64],
57-
*,
58-
density: bool,
59-
reverse: bool = False,
60-
) -> NDArray[np.float64]:
61-
"""Accumulate histogram values using matplotlib-compatible semantics."""
62-
if density:
63-
source_values = hist_values * np.diff(bin_edges)
64-
else:
65-
source_values = hist_values
66-
67-
if reverse:
68-
return np.cumsum(source_values[::-1])[::-1]
69-
return np.cumsum(source_values)
51+
return histogram_results[-1]
7052

7153

7254
def histogram(
@@ -125,15 +107,20 @@ def histogram(
125107
"""
126108
arr = np.asarray(a, dtype=np.float64).flatten()
127109

110+
if max_bins is not None and max_bins <= 0:
111+
raise ValueError("max_bins must be a positive integer or None.")
112+
128113
# Filter values by range if specified
129114
if range is not None:
130115
min_val, max_val = range
131116
arr = arr[(arr >= min_val) & (arr <= max_val)]
132117

133-
results = compute_histogram(arr)
134-
result = _select_histogram(results, max_bins=max_bins)
118+
histogram_results = compute_histograms(arr)
119+
histogram_result = _select_histogram(histogram_results, max_bins=max_bins)
135120

136121
if density:
137-
return result.density.copy(), result.bin_edges.copy()
122+
return histogram_result.densities.copy(), histogram_result.bin_edges.copy()
138123
else:
139-
return result.frequency.astype(np.float64), result.bin_edges.copy()
124+
return histogram_result.frequencies.astype(
125+
np.float64
126+
), histogram_result.bin_edges.copy()

src/khisto/core/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
# This software is distributed under the BSD 3-Clause-clear License, the text of which is available
33
# at https://spdx.org/licenses/BSD-3-Clause-Clear.html or see the "LICENSE" file for more details.
44

5-
from .cli_adapter import compute_histogram, HistogramResult
5+
from .backend import compute_histograms, HistogramResult
66

7-
__all__ = ["compute_histogram", "HistogramResult"]
7+
__all__ = ["compute_histograms", "HistogramResult"]

0 commit comments

Comments
 (0)