【ThoughtSpotTips】MuzeStudioでExcelのデータバーを実現する

ThoughtSpotのMuzeStudioでExcelのデータバーを実現する方法をご紹介します

 

AkimasaKajitaniです。

 

Excelのデータバーの機能をご存じでしょうか?これを使うと、値の後ろ側にオーバーレイで棒グラフを描くことが可能です。今回はMuzeStudioで実現する方法を探りたいと思います。

 

 

Muze Studioでどのようにデータバーを実現できるか?

Muze Studioは、JavaScriptの独自ライブラリ(Muze)を使って、チャートを描く機能です。実はJavaScriptを実行する環境なので、Muze以外のJavaScrptライブラリも動くというのがポイントです。ただしその場合(Muze以外のライブラリを使う場合)、いくつか制約があり、ドリルダウンができない、とかこのチャートから他のチャートへのリンクフィルタがきかない、Excelエクスポートができない、といった制約があります。

それでも良い、という場合は多機能なJavaScriptライブラリがそのまま使えるため、様々な表現ができるようになります。

 

実際、データバーを実現するためにはいくつかの方法があります。

1.3rdパーティのライブラリを使う

2.HTMLべた書き

 

1.3rdパーティのライブラリを使う

3rdパーティのライブラリを使う場合は、使用するJavaScriptのライブラリ次第です。例としては「Tabulator」を使うのがよさそうです。

 

例えば、以下のような画面を作成できます。

 

2.HTMLべた書き

実は、HTML+CSSでも表現は可能です。意外とこれいい感じに作れたので、紹介したいと思います。基本、JS、HTML、CSSを三位一体で作りますが、設定が完了するとHTMLをいじるだけで項目の差し替えが対応可能です。

 

 

実際のコードは以下のようになります。ちなみに、前提条件として、以下の検索キーワードを使っています。

  • カテゴリ
  • サブカテゴリ
  • 地域
  • Total 売上
  • Total 利益

 

JavaScript(JS)

通常、MuzeStudioを使う場合、このJavaScriptを編集していくのがメインになるのですが、今回のコードでは一度作ってしまえば全く触る必要がありません。このまま張り付けてください、

 

/**
 * Available Columns:
 * "カテゴリ"
 * "サブカテゴリ"
 * "地域"
 * "Total 売上"
 * "Total 利益"
 * "Measure names" // If 'measureValues' is enabled.
 * "Measure values" // If 'measureValues' is enabled.
 * --- END --- 
 */

const { getDataFromSearchQuery, formatters, events } = viz;
const { NumberFormatter, DateFormatter } = formatters;

/**
 * Rows keyed by column name rather than position, with cells unwrapped - some
 * clusters hand them back as { value } objects instead of primitives.
 */
function toObjects(dm) {
  const g = dm.getData();
  if (Array.isArray(g)) return g;                     // already array-of-objects
  const names = (g.schema || []).map(f => f.name || f);
  const val = v => (v && typeof v === "object" ? (typeof v.value === "function" ? v.value() : v.value) : v);
  return (g.data || []).map(arr => {
    const o = {};
    names.forEach((n, i) => (o[n] = val(arr[i])));
    return o;
  });
}

const esc = s => String(s ?? "").replace(/[&<>"]/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));

// A thrown error would otherwise leave the tile unreported and block PDF export of the whole Liveboard.
try {
  const root = document.getElementById("chart");
  const rows = toObjects(getDataFromSearchQuery());

  const cols = [...root.querySelectorAll("thead th")].map(th => {
    const d = th.dataset;
    const c = {
      field: d.field,
      bar: "bar" in d,
      date: "date" in d,
      color: d.color || root.dataset.barColor,
      neg: d.negColor || root.dataset.negColor,
      fmt: "date" in d
        ? new DateFormatter({ format: d.date || "yyyy/MM/dd" })
        : new NumberFormatter("decimal", { unit: "none", decimalDigits: Number(d.decimals || 0) }),
    };
    if (c.bar) {
      // Like Excel: the axis sits at zero, so a column with negatives splits left/right of it.
      const vs = rows.map(r => Number(r[c.field]) || 0);
      c.lo = vs.reduce((a, v) => Math.min(a, v), 0);
      c.span = vs.reduce((a, v) => Math.max(a, v), 0) - c.lo || 1;
    }
    return c;
  });

  const cell = (c, v) => {
    if (c.date) return `<td>${esc(c.fmt.format(Number(v)))}</td>`;
    if (typeof v !== "number") return `<td>${esc(v)}</td>`;
    const text = c.fmt.format(v);
    if (!c.bar) return `<td class="num">${text}</td>`;
    const zero = -c.lo / c.span;
    const w = Math.abs(v) / c.span;
    const l = v < 0 ? zero - w : zero;
    return `<td class="num bar${v < 0 ? " neg" : ""}" style="--l:${l};--w:${w};--c:${v < 0 ? c.neg : c.color}"><span>${text}</span></td>`;
  };

  root.querySelector("tbody").innerHTML = rows
    .map(r => `<tr>${cols.map(c => cell(c, r[c.field])).join("")}</tr>`)
    .join("");
} finally {
  events.emitRenderCompletedEvent();
}

 

HTML

 

今回のメインはこのHTML部分です。項目を追加したり削除したりといった変更がある場合、ここのHTML部分を更新していく必要があります。

例えば、項目の増減は、「<th data-fiel="フィールド名">フィールド名</th>」の部分を減らしたり増やしたりすることで対応します。もちろん、「検索」の項目も同様に増やしたり減らしたりする必要があります。細かい体裁も、このHTMLのTHタグのプロパティを変更することで対応が可能です。

コメント部分にも記載されていますが、以下のようなオプションで細かい制御が可能なコードとなっています。

  • data-field : 検索結果の列名(ThoughtSpotの列名と完全一致)
  • data-bar : 付けるとデータバーを表示
  • data-color : この列のバー色(省略時は #chart の data-bar-color)
  • data-neg-color : この列の負値の色(省略時は #chart の data-neg-color)
  • data-decimals : 小数桁数(省略時 0)
  • data-date : 日付列。値に書式を書く(例: data-date="yyyy/MM")

 

<!--
  列を増やすときは <th> を1行足すだけ。
    data-field     : 検索結果の列名(ThoughtSpotの列名と完全一致)
    data-bar       : 付けるとデータバーを表示
    data-color     : この列のバー色(省略時は #chart の data-bar-color)
    data-neg-color : この列の負値の色(省略時は #chart の data-neg-color)
    data-decimals  : 小数桁数(省略時 0)
    data-date      : 日付列。値に書式を書く(例: data-date="yyyy/MM")
  <th> の中身が見出しとして表示されます。
-->
<div id="chart" data-bar-color="#4e79a7" data-neg-color="#e15759">
  <table>
    <thead>
      <tr>
        <th data-field="カテゴリ">カテゴリ</th>
        <th data-field="サブカテゴリ">サブカテゴリ</th>
        <th data-field="地域">地域</th>
        <th data-field="Total 売上" data-bar style="width:100px">売上</th>
        <th data-field="Total 利益" data-bar data-color="#59a14f">利益</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>
</div>

 

CSS

 

CSSはスタイルを決めるだけなのでフォーマットさえ作ってしまえば特に触ることはありません。これもこのまま張り付けます。

 

html, body { width: 100%; height: 100%; margin: 0; }

#chart {
  height: 100%;
  overflow: auto;
  font: 13px/1.4 -apple-system, "Segoe UI", "Hiragino Sans", "Meiryo", sans-serif;
  color: #1d1d1f;
}

table { width: 100%; border-collapse: collapse; }
/* Auto layout treats a th width as a hint only; once any width is set by hand, honour it exactly. */
table:has(th[style*="width"]) { table-layout: fixed; }

th, td { padding: 6px 10px; border-bottom: 1px solid #e3e5e8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box; }

thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #f4f5f7;
  text-align: left;
  font-weight: 600;
}

thead th[data-bar], thead th[data-decimals] { text-align: right; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Data bar: --l and --w are fractions (0-1) of the cell's inner width, set per cell from JS. */
td.bar { position: relative; min-width: 120px; }
td.bar::before {
  content: "";
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: calc(4px + var(--l) * (100% - 8px));
  width: calc(var(--w) * (100% - 8px));
  background: linear-gradient(90deg, var(--c), color-mix(in srgb, var(--c) 15%, #fff));
  border: 1px solid var(--c);
  box-sizing: border-box;
}
/* Negative bars grow leftwards from zero, so the gradient fades the other way. */
td.bar.neg::before { background: linear-gradient(270deg, var(--c), color-mix(in srgb, var(--c) 15%, #fff)); }
td.bar > span { position: relative; }

tbody tr:hover td { background: #f9fafb; }

 

なお、実際に利用する場合、項目名でソートしたい、など様々な要望があるかと思いますが、その場合は「次で並べ替え」キーワードなどをうまく使ってください。また、項目をクリックするとソートする、などの機能も生成AIを使ったコーディングで比較的さくっと作れるのでいろいろ試してみてはいかがでしょうか?

 

まとめ

Muze Studioを使って、Muzeライブラリを使わい方法でExcelのデータバーを表現する方法をご紹介しました。

最近は生成AIを使うとこういうのもぱぱっと作れるようになってきたので、ぜひお試しいただければと思います。

 

※2026/11/9時点の情報です

おすすめの記事