Source

92 chart lines · 2 files · 2.5 kBBenchmark harness excluded · shared/mount.ts

Palmer penguins344 records · CSV · 13.5 kB
@charts-poc/demo-data/penguins

Selection: Complete published snapshot

species
string
island
string
culmen_length_mm
number | null
culmen_depth_mm
number | null
flipper_length_mm
number | null
body_mass_g
number | null
sex
string | null

Dr. Kristen Gorman / Palmer Station LTER@observablehq/sample-datasets@1.0.1 · revision 732c0148de74 · penguins.csv · ISC distribution; upstream source credited · SHA-256 dfee817d1c14Pinned snapshot

cases/14-error-bars/tanstack.ts57 lines · entry
cases/14-error-bars/tanstack.ts
import { penguins } from '@charts-poc/demo-data/penguins'
import { defineChart, dot, link, tickY } from '@tanstack/charts'
import { scaleBand, scaleLinear } from 'd3-scale'
import { summarizeErrorBars } from './transform'
import { tanstackMount } from '../../shared/mount'
import type { ConformanceInput } from '../../types'

const estimate = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 1,
  maximumFractionDigits: 1,
})
const definition = (input: ConformanceInput) => {
  const rows = summarizeErrorBars(penguins.slice(input.revision * 8))
  return defineChart({
    marks: [
      link(rows, {
        x1: 'species',
        y1: 'low',
        x2: 'species',
        y2: 'high',
        stroke: '#2563eb',
        strokeWidth: 1.5,
      }),
      tickY(rows, {
        x: 'species',
        y: 'low',
        stroke: '#2563eb',
        strokeWidth: 1.5,
      }),
      tickY(rows, {
        x: 'species',
        y: 'high',
        stroke: '#2563eb',
        strokeWidth: 1.5,
      }),
      dot(rows, {
        x: 'species',
        y: 'mean',
        fill: '#2563eb',
        r: 3.5,
      }),
    ],
    x: {
      scale: () => scaleBand<string>().padding(0.22),
    },
    y: { scale: scaleLinear, grid: true, axis: { label: 'Body mass (g)' } },
  })
}

export const mount = tanstackMount(
  definition,
  'Point estimates with error bars',
  {
    format: (point) =>
      `${point.datum.species} · Mean: ${estimate.format(point.datum.mean)} g · Range: ${estimate.format(point.datum.low)}${estimate.format(point.datum.high)} g`,
  },
)
cases/14-error-bars/transform.ts35 lines · support
cases/14-error-bars/transform.ts
import type { PenguinsRow } from '@charts-poc/demo-data/penguins'

export interface ErrorPoint {
  species: string
  mean: number
  low: number
  high: number
}

export function summarizeErrorBars(
  rows: readonly PenguinsRow[],
): readonly ErrorPoint[] {
  const groups = new Map<string, number[]>()
  for (const row of rows) {
    if (row.body_mass_g === null) continue
    const values = groups.get(row.species)
    if (values) values.push(row.body_mass_g)
    else groups.set(row.species, [row.body_mass_g])
  }

  return [...groups].map(([species, values]) => {
    const mean =
      values.reduce((total, value) => total + value, 0) / values.length
    const variance =
      values.reduce((total, value) => total + (value - mean) ** 2, 0) /
      Math.max(1, values.length - 1)
    const deviation = Math.sqrt(variance)
    return {
      species,
      mean,
      low: mean - deviation,
      high: mean + deviation,
    }
  })
}