Histogram
Basic Histogram
<script>
import { Histogram } from 'matterviz'
import { generate_normal } from '$site/plot-utils'
let bins = $state(50)
let sample_size = $state(1000)
let show_controls = $state(true)
let data = $derived({
y: generate_normal(sample_size, 50, 15),
label: `Normal Distribution (μ=50, σ=15)`,
})
</script>
<label>Bins: {bins}<input type="range" bind:value={bins} min="5" max="200" /></label>
<label>Size: {sample_size}
<input type="range" bind:value={sample_size} min="100" max="10000" step="100" />
</label>
<label><input type="checkbox" bind:checked={show_controls} />Controls</label>
<Histogram
series={[data]}
{bins}
{show_controls}
style="height: 400px"
>
{#snippet tooltip({ value, count })}
Value: {value.toFixed(1)}<br>Count: {count}<br>
%: {(count / sample_size * 100).toFixed(1)}%
{/snippet}
</Histogram>
Multiple Series Overlay
X: Y:
Normal (μ=5, σ=2)
Exponential (λ=0.3)
Uniform (0-15)
Gamma (α=2, β=3)
<script>
import { Histogram } from 'matterviz'
import { generate_normal, generate_exponential, generate_uniform, generate_gamma } from '$site/plot-utils'
let opacity = $state(0.6)
let stroke_width = $state(1.5)
let x_scale = $state(`linear`)
let y_scale = $state(`linear`)
let show_grid = $state(true)
let series = $state([
{ y: generate_normal(1200, 5, 2), label: `Normal (μ=5, σ=2)`, line_style: { stroke: `crimson` } },
{ y: generate_exponential(1200, 0.3), label: `Exponential (λ=0.3)`, line_style: { stroke: `royalblue` } },
{ y: generate_uniform(1200, 0, 15), label: `Uniform (0-15)`, line_style: { stroke: `mediumseagreen` } },
{ y: generate_gamma(1000, 2, 3), label: `Gamma (α=2, β=3)`, line_style: { stroke: `darkorange` } },
])
function toggle_series(idx) {
series[idx].visible = !series[idx].visible
series = [...series]
}
</script>
<label>Opacity: {opacity}<input type="range" bind:value={opacity} min="0.1" max="1" step="0.1" /></label>
<label>Stroke: {stroke_width}<input type="range" bind:value={stroke_width} min="0" max="5" step="0.5" /></label>
X: {#each [`linear`, `log`] as scale}<label><input type="radio" bind:group={x_scale} value={scale} />{scale}</label>{/each}
Y: {#each [`linear`, `log`] as scale}<label><input type="radio" bind:group={y_scale} value={scale} />{scale}</label>{/each}
<label><input type="checkbox" bind:checked={show_grid} />Grid</label>
{#each series as s, idx}
<label>
<input type="checkbox" checked={s.visible} onchange={() => toggle_series(idx)} />
<span style="width: 16px; height: 16px; margin: 0 0.5em; background: {s.line_style.stroke}"></span>
{s.label}
</label>
{/each}
<Histogram
{series}
mode="overlay"
bins={50}
bar_opacity={opacity}
bar_stroke_width={stroke_width}
x_scale_type={x_scale}
y_scale_type={y_scale}
x_grid={show_grid}
y_grid={show_grid}
show_controls
style="height: 450px"
>
{#snippet tooltip({ value, count, property })}
<strong style="color: {series.find(s => s.label === property)?.line_style?.stroke}">{property}</strong><br>
Value: {value.toFixed(2)}<br>Count: {count}
{/snippet}
</Histogram>
Logarithmic Scales
X: Y:
Log-Normal (μ=2, σ=1)
Power Law (α=2.5)
Pareto (α=3)
<script>
import { Histogram } from 'matterviz'
import {
generate_log_normal,
generate_pareto,
generate_power_law,
} from '$site/plot-utils'
let x_scale = $state(`linear`)
let y_scale = $state(`log`)
let bins = $state(40)
let series = $state([
{
y: generate_log_normal(1500, 2, 1),
label: `Log-Normal (μ=2, σ=1)`,
line_style: { stroke: `darkorange` },
},
{
y: generate_power_law(1500, 2.5),
label: `Power Law (α=2.5)`,
line_style: { stroke: `darkgreen` },
},
{
y: generate_pareto(1200, 1, 3),
label: `Pareto (α=3)`,
line_style: { stroke: `darkviolet` },
},
])
</script>
X: {#each [`linear`, `log`] as scale}<label><input
type="radio"
bind:group={x_scale}
value={scale}
/>{scale}</label>{/each}
Y: {#each [`linear`, `log`] as scale}<label><input
type="radio"
bind:group={y_scale}
value={scale}
/>{scale}</label>{/each}
<label>Bins: {bins}<input
type="range"
bind:value={bins}
min="10"
max="100"
step="5"
/></label>
<Histogram
{series}
mode="overlay"
{bins}
x_scale_type={x_scale}
y_scale_type={y_scale}
x_label="Value ({x_scale} scale)"
y_label="Frequency ({y_scale} scale)"
x_format="~s"
y_format={y_scale === `log` ? `~s` : `d`}
show_controls
style="height: 450px"
>
{#snippet tooltip({ value, count, property })}
<strong>{property}</strong><br>
Value: {value.toExponential(2)}<br>Count: {count}
{/snippet}
</Histogram>
Real-World Distributions
<script>
import { Histogram } from 'matterviz'
import {
generate_age_distribution,
generate_bimodal,
generate_discrete,
generate_mixture,
generate_skewed,
} from '$site/plot-utils'
let selected = $state(`bimodal`)
let mode = $state(`single`)
let distributions = $derived({
bimodal: {
data: generate_bimodal(1500),
label: `Bimodal Distribution`,
color: `#e74c3c`,
},
skewed: {
data: generate_skewed(1200),
label: `Right-Skewed Distribution`,
color: `#3498db`,
},
discrete: {
data: generate_discrete(1000),
label: `Survey Responses (1-10)`,
color: `#2ecc71`,
},
age: {
data: generate_age_distribution(2000),
label: `Age Distribution`,
color: `#9b59b6`,
},
mixture: {
data: generate_mixture(1800),
label: `Complex Mixture`,
color: `#f39c12`,
},
})
let current = $derived(distributions[selected])
let series_data = $derived(
mode === `single`
? [{
y: current.data,
label: current.label,
line_style: { stroke: current.color },
}]
: Object.entries(distributions).map(([key, dist]) => ({
y: dist.data,
label: dist.label,
line_style: { stroke: dist.color },
visible: key === selected,
})),
)
</script>
<select bind:value={selected}>
{#each Object.entries(distributions) as [key, dist]}
<option value={key}>{dist.label}</option>
{/each}
</select>
{#each [`single`, `overlay`] as display_mode}
<label><input type="radio" bind:group={mode} value={display_mode} />{
display_mode
}</label>
{/each}
<Histogram
series={series_data}
{mode}
bins={selected === `discrete` ? 10 : 40}
x_label={selected === `age` ? `Age (years)` : selected === `discrete` ? `Rating` : `Value`}
y_label="Count"
x_format={selected === `discrete` ? `.1f` : `.0f`}
show_controls
show_legend={mode === `overlay`}
style="height: 450px"
>
{#snippet tooltip({ value, count, property })}
<strong>{property}</strong><br>
{{ age: `Age`, discrete: `Rating` }[selected] ?? `Value`}: {
value.toFixed(selected === `discrete` ? 1 : 0)
}<br>
Count: {count}<br>%: {(count / current.data.length * 100).toFixed(1)}%
{/snippet}
</Histogram>
Bin Size Comparison
10 bins
25 bins
50 bins
100 bins
<script>
import { Histogram } from 'matterviz'
import { generate_mixed_data, generate_complex_distribution } from '$site/plot-utils'
let bin_counts = $state([10, 25, 50, 100])
let show_overlay = $state(true)
let data_type = $state(`mixed`)
let opacity = $state(0.6)
const base_data = $derived(data_type === `mixed` ? generate_mixed_data(3000) : generate_complex_distribution(3000))
const colors = [`#e74c3c`, `#3498db`, `#2ecc71`, `#f39c12`]
let series = $derived(
show_overlay
? bin_counts.map((bins, idx) => ({
y: base_data,
label: `${bins} bins`,
line_style: { stroke: colors[idx] },
}))
: [{ y: base_data, label: `${data_type === `mixed` ? `Mixed` : `Complex`} Distribution`, line_style: { stroke: `#8e44ad` } }]
)
</script>
{#each [`mixed`, `complex`] as type}<label><input type="radio" bind:group={data_type} value={type} />{type}</label>{/each}
<label><input type="checkbox" bind:checked={show_overlay} />Multiple Bin Sizes</label>
<label>Opacity: {opacity}<input type="range" bind:value={opacity} min="0.1" max="1" step="0.1" /></label>
{#if !show_overlay}
<label>Bins: {bin_counts[1]}<input type="range" bind:value={bin_counts[1]} min="5" max="200" step="5" /></label>
{:else}
{#each bin_counts as count, idx}
<label style="color: {colors[idx]}">{count} bins: <input type="range" bind:value={bin_counts[idx]} min="5" max="200" step="5" /></label>
{/each}
{/if}
<Histogram
{series}
bins={show_overlay ? 25 : bin_counts[1]}
mode={show_overlay ? `overlay` : `single`}
bar_opacity={opacity}
show_controls
show_legend={show_overlay}
style="height: 450px"
>
{#snippet tooltip({ value, count, property })}
<strong>{property}</strong><br>Range: {value.toFixed(1)}<br>Count: {count}
{/snippet}
</Histogram>
Custom Styling
<script>
import { Histogram } from 'matterviz'
import { generate_financial_data, generate_scientific_data } from '$site/plot-utils'
let color_scheme = $state(`default`)
let x_format = $state(`number`)
let y_format = $state(`count`)
let data_source = $state(`financial`)
const color_schemes = {
default: [`#3498db`], warm: [`#e74c3c`, `#f39c12`, `#e67e22`],
cool: [`#3498db`, `#2ecc71`, `#1abc9c`], monochrome: [`#2c3e50`, `#34495e`, `#7f8c8d`],
}
const x_formats = { number: `.1f`, scientific: `.2e`, percentage: `.1%`, currency: `$,.0f`, engineering: `.2~s` }
const y_formats = { count: `d`, percentage: `.1%`, thousands: `,.0f`, scientific: `.1e` }
let data = $derived(data_source === `financial` ? generate_financial_data(1200) : generate_scientific_data(1200))
let series = $derived([{
y: data,
label: data_source === `financial` ? `Stock Prices` : `Scientific Measurements`,
line_style: { stroke: color_schemes[color_scheme][0] },
}])
</script>
{#each [`financial`, `scientific`] as source}<label><input type="radio" bind:group={data_source} value={source} />{source}</label>{/each}
<select bind:value={color_scheme}>
{#each Object.keys(color_schemes) as scheme}<option value={scheme}>{scheme}</option>{/each}
</select>
<select bind:value={x_format}>
{#each Object.entries(x_formats) as [key, format]}<option value={key}>{key} ({format})</option>{/each}
</select>
<select bind:value={y_format}>
{#each Object.entries(y_formats) as [key, format]}<option value={key}>{key} ({format})</option>{/each}
</select>
<Histogram
{series}
bins={35}
x_label={x_format === `currency` ? `Stock Price` : `Value`}
y_label={y_format === `percentage` ? `Percentage` : `Count`}
x_format={x_formats[x_format]}
y_format={y_format === `percentage` ? `.1%` : y_formats[y_format]}
show_controls
style="height: 450px; border: 2px solid {color_schemes[color_scheme][0]}; border-radius: 8px;"
>
{#snippet tooltip({ value, count, property })}
<div style="background: {color_schemes[color_scheme][0]}; color: white; padding: 8px; border-radius: 6px;">
<strong>{property}</strong><br>
{x_format === `currency` ? `Price: $${value.toFixed(0)}` : `Value: ${value.toFixed(2)}`}<br>
Count: {count}
</div>
{/snippet}
</Histogram>
Performance Test
Performance: normal distribution, 10,000
points, 50 bins, single mode
<script>
import { Histogram } from 'matterviz'
import { generate_large_dataset, generate_sparse_data } from '$site/plot-utils'
let dataset_size = $state(10000)
let data_type = $state(`normal`)
let bins = $state(50)
let mode = $state(`single`)
let performance_data = $derived({
normal: generate_large_dataset(dataset_size, `normal`),
uniform: generate_large_dataset(dataset_size, `uniform`),
sparse: generate_sparse_data(dataset_size),
})
let series_data = $derived(
mode === `single`
? [{
y: performance_data[data_type],
label: `${data_type} (${dataset_size.toLocaleString()} points)`,
line_style: { stroke: `#2c3e50` },
}]
: Object.entries(performance_data).map(([key, data]) => ({
y: data,
label: `${key} (${data.length.toLocaleString()} points)`,
line_style: {
stroke: key === `normal`
? `#e74c3c`
: key === `uniform`
? `#3498db`
: `#2ecc71`,
},
visible: key === data_type,
})),
)
</script>
<label>Size: {dataset_size.toLocaleString()}<input
type="range"
bind:value={dataset_size}
min="1000"
max="50000"
step="1000"
/></label>
{#each [`normal`, `uniform`, `sparse`] as type}<label><input
type="radio"
bind:group={data_type}
value={type}
/>{type}</label>{/each}
<label>Bins: {bins}<input
type="range"
bind:value={bins}
min="10"
max="200"
step="10"
/></label>
{#each [`single`, `overlay`] as display_mode}
<label><input
type="radio"
bind:group={mode}
value={display_mode}
/>{display_mode}</label>
{/each}
<strong>Performance:</strong> {data_type} distribution, {dataset_size.toLocaleString()}
points, {bins} bins, {mode} mode
<Histogram
series={series_data}
{mode}
{bins}
show_controls
show_legend={mode === `overlay`}
style="height: 450px"
>
{#snippet tooltip({ value, count, property })}
<strong>{property}</strong><br>Value: {value.toFixed(2)}<br>Count: {count}
{/snippet}
</Histogram>