You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
categorical histogram: a value named unique or longtail is overwritten by the summary bars #1084
categorical_dict (histogram.py:66) puts the top values and the longtail and unique summary entries in one dict keyed by value, and categorical_histogram treats any key named longtail or unique as a summary bar (lines 93 and 110). A column whose values include the string unique or longtail loses them. With 100 unique, 60 longtail and 40 singletons, the histogram is unique 20 (the singletons) and longtail 60, where 60 is the raw count of the longtail value rather than its 30%. The unique value, half the column, isn't shown. A value named NA gets a bar beside the NA bar, and the two share the name.
Probe script for this issue. Save it as probe_1084.py in a buckaroo checkout and run uv run python probe_1084.py. The output below is from main at e88ace9. Each case prints the histogram from pandas (pd), polars (pl) and xorq (xo): the bar kinds, the sum of the population bars, the sum of the categorical bars and the NA bar, then each bar as name:kind=value, and below that the histogram_bins range, min, max, nan_per and distinct_count.
probe_1084.py
"""Histogram probe: runs columns through the pandas, polars and xorq stat pipelines and prints each histogram.Each line is [bar kinds, population sum, categorical sum, NA, duplicate names] then the bars as name:kind=value."""importloggingimportwarningsfromcollectionsimportCounterlogging.disable(logging.CRITICAL)
warnings.filterwarnings('ignore')
importnumpyasnpimportpandasaspdimportpolarsasplimportpyarrowaspaimportxorq.apiasxofrombuckaroo.customizations.pd_stats_v2importPD_ANALYSIS_V2frombuckaroo.customizations.pl_stats_v2importPL_ANALYSIS_V2frombuckaroo.customizations.xorq_stats_v2importXORQ_STATS_V2frombuckaroo.pluggable_analysis_framework.stat_pipelineimportStatPipelinefrombuckaroo.pluggable_analysis_framework.xorq_stat_pipelineimportXorqStatPipelinerng=np.random.default_rng(0)
defsummarize(h):
ifnotisinstance(h, list):
returnf'NOT-A-LIST {type(h).__name__}'iflen(h) ==0:
return'EMPTY'kinds=Counter(kforbinhforkinbifk!='name')
parts= []
forbinh:
k=next((kforkinbifk!='name'), '?')
v=b.get(k)
parts.append(f"{b['name']}:{k[:3]}={v:g}"ifisinstance(v, (int, float, np.number)) elsef"{b['name']}:{k}={v}")
dups= [nforn, cinCounter(b['name'] forbinh).items() ifc>1]
pop=sum(b.get('population', 0) forbinh)
cat=sum(b.get(k, 0) forbinhforkin ('cat_pop', 'unique', 'longtail'))
na=sum(b.get('NA', 0) forbinh)
returnf"[{dict(kinds)} pop={pop:g} cat={cat:g} NA={na:g}{' DUPNAMES='+str(dups) ifdupselse''}] "+' | '.join(parts)
defbins_s(b):
ifnotb:
return'bins=[]'arr=np.asarray(b, dtype=float)
returnf'bins={len(b)} [{arr[0]:.6g}..{arr[-1]:.6g}]'+ (''ifnp.isfinite(arr).all() else' NONFINITE')
defrun(backend, values, pd_dtype=None, pl_dtype=None, pa_type=None):
ifbackend=='pd':
sd, errs=StatPipeline(PD_ANALYSIS_V2, unit_test=False).process_df(pd.DataFrame({'a': pd.Series(values, dtype=pd_dtype)}))
elifbackend=='pl':
sd, errs=StatPipeline(PL_ANALYSIS_V2, unit_test=False).process_df(pl.DataFrame({'a': pl.Series('a', values, dtype=pl_dtype, strict=False)}))
else:
table=xo.memtable(pa.table({'a': pa.array(values, type=pa_type)}), name='t')
sd, errs=XorqStatPipeline(XORQ_STATS_V2, unit_test=False).process_table(table)
returnsd.get('a', {}), sorted({f'{e.stat_key}:{type(e.error).__name__}'foreinerrs})
defcase(name, values, pd_dtype=None, pl_dtype=None, pa_type=None, backends=('pd', 'pl', 'xo')):
print(f'\n### {name}')
forbinbackends:
try:
c, errs=run(b, values, pd_dtype, pl_dtype, pa_type)
exceptExceptionase:
print(f' {b}: RAISED {type(e).__name__}: {str(e)[:150]}')
continueprint(f" {b}: {summarize(c.get('histogram'))}")
print(f" {bins_s(c.get('histogram_bins'))} min={c.get('min')!r:.30} max={c.get('max')!r:.30} "f"nan_per={c.get('nan_per')} distinct={c.get('distinct_count')}"+ (f' ERRS={errs}'iferrselse''))
case('100 unique, 60 longtail, 40 singletons', ['unique'] *100+ ['longtail'] *60+ [f'v{i}'foriinrange(40)], pa_type=pa.string())
case('categories named unique/longtail/NA beside nulls', ['unique'] *50+ ['longtail'] *30+ ['NA'] *20+ [f'v{i}'foriinrange(20)] + [None] *5, pa_type=pa.string())
case('a value named NA beside real nulls', ['NA'] *30+ ['x'] *60+ [None] *10, pa_type=pa.string())
case('1 and "1" in an object column', [1, '1', 1, '1', 2, '2'], backends=('pd',))
Activity
paddymul commentedon Oct 7, 2026
Probe script for this issue. Save it as
probe_1084.pyin a buckaroo checkout and runuv run python probe_1084.py. The output below is from main at e88ace9. Each case prints the histogram from pandas (pd), polars (pl) and xorq (xo): the bar kinds, the sum of thepopulationbars, the sum of the categorical bars and the NA bar, then each bar asname:kind=value, and below that thehistogram_binsrange,min,max,nan_peranddistinct_count.probe_1084.py
Output at e88ace9