1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticaschartsvgzero-dependencyhistogramdistributionstatistics

Histogram Chart

Agrupa valores en intervalos iguales y muestra la distribución con marcadores de media y mediana.

Vista Previa en Vivo

Page load time

240 sessions, seconds

1.19smedian
02550751000.61.42.33.13.94.75.66.4
View data
BucketCount
0.6–1.0s81
1.0–1.4s78
1.4–1.8s38
1.8–2.3s25
2.3–2.7s14
2.7–3.1s1
3.1–3.5s0
3.5–3.9s1
3.9–4.3s1
4.3–4.7s0
4.7–5.1s0
5.1–5.6s0
5.6–6.0s0
6.0–6.4s1

Uso

Copia y pega este código directamente en tu componente React:

tsx
import { HistogramChart } from 'react-component-kit-ui/charts';

<HistogramChart values={loadTimes} bins={12} unit="s" showStats />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/HistogramChart.tsx + HistogramChart.css:

HistogramChart.tsx
'use client';

import ChartFrame from './ChartFrame';
import { scaleLinear, niceTicks, quantile, sum } from './chart-utils';

// Deterministic sample: a right-skewed page-load distribution.
const sample = Array.from({ length: 240 }, (_, i) => {
  const a = Math.sin(i * 12.9898) * 43758.5453;
  const b = Math.sin(i * 78.233) * 12345.678;
  const u = a - Math.floor(a);
  const v = b - Math.floor(b);
  return Math.round((0.6 + Math.abs(Math.sqrt(-2 * Math.log(u + 1e-9)) * Math.cos(2 * Math.PI * v)) * 0.9) * 100) / 100;
});

/**
 * HistogramChart — Bins raw values into equal-width buckets and draws the
 * distribution, with optional mean and median markers.
 *
 * @param {number[]} values - Raw observations.
 * @param {number} bins - Number of buckets.
 * @param {boolean} showStats - Draw mean & median reference lines.
 * @param {string} unit - Unit suffix for bucket edges.
 * @param {string} color - Bar color.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface HistogramChartProps {
  title?: string;
  subtitle?: string;
  values?: number[];
  bins?: number;
  showStats?: boolean;
  unit?: string;
  height?: number;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function HistogramChart({
  title = 'Page load time',
  subtitle = '240 sessions, seconds',
  values = sample,
  bins = 14,
  showStats = true,
  unit = 's',
  height = 260,
  color = 'var(--chart-1, #3987e5)',
  variant = 'glass',
  size = 'md',
  className = '',
}: HistogramChartProps) {
  const W = 640;
  const H = height;
  const m = { t: 22, r: 12, b: 30, l: 40 };
  const sorted = [...values].sort((a, b) => a - b);
  const lo = sorted[0];
  const hi = sorted[sorted.length - 1];
  const width = (hi - lo) / bins || 1;
  const counts = Array.from({ length: bins }, () => 0);
  values.forEach((v) => {
    counts[Math.min(bins - 1, Math.floor((v - lo) / width))] += 1;
  });
  const yTicks = niceTicks(0, Math.max(...counts), 4);
  const y = scaleLinear(0, yTicks[yTicks.length - 1], H - m.b, m.t);
  const x = scaleLinear(lo, hi, m.l, W - m.r);
  const mean = sum(values) / values.length;
  const median = quantile(sorted, 0.5);
  const edge = (i: number) => (lo + i * width).toFixed(1);
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} value={`${median.toFixed(2)}${unit}`} delta="median" variant={variant} size={size} className={className} table={{ headers: ['Bucket', 'Count'], rows: counts.map((c, i) => [`${edge(i)}–${edge(i + 1)}${unit}`, c]) }}>
        <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: histogram of ${values.length} values, median ${median.toFixed(2)}${unit}`}>
          {yTicks.map((t, __i: number) => (
            <g key={__i}>
              <line className={t === 0 ? 'cf-axis' : 'cf-grid'} x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} />
              <text className="cf-label" x={m.l - 8} y={y(t) + 4} textAnchor="end">{t}</text>
            </g>
          ))}
          {counts.map((c, i) => {
            const bx = x(lo + i * width) + 1;
            const bw = x(lo + (i + 1) * width) - x(lo + i * width) - 2;
            return (
              <rect className="cf-mark cf-dim" x={bx} y={y(c)} width={Math.max(1, bw)} height={Math.max(0, y(0) - y(c))} rx="2" fill={color} tabIndex={0} role="img" aria-label={`${edge(i)} to ${edge(i + 1)}${unit}: ${c} values`} data-tip-title={`${edge(i)}–${edge(i + 1)}${unit}`} data-tip={`${color}::${c} values · ${((c / values.length) * 100).toFixed(1)}%`} />
            );
          })}
          {Array.from({ length: bins + 1 }, (_, i) => i).filter((i) => i % 2 === 0).map((i, __i: number) => (
            <text key={__i} className="cf-label" x={x(lo + i * width)} y={H - 10} textAnchor="middle">{edge(i)}</text>
          ))}
          {showStats && (
            <g aria-hidden="true">
              <line x1={x(median)} x2={x(median)} y1={m.t - 6} y2={H - m.b} stroke="var(--c-text-1, #fff)" strokeWidth="1.5" />
              <text className="cf-label cf-label--strong" x={x(median) + 5} y={m.t}>Median</text>
              <line x1={x(mean)} x2={x(mean)} y1={m.t + 10} y2={H - m.b} stroke="var(--chart-2, #d95926)" strokeWidth="1.5" />
              <text className="cf-label" x={x(mean) + 5} y={m.t + 16}>Mean {mean.toFixed(2)}{unit}</text>
            </g>
          )}
        </svg>
      </ChartFrame>
    </>
  );
}

export default HistogramChart;

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
HistogramChart.tsx
Dependencias
Ninguna — React + CSS puro
Etiquetas
chartsvgzero-dependencyhistogramdistributionstatistics