<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>setttyのブログ</title>
<link>https://ameblo.jp/settty/</link>
<atom:link href="https://rssblog.ameba.jp/settty/rss20.xml" rel="self" type="application/rss+xml" />
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com" />
<description>ブログの説明を入力します。</description>
<language>ja</language>
<item>
<title>Upset claude</title>
<description>
<![CDATA[ <p>#HTML</p><p><br></p><p>&lt;!DOCTYPE html&gt;&lt;html lang="ja"&gt;&lt;head&gt;  &lt;meta charset="UTF-8"&gt;  &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;  &lt;title&gt;UpSet Plot — Interactive Viewer&lt;/title&gt;  &lt;link rel="stylesheet" href="upset.css"&gt;&lt;/head&gt;&lt;body&gt;&lt;header&gt;  &lt;h1&gt;Up&lt;span&gt;Set&lt;/span&gt; Viewer&lt;/h1&gt;  &lt;span class="badge"&gt;Interactive&lt;/span&gt;  &lt;span class="badge" style="margin-left:auto; border-color:#ff4081; color:#ff4081;"&gt;v2.0&lt;/span&gt;&lt;/header&gt;&lt;div class="layout"&gt;  &lt;aside class="sidebar"&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Sets&lt;/h3&gt;      &lt;div id="set-list"&gt;&lt;/div&gt;    &lt;/div&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Sort by&lt;/h3&gt;      &lt;div class="sort-group"&gt;        &lt;button class="sort-btn active" data-sort="size"&gt;↓ Intersection size&lt;/button&gt;        &lt;button class="sort-btn" data-sort="degree"&gt;◈ Degree&lt;/button&gt;        &lt;button class="sort-btn" data-sort="name"&gt;Aa Name&lt;/button&gt;      &lt;/div&gt;    &lt;/div&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Min intersection size&lt;/h3&gt;      &lt;div class="filter-row"&gt;        &lt;label&gt;Threshold &lt;span id="min-size-val"&gt;1&lt;/span&gt;&lt;/label&gt;        &lt;input type="range" id="min-size" min="1" max="10" value="1" step="1"&gt;      &lt;/div&gt;    &lt;/div&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Degree filter&lt;/h3&gt;      &lt;div class="degree-group" id="deg-group"&gt;&lt;/div&gt;    &lt;/div&gt;    &lt;!-- ★ New: Boxplot mode --&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Box plot mode&lt;/h3&gt;      &lt;div class="sort-group"&gt;        &lt;button class="box-mode-btn active" data-mode="intersection" title="One box per intersection (avg value across sets)"&gt;          ▣ Per intersection        &lt;/button&gt;        &lt;button class="box-mode-btn" data-mode="per-set" title="Select a column first, then see each set's values separately"&gt;          ◧ Per set (select column)        &lt;/button&gt;      &lt;/div&gt;      &lt;div style="font-size:0.65rem; color:var(--muted); margin-top:8px; line-height:1.5;"&gt;        「Per set」は列をクリックして交差を選択後に有効      &lt;/div&gt;    &lt;/div&gt;    &lt;div class="sidebar-section"&gt;      &lt;h3&gt;Summary&lt;/h3&gt;      &lt;div class="stat-grid"&gt;        &lt;div class="stat-cell"&gt;          &lt;div class="sv" id="stat-intersections"&gt;—&lt;/div&gt;          &lt;div class="sk"&gt;Intersections&lt;/div&gt;        &lt;/div&gt;        &lt;div class="stat-cell"&gt;          &lt;div class="sv" id="stat-elements"&gt;—&lt;/div&gt;          &lt;div class="sk"&gt;Elements&lt;/div&gt;        &lt;/div&gt;        &lt;div class="stat-cell"&gt;          &lt;div class="sv" id="stat-sets"&gt;—&lt;/div&gt;          &lt;div class="sk"&gt;Active Sets&lt;/div&gt;        &lt;/div&gt;        &lt;div class="stat-cell"&gt;          &lt;div class="sv" id="stat-maxdeg"&gt;—&lt;/div&gt;          &lt;div class="sk"&gt;Max Degree&lt;/div&gt;        &lt;/div&gt;      &lt;/div&gt;    &lt;/div&gt;    &lt;div class="sidebar-section"&gt;      &lt;button class="reset-btn" id="reset-btn"&gt;⟳ Reset all filters&lt;/button&gt;    &lt;/div&gt;  &lt;/aside&gt;  &lt;main class="main"&gt;    &lt;div id="upset-wrap"&gt;&lt;/div&gt;    &lt;div class="boxplot-section"&gt;      &lt;div class="section-title"&gt;        Box Plot — &lt;span&gt;Value distribution per intersection / set&lt;/span&gt;      &lt;/div&gt;      &lt;canvas id="boxplot-canvas"&gt;&lt;/canvas&gt;    &lt;/div&gt;  &lt;/main&gt;&lt;/div&gt;&lt;div id="tooltip"&gt;&lt;/div&gt;&lt;script src="upset.js"&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;<br></p><p><br></p><p><br></p><p>#JS</p><p>/* =============================================   upset.js  —  UpSet Plot + BoxPlot Engine   Per-set values: element[set] = value   ============================================= */class UpSetApp {  constructor(data) {    this.rawSets    = data.sets;    this.rawElems   = data.elements;   // [{id, label, sets:{SetName: value, ...}}]    this.activesets = new Set(data.sets.map(s =&gt; s.name));    this.sortMode   = 'size';    this.minSize    = 1;    this.activeDeg  = new Set();    this.highlighted = null;    this.selected    = null;    // Boxplot mode: 'intersection' = one box per intersection (avg value)    //               'per-set'      = one box per set within the selected intersection    this.boxMode = 'intersection';    this._buildUI();    this._render();  }  /* ─── Data helpers ─── */  // Return all set-names this element belongs to (filtered to active)  _elemSets(el) {    return Object.keys(el.sets).filter(s =&gt; this.activesets.has(s));  }  // Value of element in a specific set  _elemVal(el, setName) {    return el.sets[setName];  }  // Average value of element across the sets it participates in (for intersection-level stats)  _elemAvgVal(el, setNames) {    const vals = setNames.map(s =&gt; el.sets[s]).filter(v =&gt; v !== undefined);    if (!vals.length) return null;    return vals.reduce((a, b) =&gt; a + b, 0) / vals.length;  }  /* ─── Compute intersections ─── */  _computeIntersections() {    const map = new Map();    for (const el of this.rawElems) {      const inSets = this._elemSets(el);      if (inSets.length === 0) continue;      const key = [...inSets].sort().join('|||');      if (!map.has(key)) {        map.set(key, {          key,          sets: [...inSets].sort(),          elements: [],          degree: inSets.length,        });      }      map.get(key).elements.push(el);    }    let intersections = [...map.values()].filter(      ix =&gt; ix.elements.length &gt;= this.minSize    );    if (this.activeDeg.size &gt; 0) {      intersections = intersections.filter(ix =&gt; this.activeDeg.has(ix.degree));    }    if (this.sortMode === 'size') {      intersections.sort((a, b) =&gt; b.elements.length - a.elements.length);    } else if (this.sortMode === 'degree') {      intersections.sort((a, b) =&gt; a.degree - b.degree || b.elements.length - a.elements.length);    } else {      intersections.sort((a, b) =&gt; a.key.localeCompare(b.key));    }    return intersections;  }  /* ─── Stats ─── */  _stats(values) {    const v = values.filter(x =&gt; x !== null &amp;&amp; x !== undefined &amp;&amp; isFinite(x));    if (!v.length) return null;    const s = [...v].sort((a, b) =&gt; a - b);    const n = s.length;    const q1 = s[Math.floor(n * 0.25)];    const median = n % 2 === 0 ? (s[n/2-1] + s[n/2]) / 2 : s[Math.floor(n/2)];    const q3 = s[Math.floor(n * 0.75)];    const iqr = q3 - q1;    const whiskerLo = Math.min(...s.filter(x =&gt; x &gt;= q1 - 1.5 * iqr));    const whiskerHi = Math.max(...s.filter(x =&gt; x &lt;= q3 + 1.5 * iqr));    const outliers  = s.filter(x =&gt; x &lt; whiskerLo || x &gt; whiskerHi);    const mean = s.reduce((a, b) =&gt; a + b, 0) / n;    return { min: s[0], q1, median, q3, max: s[n-1], whiskerLo, whiskerHi, outliers, mean, n };  }  /* ─── Sidebar UI ─── */  _buildUI() {    /* Set toggles */    const setList = document.getElementById('set-list');    setList.innerHTML = '';    for (const s of this.rawSets) {      const label = document.createElement('label');      label.className = 'set-toggle';      label.innerHTML = `        &lt;input type="checkbox" checked&gt;        &lt;span class="swatch" style="background:${s.color}"&gt;&lt;/span&gt;        &lt;span class="set-name"&gt;${s.name}&lt;/span&gt;        &lt;span class="set-count"&gt;${this._setCount(s.name)}&lt;/span&gt;      `;      label.querySelector('input').addEventListener('change', e =&gt; {        if (e.target.checked) { this.activesets.add(s.name); label.classList.remove('off'); }        else                  { this.activesets.delete(s.name); label.classList.add('off'); }        this._render();      });      setList.appendChild(label);    }    /* Sort */    document.querySelectorAll('.sort-btn').forEach(btn =&gt; {      btn.addEventListener('click', () =&gt; {        document.querySelectorAll('.sort-btn').forEach(b =&gt; b.classList.remove('active'));        btn.classList.add('active');        this.sortMode = btn.dataset.sort;        this._render();      });    });    /* Min size slider */    const slider = document.getElementById('min-size');    const sliderVal = document.getElementById('min-size-val');    slider.addEventListener('input', () =&gt; {      this.minSize = +slider.value;      sliderVal.textContent = slider.value;      this._render();    });    /* Degree filter */    const degGroup = document.getElementById('deg-group');    degGroup.innerHTML = '';    const allBtn = document.createElement('button');    allBtn.className = 'deg-btn active';    allBtn.textContent = 'All';    allBtn.addEventListener('click', () =&gt; {      this.activeDeg.clear();      document.querySelectorAll('.deg-btn').forEach(b =&gt; b.classList.remove('active'));      allBtn.classList.add('active');      this._render();    });    degGroup.appendChild(allBtn);    for (let d = 1; d &lt;= this.rawSets.length; d++) {      const btn = document.createElement('button');      btn.className = 'deg-btn';      btn.textContent = d;      btn.addEventListener('click', () =&gt; {        allBtn.classList.remove('active');        if (this.activeDeg.has(d)) { this.activeDeg.delete(d); btn.classList.remove('active'); }        else                       { this.activeDeg.add(d);    btn.classList.add('active'); }        if (this.activeDeg.size === 0) allBtn.classList.add('active');        this._render();      });      degGroup.appendChild(btn);    }    /* Boxplot mode toggle */    document.querySelectorAll('.box-mode-btn').forEach(btn =&gt; {      btn.addEventListener('click', () =&gt; {        document.querySelectorAll('.box-mode-btn').forEach(b =&gt; b.classList.remove('active'));        btn.classList.add('active');        this.boxMode = btn.dataset.mode;        this._render();      });    });    /* Reset */    document.getElementById('reset-btn').addEventListener('click', () =&gt; {      this.activesets = new Set(this.rawSets.map(s =&gt; s.name));      this.sortMode = 'size'; this.minSize = 1;      this.activeDeg.clear(); this.highlighted = null; this.selected = null;      this.boxMode = 'intersection';      document.querySelectorAll('.set-toggle').forEach(l =&gt; {        l.classList.remove('off'); l.querySelector('input').checked = true;      });      document.querySelectorAll('.sort-btn').forEach(b =&gt; b.classList.remove('active'));      document.querySelector('.sort-btn[data-sort="size"]').classList.add('active');      slider.value = 1; sliderVal.textContent = '1';      document.querySelectorAll('.deg-btn').forEach(b =&gt; b.classList.remove('active'));      allBtn.classList.add('active');      document.querySelectorAll('.box-mode-btn').forEach(b =&gt; b.classList.remove('active'));      document.querySelector('.box-mode-btn[data-mode="intersection"]').classList.add('active');      this._render();    });    /* Tooltip */    this.tooltip = document.getElementById('tooltip');    document.addEventListener('mousemove', e =&gt; {      this.tooltip.style.left = (e.clientX + 14) + 'px';      this.tooltip.style.top  = (e.clientY - 10) + 'px';    });  }  _setCount(name) {    return this.rawElems.filter(e =&gt; name in e.sets).length;  }  _setColor(name) {    return this.rawSets.find(s =&gt; s.name === name)?.color || '#888';  }  /* ─── Main render ─── */  _render() {    const intersections = this._computeIntersections();    const activeSets = this.rawSets.filter(s =&gt; this.activesets.has(s.name));    this._renderUpSet(intersections, activeSets);    this._renderBoxplot(intersections, activeSets);    this._updateStats(intersections);  }  /* ─── UpSet matrix ─── */  _renderUpSet(intersections, activeSets) {    const wrap = document.getElementById('upset-wrap');    wrap.innerHTML = '';    if (!intersections.length || !activeSets.length) {      wrap.innerHTML = '&lt;div class="empty-state"&gt;No intersections match current filters.&lt;/div&gt;';      return;    }    const maxCount = Math.max(...intersections.map(ix =&gt; ix.elements.length));    const BAR_H = 180;    const maxSetSize = Math.max(...activeSets.map(s =&gt;      this.rawElems.filter(e =&gt; s.name in e.sets).length    ));    /* Top bar chart */    const barArea = document.createElement('div');    barArea.className = 'bar-area';    const yAxis = document.createElement('div');    yAxis.className = 'bar-yaxis';    yAxis.style.height = BAR_H + 'px';    for (let i = 4; i &gt;= 0; i--) {      const t = document.createElement('div');      t.className = 'tick';      t.textContent = Math.round(maxCount * i / 4);      yAxis.appendChild(t);    }    barArea.appendChild(yAxis);    const barsRow = document.createElement('div');    barsRow.className = 'bars-row';    intersections.forEach(ix =&gt; {      const col = document.createElement('div');      col.className = 'bar-col' + (this.highlighted === ix.key ? ' highlighted' : '');      const h = (ix.elements.length / maxCount) * BAR_H;      const inner = document.createElement('div');      inner.className = 'bar-inner';      inner.style.height = h + 'px';      const val = document.createElement('div');      val.className = 'bar-val';      val.textContent = ix.elements.length;      col.appendChild(val);      col.appendChild(inner);      this._addColEvents(col, ix, intersections, activeSets);      barsRow.appendChild(col);    });    barArea.appendChild(barsRow);    wrap.appendChild(barArea);    const div = document.createElement('div');    div.className = 'chart-divider';    wrap.appendChild(div);    /* Matrix */    const matrixArea = document.createElement('div');    matrixArea.className = 'matrix-area';    /* Set labels */    const labelCol = document.createElement('div');    labelCol.className = 'set-labels';    for (const s of activeSets) {      const row = document.createElement('div');      row.className = 'set-label-row';      row.innerHTML = `&lt;span class="swatch-sm" style="background:${s.color}"&gt;&lt;/span&gt;&lt;span&gt;${s.name}&lt;/span&gt;`;      labelCol.appendChild(row);    }    matrixArea.appendChild(labelCol);    /* Dot columns */    const dotCols = document.createElement('div');    dotCols.className = 'dot-cols';    intersections.forEach(ix =&gt; {      const col = document.createElement('div');      col.className = 'dot-col'        + (this.highlighted === ix.key ? ' highlighted' : '')        + (this.selected    === ix.key ? ' selected' : '');      const onIndices = activeSets.map((s, idx) =&gt; ix.sets.includes(s.name) ? idx : -1).filter(x =&gt; x &gt;= 0);      activeSets.forEach(s =&gt; {        const dot = document.createElement('div');        dot.className = 'dot ' + (ix.sets.includes(s.name) ? 'on' : 'off');        col.appendChild(dot);      });      if (onIndices.length &gt; 1) {        const CELL = 52;        const top = onIndices[0] * CELL + (CELL - 18) / 2 + 9;        const bot = onIndices[onIndices.length - 1] * CELL + (CELL - 18) / 2 + 9;        const conn = document.createElement('div');        conn.className = 'connector';        conn.style.top    = top + 'px';        conn.style.height = (bot - top) + 'px';        col.appendChild(conn);      }      this._addColEvents(col, ix, intersections, activeSets);      dotCols.appendChild(col);    });    matrixArea.appendChild(dotCols);    /* Set size bars */    const sizeArea = document.createElement('div');    sizeArea.className = 'size-area';    for (const s of activeSets) {      const cnt = this.rawElems.filter(e =&gt; s.name in e.sets).length;      const w = Math.max(4, (cnt / maxSetSize) * 100);      const row = document.createElement('div');      row.className = 'size-row';      row.innerHTML = `        &lt;div class="size-bar" style="width:${w}px; background:${s.color}"&gt;&lt;/div&gt;        &lt;span class="size-val"&gt;${cnt}&lt;/span&gt;      `;      sizeArea.appendChild(row);    }    matrixArea.appendChild(sizeArea);    wrap.appendChild(matrixArea);  }  _addColEvents(col, ix, intersections, activeSets) {    col.addEventListener('mouseenter', () =&gt; {      this.highlighted = ix.key;      this._showTooltip(ix);      this._renderUpSet(intersections, activeSets);    });    col.addEventListener('mouseleave', () =&gt; {      this.highlighted = null;      this._hideTooltip();      this._renderUpSet(intersections, activeSets);    });    col.addEventListener('click', () =&gt; {      this.selected = this.selected === ix.key ? null : ix.key;      this._renderUpSet(intersections, activeSets);      this._renderBoxplot(intersections, activeSets);    });  }  /* ─── Boxplot ─── */  _renderBoxplot(intersections, activeSets) {    const canvas = document.getElementById('boxplot-canvas');    const ctx    = canvas.getContext('2d');    const DPR    = window.devicePixelRatio || 1;    const W      = Math.max(400, canvas.parentElement.clientWidth || 800);    const H      = 340;    canvas.width  = W * DPR;    canvas.height = H * DPR;    canvas.style.width  = W + 'px';    canvas.style.height = H + 'px';    ctx.scale(DPR, DPR);    ctx.clearRect(0, 0, W, H);    ctx.fillStyle = '#0f0f12';    ctx.fillRect(0, 0, W, H);    const PAD_L = 54, PAD_R = 20, PAD_T = 36, PAD_B = 72;    const plotW = W - PAD_L - PAD_R;    const plotH = H - PAD_T - PAD_B;    // ── Decide what to draw ──    // Mode A (intersection): one box per intersection, value = avg across sets    // Mode B (per-set): selected intersection → one box per set    let boxes = [];  // [{label, color, values}]    if (this.boxMode === 'per-set' &amp;&amp; this.selected) {      // Per-set breakdown for the selected intersection      const ix = intersections.find(i =&gt; i.key === this.selected);      if (ix) {        for (const s of ix.sets) {          const vals = ix.elements.map(el =&gt; el.sets[s]).filter(v =&gt; v !== undefined);          boxes.push({ label: s, color: this._setColor(s), values: vals });        }      }    } else {      // One box per intersection (avg value across participating sets)      const show = this.selected        ? intersections.filter(ix =&gt; ix.key === this.selected)        : intersections.slice(0, Math.min(intersections.length, 20));      for (const ix of show) {        // avg value across all sets this element appears in within this intersection        const vals = ix.elements.map(el =&gt; this._elemAvgVal(el, ix.sets)).filter(v =&gt; v !== null);        const label = ix.sets.length &lt;= 2          ? ix.sets.join(' ∩ ')          : ix.sets.slice(0, 2).join(' ∩ ') + '…';        let color = '#00e5ff';        if (ix.sets.length === 1) color = this._setColor(ix.sets[0]);        if (this.selected === ix.key) color = '#ff4081';        boxes.push({ label, color, values: vals });      }    }    if (!boxes.length) return;    const allStats = boxes.map(b =&gt; ({ ...b, st: this._stats(b.values) })).filter(x =&gt; x.st);    if (!allStats.length) return;    const allVals = allStats.flatMap(x =&gt; x.values);    const globalMin = Math.min(...allVals);    const globalMax = Math.max(...allVals);    const range = globalMax - globalMin || 1;    const toY = v =&gt; PAD_T + plotH - ((v - globalMin) / range) * plotH;    // Grid    ctx.strokeStyle = '#2a2a38';    ctx.lineWidth = 1;    ctx.setLineDash([3, 4]);    ctx.textAlign = 'right';    ctx.font = '10px JetBrains Mono, monospace';    ctx.fillStyle = '#6b6b85';    for (let i = 0; i &lt;= 5; i++) {      const v = globalMin + (range * i / 5);      const y = toY(v);      ctx.beginPath();      ctx.moveTo(PAD_L, y); ctx.lineTo(W - PAD_R, y);      ctx.stroke();      ctx.fillText(v.toFixed(1), PAD_L - 6, y + 4);    }    ctx.setLineDash([]);    // Draw boxes    const slotW = plotW / allStats.length;    const BOX_W = Math.min(30, slotW * 0.45);    allStats.forEach(({ label, color, st }, i) =&gt; {      const cx = PAD_L + slotW * i + slotW / 2;      const alpha = '88';      // Whisker line      ctx.strokeStyle = color;      ctx.lineWidth = 1.5;      ctx.beginPath();      ctx.moveTo(cx, toY(st.whiskerLo));      ctx.lineTo(cx, toY(st.whiskerHi));      ctx.stroke();      // Whisker caps      [[st.whiskerLo], [st.whiskerHi]].forEach(([v]) =&gt; {        ctx.beginPath();        ctx.moveTo(cx - BOX_W * 0.35, toY(v));        ctx.lineTo(cx + BOX_W * 0.35, toY(v));        ctx.stroke();      });      // IQR box      const boxTop = toY(st.q3);      const boxH   = toY(st.q1) - boxTop;      ctx.fillStyle = color + alpha;      ctx.fillRect(cx - BOX_W / 2, boxTop, BOX_W, boxH);      ctx.strokeStyle = color;      ctx.strokeRect(cx - BOX_W / 2, boxTop, BOX_W, boxH);      // Median      ctx.strokeStyle = '#ffffff';      ctx.lineWidth = 2;      ctx.beginPath();      ctx.moveTo(cx - BOX_W / 2, toY(st.median));      ctx.lineTo(cx + BOX_W / 2, toY(st.median));      ctx.stroke();      // Mean dot      ctx.fillStyle = '#ffffff';      ctx.beginPath();      ctx.arc(cx, toY(st.mean), 3, 0, Math.PI * 2);      ctx.fill();      // Outliers      ctx.fillStyle = color;      st.outliers.forEach(v =&gt; {        ctx.beginPath();        ctx.arc(cx, toY(v), 3, 0, Math.PI * 2);        ctx.fill();      });      // X label (rotated)      ctx.save();      ctx.translate(cx, H - PAD_B + 10);      ctx.rotate(-Math.PI / 4);      ctx.fillStyle = '#c0c0d0';      ctx.font = '9.5px JetBrains Mono, monospace';      ctx.textAlign = 'right';      ctx.fillText(label, 0, 0);      ctx.restore();      // n label above box      ctx.fillStyle = '#6b6b85';      ctx.font = '8px JetBrains Mono, monospace';      ctx.textAlign = 'center';      ctx.fillText(`n=${st.n}`, cx, boxTop - 4);    });    // Y-axis label    ctx.save();    ctx.translate(12, H / 2);    ctx.rotate(-Math.PI / 2);    ctx.fillStyle = '#6b6b85';    ctx.font = '10px JetBrains Mono, monospace';    ctx.textAlign = 'center';    const yLabel = (this.boxMode === 'per-set' &amp;&amp; this.selected) ? 'Value (per set)' : 'Value (avg across sets)';    ctx.fillText(yLabel, 0, 0);    ctx.restore();    // Legend    ctx.fillStyle = '#6b6b85';    ctx.font = '9px JetBrains Mono, monospace';    ctx.textAlign = 'left';    const note = this.selected      ? (this.boxMode === 'per-set' ? 'Per-set breakdown of selected intersection' : 'Avg value for selected intersection')      : '● mean  — median  |  Click column to isolate  |  Toggle mode in sidebar';    ctx.fillText(note, PAD_L, H - 8);  }  /* ─── Tooltip ─── */  _showTooltip(ix) {    // Show per-set values for each element    const label = ix.sets.join(' ∩ ');    let html = `&lt;div class="tt-title"&gt;${label}&lt;/div&gt;`;    html += `&lt;div class="tt-row"&gt;&lt;span class="tt-key"&gt;Count&lt;/span&gt;&lt;span class="tt-val"&gt;${ix.elements.length}&lt;/span&gt;&lt;/div&gt;`;    html += `&lt;div class="tt-row"&gt;&lt;span class="tt-key"&gt;Degree&lt;/span&gt;&lt;span class="tt-val"&gt;${ix.degree}&lt;/span&gt;&lt;/div&gt;`;    // Per-set stats    for (const s of ix.sets) {      const vals = ix.elements.map(el =&gt; el.sets[s]).filter(v =&gt; v !== undefined);      const st = this._stats(vals);      if (st) {        html += `&lt;div class="tt-sep"&gt;${s}&lt;/div&gt;`;        html += `&lt;div class="tt-row"&gt;&lt;span class="tt-key"&gt;Median&lt;/span&gt;&lt;span class="tt-val" style="color:${this._setColor(s)}"&gt;${st.median.toFixed(2)}&lt;/span&gt;&lt;/div&gt;`;        html += `&lt;div class="tt-row"&gt;&lt;span class="tt-key"&gt;Mean&lt;/span&gt;&lt;span class="tt-val"&gt;${st.mean.toFixed(2)}&lt;/span&gt;&lt;/div&gt;`;      }    }    this.tooltip.innerHTML = html;    this.tooltip.classList.add('show');  }  _hideTooltip() { this.tooltip.classList.remove('show'); }  /* ─── Stats panel ─── */  _updateStats(intersections) {    const totalElems = new Set();    intersections.forEach(ix =&gt; ix.elements.forEach(e =&gt; totalElems.add(e.id)));    document.getElementById('stat-intersections').textContent = intersections.length;    document.getElementById('stat-elements').textContent = totalElems.size;    document.getElementById('stat-sets').textContent = this.activesets.size;    const maxDeg = intersections.length ? Math.max(...intersections.map(ix =&gt; ix.degree)) : 0;    document.getElementById('stat-maxdeg').textContent = maxDeg;  }}/* ─── Boot ─── */async function boot() {  try {    const resp = await fetch('data.json');    if (!resp.ok) throw new Error('data.json not found');    const data = await resp.json();    window._upsetApp = new UpSetApp(data);  } catch (e) {    document.getElementById('upset-wrap').innerHTML =      `&lt;div class="empty-state"&gt;⚠ Could not load data.json&lt;br&gt;&lt;small&gt;${e.message}&lt;/small&gt;&lt;/div&gt;`;    console.error(e);  }}document.addEventListener('DOMContentLoaded', boot);window.addEventListener('resize', () =&gt; {  if (window._upsetApp) window._upsetApp._render();});<br></p><p><br></p><p><br></p><p>#css</p><p>/* =============================================   UpSet Plot — Industrial Data-Lab Aesthetic   ============================================= */@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600;700&amp;family=Syne:wght@400;600;800&amp;display=swap');:root {  --bg:        #0f0f12;  --surface:   #16161c;  --surface2:  #1e1e27;  --border:    #2a2a38;  --accent:    #00e5ff;  --accent2:   #ff4081;  --text:      #e8e8f0;  --muted:     #6b6b85;  --dot-off:   #2a2a38;  --radius:    6px;  --bar-h:     180px;  --cell:      52px;  --label-w:   110px;}*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }body {  font-family: 'JetBrains Mono', monospace;  background: var(--bg);  color: var(--text);  min-height: 100vh;  overflow-x: auto;}/* ─── Header ─── */header {  display: flex;  align-items: center;  gap: 18px;  padding: 20px 32px;  border-bottom: 1px solid var(--border);  background: var(--surface);  position: sticky;  top: 0;  z-index: 100;}header h1 {  font-family: 'Syne', sans-serif;  font-weight: 800;  font-size: 1.35rem;  letter-spacing: -0.02em;  color: var(--text);}header h1 span { color: var(--accent); }.badge {  font-size: 0.62rem;  letter-spacing: 0.12em;  padding: 3px 8px;  border: 1px solid var(--accent);  color: var(--accent);  border-radius: 2px;  text-transform: uppercase;}/* ─── Layout ─── */.layout {  display: flex;  gap: 0;  min-height: calc(100vh - 61px);}/* ─── Sidebar ─── */.sidebar {  width: 260px;  min-width: 220px;  background: var(--surface);  border-right: 1px solid var(--border);  padding: 20px 16px;  display: flex;  flex-direction: column;  gap: 24px;  flex-shrink: 0;}.sidebar-section h3 {  font-size: 0.68rem;  letter-spacing: 0.14em;  text-transform: uppercase;  color: var(--muted);  margin-bottom: 12px;  padding-bottom: 6px;  border-bottom: 1px solid var(--border);}/* Set toggles */.set-toggle {  display: flex;  align-items: center;  gap: 10px;  padding: 7px 10px;  border-radius: var(--radius);  cursor: pointer;  transition: background 0.15s;  user-select: none;}.set-toggle:hover { background: var(--surface2); }.set-toggle input { display: none; }.set-toggle .swatch {  width: 12px; height: 12px;  border-radius: 2px;  flex-shrink: 0;  transition: opacity 0.2s;}.set-toggle.off .swatch { opacity: 0.25; }.set-toggle .set-name { font-size: 0.82rem; flex: 1; transition: color 0.2s; }.set-toggle.off .set-name { color: var(--muted); }.set-count { font-size: 0.72rem; color: var(--muted); }/* Sort / generic button group */.sort-group { display: flex; flex-direction: column; gap: 6px; }.sort-btn, .box-mode-btn {  background: none;  border: 1px solid var(--border);  color: var(--muted);  font-family: 'JetBrains Mono', monospace;  font-size: 0.75rem;  padding: 6px 10px;  border-radius: var(--radius);  cursor: pointer;  text-align: left;  transition: all 0.15s;}.sort-btn:hover, .box-mode-btn:hover { border-color: var(--accent); color: var(--accent); }.sort-btn.active, .box-mode-btn.active {  border-color: var(--accent);  color: var(--accent);  background: rgba(0,229,255,0.06);}/* Min size filter */.filter-row { display: flex; flex-direction: column; gap: 8px; }.filter-row label {  font-size: 0.72rem; color: var(--muted);  display: flex; justify-content: space-between;}.filter-row label span { color: var(--accent); }input[type="range"] {  -webkit-appearance: none;  width: 100%; height: 3px;  background: var(--border); border-radius: 2px; outline: none;}input[type="range"]::-webkit-slider-thumb {  -webkit-appearance: none;  width: 14px; height: 14px; border-radius: 50%;  background: var(--accent); cursor: pointer; border: 2px solid var(--bg);}/* Degree filter */.degree-group { display: flex; flex-wrap: wrap; gap: 5px; }.deg-btn {  font-family: 'JetBrains Mono', monospace;  font-size: 0.72rem; padding: 4px 9px;  border-radius: var(--radius); border: 1px solid var(--border);  background: none; color: var(--muted); cursor: pointer; transition: all 0.15s;}.deg-btn:hover { border-color: var(--accent2); color: var(--accent2); }.deg-btn.active {  border-color: var(--accent2); color: var(--accent2);  background: rgba(255,64,129,0.08);}/* Stats panel */.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }.stat-cell {  background: var(--surface2); border: 1px solid var(--border);  border-radius: var(--radius); padding: 10px 8px; text-align: center;}.stat-cell .sv { font-size: 1.1rem; font-weight: 600; color: var(--accent); }.stat-cell .sk { font-size: 0.62rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }/* Reset btn */.reset-btn {  background: none; border: 1px solid var(--border); color: var(--muted);  font-family: 'JetBrains Mono', monospace; font-size: 0.75rem;  padding: 8px; border-radius: var(--radius); cursor: pointer;  text-align: center; transition: all 0.15s; width: 100%;}.reset-btn:hover { border-color: var(--accent2); color: var(--accent2); }/* ─── Main ─── */.main {  flex: 1; padding: 28px 32px;  display: flex; flex-direction: column; gap: 0;  overflow-x: auto;}/* ─── UpSet chart ─── */#upset-wrap { display: inline-flex; flex-direction: column; min-width: 600px; }/* Bar chart */.bar-area { display: flex; align-items: flex-end; gap: 0; }.bar-yaxis {  width: var(--label-w); flex-shrink: 0;  display: flex; flex-direction: column; justify-content: space-between;  align-items: flex-end; padding-right: 8px;}.bar-yaxis .tick { font-size: 0.62rem; color: var(--muted); line-height: 1; }.bars-row { display: flex; align-items: flex-end; gap: 6px; }.bar-col {  width: var(--cell); display: flex; flex-direction: column;  align-items: center; gap: 3px; cursor: pointer; position: relative;}.bar-col .bar-val {  font-size: 0.65rem; color: var(--muted);  position: absolute; top: -16px; white-space: nowrap;}.bar-col .bar-inner {  width: 32px; background: var(--accent);  border-radius: 3px 3px 0 0;  transition: background 0.15s;  min-height: 4px;}.bar-col.highlighted .bar-inner,.bar-col:hover .bar-inner { background: var(--accent2); }.bar-col.selected .bar-inner { background: var(--accent2) !important; }/* Divider */.chart-divider { height: 1px; background: var(--border); width: 100%; }/* Matrix */.matrix-area { display: flex; }.set-labels { width: var(--label-w); flex-shrink: 0; display: flex; flex-direction: column; }.set-label-row {  height: var(--cell); display: flex; align-items: center;  gap: 8px; padding-right: 8px; justify-content: flex-end;}.set-label-row .swatch-sm { width: 8px; height: 8px; border-radius: 1px; flex-shrink: 0; }.set-label-row span {  font-size: 0.75rem; color: var(--text);  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;  max-width: 75px; text-align: right;}.dot-cols { display: flex; gap: 6px; }.dot-col {  width: var(--cell); display: flex; flex-direction: column;  align-items: center; cursor: pointer; position: relative;}.dot-col::before {  content: ''; position: absolute; top: 0; bottom: 0;  left: 50%; transform: translateX(-50%);  width: 1px; background: var(--border); z-index: 0;}.dot-col.highlighted::before,.dot-col.selected::before { background: rgba(255,64,129,0.3); }.dot {  width: 18px; height: 18px; border-radius: 50%;  margin: calc((var(--cell) - 18px)/2) 0; flex-shrink: 0;  transition: background 0.15s, transform 0.15s;  position: relative; z-index: 1;}.dot.off { background: var(--dot-off); }.dot.on  { background: var(--accent); }.dot-col.highlighted .dot.on,.dot-col.selected .dot.on { background: var(--accent2); }.dot-col:hover .dot.on { transform: scale(1.25); }.dot-col .connector {  position: absolute; left: 50%; transform: translateX(-50%);  width: 3px; background: var(--accent); z-index: 0;  border-radius: 2px; transition: background 0.15s;}.dot-col.highlighted .connector,.dot-col.selected .connector { background: var(--accent2); }/* Set size bars */.size-area { display: flex; flex-direction: column; }.size-row { height: var(--cell); display: flex; align-items: center; padding-left: 12px; gap: 6px; }.size-bar { height: 10px; border-radius: 2px; background: var(--accent); min-width: 3px; }.size-val { font-size: 0.65rem; color: var(--muted); white-space: nowrap; }/* ─── Boxplot section ─── */.boxplot-section { margin-top: 40px; padding-top: 28px; border-top: 1px solid var(--border); }.section-title {  font-family: 'Syne', sans-serif; font-weight: 600;  font-size: 0.85rem; letter-spacing: 0.08em; text-transform: uppercase;  color: var(--muted); margin-bottom: 20px;}.section-title span { color: var(--accent); }#boxplot-canvas { display: block; width: 100%; max-width: 900px; }/* ─── Tooltip ─── */#tooltip {  position: fixed; background: var(--surface2);  border: 1px solid var(--accent); border-radius: var(--radius);  padding: 10px 14px; font-size: 0.72rem; line-height: 1.7;  pointer-events: none; opacity: 0; transition: opacity 0.15s;  z-index: 999; max-width: 240px; color: var(--text);}#tooltip.show { opacity: 1; }#tooltip .tt-title { font-weight: 600; color: var(--accent); margin-bottom: 4px; font-size: 0.78rem; }#tooltip .tt-row { display: flex; justify-content: space-between; gap: 12px; }#tooltip .tt-key { color: var(--muted); }#tooltip .tt-val { color: var(--text); font-weight: 600; }#tooltip .tt-sep {  font-size: 0.65rem; letter-spacing: 0.1em; text-transform: uppercase;  color: var(--muted); margin-top: 6px; padding-top: 4px;  border-top: 1px solid var(--border);}/* ─── Empty ─── */.empty-state { padding: 60px 20px; text-align: center; color: var(--muted); font-size: 0.8rem; }/* ─── Scrollbar ─── */::-webkit-scrollbar { width: 6px; height: 6px; }::-webkit-scrollbar-track { background: var(--bg); }::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }::-webkit-scrollbar-thumb:hover { background: var(--muted); }<br></p><p><br></p><p><br></p><p># json</p><p>{  "sets": [    { "name": "Set A", "color": "#e74c3c" },    { "name": "Set B", "color": "#3498db" },    { "name": "Set C", "color": "#2ecc71" },    { "name": "Set D", "color": "#f39c12" },    { "name": "Set E", "color": "#9b59b6" }  ],  "elements": [    { "id":  1, "label": "Elem 1",  "sets": { "Set A": 12.3 } },    { "id":  2, "label": "Elem 2",  "sets": { "Set A": 15.1 } },    { "id":  3, "label": "Elem 3",  "sets": { "Set A": 9.8  } },    { "id":  4, "label": "Elem 4",  "sets": { "Set A": 18.4 } },    { "id":  5, "label": "Elem 5",  "sets": { "Set A": 11.0 } },    { "id":  6, "label": "Elem 6",  "sets": { "Set B": 22.5 } },    { "id":  7, "label": "Elem 7",  "sets": { "Set B": 19.3 } },    { "id":  8, "label": "Elem 8",  "sets": { "Set B": 25.0 } },    { "id":  9, "label": "Elem 9",  "sets": { "Set B": 17.8 } },    { "id": 10, "label": "Elem 10", "sets": { "Set C": 8.2  } },    { "id": 11, "label": "Elem 11", "sets": { "Set C": 6.5  } },    { "id": 12, "label": "Elem 12", "sets": { "Set C": 10.1 } },    { "id": 13, "label": "Elem 13", "sets": { "Set D": 30.2 } },    { "id": 14, "label": "Elem 14", "sets": { "Set D": 28.7 } },    { "id": 15, "label": "Elem 15", "sets": { "Set D": 33.5 } },    { "id": 16, "label": "Elem 16", "sets": { "Set E": 5.5  } },    { "id": 17, "label": "Elem 17", "sets": { "Set E": 7.2  } },    { "id": 18, "label": "Elem 18", "sets": { "Set A": 14.0, "Set B": 8.5  } },    { "id": 19, "label": "Elem 19", "sets": { "Set A": 16.3, "Set B": 11.2 } },    { "id": 20, "label": "Elem 20", "sets": { "Set A": 13.5, "Set B": 9.0  } },    { "id": 21, "label": "Elem 21", "sets": { "Set A": 10.5, "Set C": 4.2  } },    { "id": 22, "label": "Elem 22", "sets": { "Set A": 12.8, "Set C": 5.9  } },    { "id": 23, "label": "Elem 23", "sets": { "Set A": 25.4, "Set D": 31.0 } },    { "id": 24, "label": "Elem 24", "sets": { "Set A": 27.1, "Set D": 29.4 } },    { "id": 25, "label": "Elem 25", "sets": { "Set B": 18.9, "Set C": 7.3  } },    { "id": 26, "label": "Elem 26", "sets": { "Set B": 20.3, "Set C": 8.8  } },    { "id": 27, "label": "Elem 27", "sets": { "Set B": 26.7, "Set D": 32.1 } },    { "id": 28, "label": "Elem 28", "sets": { "Set C": 7.8,  "Set E": 3.1  } },    { "id": 29, "label": "Elem 29", "sets": { "Set D": 29.5, "Set E": 6.4  } },    { "id": 30, "label": "Elem 30", "sets": { "Set A": 13.2, "Set B": 7.8,  "Set C": 3.5  } },    { "id": 31, "label": "Elem 31", "sets": { "Set A": 15.7, "Set B": 9.3,  "Set C": 4.1  } },    { "id": 32, "label": "Elem 32", "sets": { "Set A": 22.1, "Set B": 14.5, "Set D": 28.9 } },    { "id": 33, "label": "Elem 33", "sets": { "Set A": 20.8, "Set C": 6.2,  "Set D": 27.3 } },    { "id": 34, "label": "Elem 34", "sets": { "Set B": 16.4, "Set C": 5.7,  "Set E": 2.9  } },    { "id": 35, "label": "Elem 35", "sets": { "Set A": 19.5, "Set B": 12.1, "Set C": 4.8,  "Set D": 26.0 } },    { "id": 36, "label": "Elem 36", "sets": { "Set A": 21.3, "Set B": 13.7, "Set C": 5.5,  "Set D": 24.8 } },    { "id": 37, "label": "Elem 37", "sets": { "Set A": 18.0, "Set B": 11.5, "Set C": 4.3,  "Set D": 25.5, "Set E": 3.8 } }  ]}<br></p><p><br></p><p><br></p><p><br></p><p><br></p>
]]>
</description>
<link>https://ameblo.jp/settty/entry-12966225819.html</link>
<pubDate>Fri, 15 May 2026 13:38:18 +0900</pubDate>
</item>
<item>
<title>Upset plot</title>
<description>
<![CDATA[ <p>#HTML</p><p>&lt;!DOCTYPE html&gt;&lt;html lang="ja"&gt;&lt;head&gt;  &lt;meta charset="UTF-8" /&gt;  &lt;title&gt;UpSetJS Bundle Example&lt;/title&gt;  &lt;link rel="stylesheet" href="style.css" /&gt;  &lt;!-- UpsetJS Bundle (Preact 内蔵 / UMD) --&gt;  &lt;script src="https://unpkg.com/@upsetjs/bundle/umd/upsetjs-bundle.min.js"&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt;  &lt;div id="root"&gt;&lt;/div&gt;  &lt;!-- 外部 JavaScript --&gt;  &lt;script src="app.js"&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;<br></p><p><br></p><p>#JS</p><p>// UpsetJS bundle のエイリアスconst { h, render } = upsetjsBundle.preact;const { UpSetJS } = upsetjsBundle.react;const { extractCombinations } = upsetjsBundle.model;// JSON 読み込みasync function loadJSON() {  const res = await fetch("data.json");  return await res.json();}// Filter Panel Componentfunction FilterPanel(props) {  const {    sets,    combinations,    activeSets,    setActiveSets,    activeGroups,    setActiveGroups,    search,    setSearch,    sort,    setSort  } = props;  return h("div", { class: "panel" }, [    h("h3", {}, "Search"),    h("input", {      class: "search-box",      type: "text",      value: search,      placeholder: "Search sets/groups",      onInput: (e) =&gt; setSearch(e.target.value)    }),    h("h3", {}, "Sort"),    h(      "select",      {        class: "sort-select",        value: sort,        onInput: (e) =&gt; setSort(e.target.value)      },      [        h("option", { value: "name" }, "Name"),        h("option", { value: "size" }, "Size")      ]    ),    h("h3", {}, "Sets"),    ...sets      .filter((s) =&gt; s.name.includes(search))      .map((s) =&gt;        h("label", {}, [          h("input", {            type: "checkbox",            checked: activeSets[s.name],            onInput: () =&gt;              setActiveSets({                ...activeSets,                [s.name]: !activeSets[s.name]              })          }),          s.name        ])      ),    h("h3", {}, "Groups"),    ...combinations      .filter((c) =&gt; c.name.includes(search))      .map((c) =&gt;        h("label", {}, [          h("input", {            type: "checkbox",            checked: activeGroups[c.name] ?? false,            onInput: () =&gt;              setActiveGroups({                ...activeGroups,                [c.name]: !activeGroups[c.name]              })          }),          `${c.name} (${c.size})`        ])      )  ]);}// Main App Componentfunction App({ data }) {  const [activeSets, setActiveSets] = upsetjsBundle.preactHooks.useState(() =&gt; {    const all = new Set(data.elements.flatMap((e) =&gt; e.sets));    return Object.fromEntries([...all].map((s) =&gt; [s, true]));  });  const [activeGroups, setActiveGroups] = upsetjsBundle.preactHooks.useState({});  const [search, setSearch] = upsetjsBundle.preactHooks.useState("");  const [sort, setSort] = upsetjsBundle.preactHooks.useState("name");  const filteredBySet = data.elements.filter((elem) =&gt;    elem.sets.some((s) =&gt; activeSets[s])  );  const { sets, combinations } = extractCombinations(filteredBySet);  const sortedCombinations =    sort === "size"      ? [...combinations].sort((a, b) =&gt; b.size - a.size)      : [...combinations].sort((a, b) =&gt; a.name.localeCompare(b.name));  const filteredCombinations = Object.keys(activeGroups).some(    (k) =&gt; activeGroups[k]  )    ? sortedCombinations.filter((c) =&gt; activeGroups[c.name])    : sortedCombinations;  const attributes = [    {      type: "boxplot",      label: "Boxplot",      accessor: (elem, combination) =&gt; elem.values?.[combination.name] ?? null    },    {      type: "scatter",      label: "Scatter",      accessor: (elem, combination) =&gt; elem.values?.[combination.name] ?? null    }  ];  return h("div", { id: "layout", style: "display:flex; gap:20px;" }, [    h(FilterPanel, {      sets,      combinations,      activeSets,      setActiveSets,      activeGroups,      setActiveGroups,      search,      setSearch,      sort,      setSort    }),    h(UpSetJS, {      sets,      combinations: filteredCombinations,      attributes,      width: 800,      height: 600    })  ]);}// JSON を読み込んで描画loadJSON().then((data) =&gt; {  render(h(App, { data }), document.getElementById("root"));});<br></p><p><br></p><p>#CSS</p><p>body {  font-family: sans-serif;  margin: 20px;  display: flex;  gap: 20px;}#root {  display: flex;  gap: 20px;}.panel {  min-width: 240px;  border-right: 1px solid #ccc;  padding-right: 10px;}.panel h3 {  margin-top: 20px;  margin-bottom: 8px;}label {  display: block;  margin-bottom: 4px;}.search-box {  margin-bottom: 10px;}.sort-select {  margin-bottom: 10px;}<br></p><p><br></p><p>#data</p><p>{  "elements": [    {      "name": "A",      "sets": ["S1", "S2"],      "values": { "S1": 5, "S2": 8 }    },    {      "name": "B",      "sets": ["S1"],      "values": { "S1": 3 }    },    {      "name": "C",      "sets": ["S2"],      "values": { "S2": 10 }    }  ]}<br></p><p><br></p><p><br></p><p><br></p><p><br></p><p><br></p><p><br></p>
]]>
</description>
<link>https://ameblo.jp/settty/entry-12966205989.html</link>
<pubDate>Fri, 15 May 2026 09:42:10 +0900</pubDate>
</item>
</channel>
</rss>
