iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0

概述

  • 圓餅圖的作用,就是用一張圖快速地看出「佔比」,分析組成的內容是什麼。
  • 圓餅圖內所有比例的加總是 100%

困擾已久的問題

  • 以「陸客蛋塔效應轉型」圓餅圖使用範例
    • 該圓餅圖出現了 「台中人20%」
    • 因此「非台中人」的族群,也可以被稱為「外來客80%」
    • 至於有爭議的部分,應該就是「陸客30%」了吧!!
      • 陸客不是「台中人」
      • 陸客是「外來客」的「子集合」
        • 假設陸客佔整體的 30%,那其他外來客佔整體 50%,圖片中的外來客應該是佔圓餅的一半,與事實不符,因此假設錯誤。
        • 假設陸客佔外來客的 30%,等價於陸客佔整體 24%,則其他外來客應佔整體 56%,畫面中的比例則可能成立。

實作

參考文件

生成資料

  • 根據上述鍵盤柯南的推理,我自己生成了資料 逢甲客層來源 (fengchia_guest_source),長得像下方這樣
fengchia_guest_source = [
  { 客源:'外來客(不含陸客)', 比例: 80*(1-0.3) },
  { 客源:'台中人', 比例: 20 },
  { 客源:'陸客', 比例: 80*0.3 },
]
  • 根據範例,把畫圖的區塊貼上來,修改其中傳入的參數變成 fengchia_guest_source
    • 利用箭頭函示,提取傳入資料中,所需要的內容 namevalue
chart = DonutChart(fengchia_guest_source, {
  name: d => d.客源,
  value: d => d.比例,
  width,
  height: 500
})
  • 結果如下

  • 我看到幾個問題

    • 首先,這邊圍繞一圈的 name 和 value,value 是純粹的數字、統計出來的數字,他不是百分比。
    • 如果要換成百分比的話,有些地方要處理。
    • 探討我提供的資料本身,因為剛好湊出來是 100
      • 考量記者並非數學系畢業的可能性,我打算先教他們標上百分比符號,結束這回合。
      • 如果要換算裡面的值,不管丟進去多少的數字,最後都要加總等於 100% 的那種狀況,之後再來討論。
        20 + 80*(1-0.3) + 80*0.3 = 20 + 56 + 24 = 100

修改 DonutChart function

  • 打開開發者工具,用小箭頭功能,找出 DOM 的位置

    • 叫做 tspan 的 tag 是吧?!
  • DonutChart 中,理解出這些文字怎麼被謄寫上來的

  • 原本長這樣

svg.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "middle")
    .selectAll("text")
    .data(arcs)
    .join("text")
      .attr("transform", d => `translate(${arcLabel.centroid(d)})`)
    .selectAll("tspan")
    .data(d => {
      const lines = `${title(d.data)}`.split(/\n/);
      return (d.endAngle - d.startAngle) > 0.25 ? lines : lines.slice(0, 1);
    })
    .join("tspan")
      .attr("x", 0)
      .attr("y", (_, i) => `${i * 1.1}em`)
      .attr("font-weight", (_, i) => i ? null : "bold")
      .text(d => d);
  • 那個字太小了,老闆通常有老花看不清楚,我把它改成 .attr("font-size", 20)
  • 最後一行 .text(d => d); 就是直接把傳進來的值,原封不動謄寫上去。
    • 剛剛說了,我要幫他加上 % 符號
    • 欸!怎麼每個 <tspan> 中的文字,都被加了 %

使用 Arrow function / one line statement 判斷

  • 如果你仔細看開發者工具裡面爬到的內容,標題和數字,一個有加粗,一個沒加粗,怎麼做到的?
  • 哦哦,透過一行判斷 i 的值是吧
  • 我們如法炮製,那個 Arrow function
(d, i) => i==0 ? d : d + " %"
  • 分成幾個部分來看
    • 箭頭的左邊是參數、被括號包起來的那些東西
    • 箭頭和問號中間的是判斷,透過 i == 0 這樣的敘述,會回傳「真」或「假」
    • 問號右邊的 d : d + " %" 表示,如果上述條件為真,回傳冒號左邊的東西,如果上述條件為假,回傳冒號右邊的東西。
  • 請參考 箭頭函示的做法

修改後的 DonutChart 的部分內容如下

修改後的 DonutChart 的完整內容如下

// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/donut-chart
function DonutChart(data, {
  name = ([x]) => x,  // given d in data, returns the (ordinal) label
  value = ([, y]) => y, // given d in data, returns the (quantitative) value
  title, // given d in data, returns the title text
  width = 640, // outer width, in pixels
  height = 400, // outer height, in pixels
  innerRadius = Math.min(width, height) / 3, // inner radius of pie, in pixels (non-zero for donut)
  outerRadius = Math.min(width, height) / 2, // outer radius of pie, in pixels
  labelRadius = (innerRadius + outerRadius) / 2, // center radius of labels
  format = ",", // a format specifier for values (in the label)
  names, // array of names (the domain of the color scale)
  colors, // array of colors for names
  stroke = innerRadius > 0 ? "none" : "white", // stroke separating widths
  strokeWidth = 1, // width of stroke separating wedges
  strokeLinejoin = "round", // line join of stroke separating wedges
  padAngle = stroke === "none" ? 1 / outerRadius : 0, // angular separation between wedges
} = {}) {
  // Compute values.
  const N = d3.map(data, name);
  const V = d3.map(data, value);
  const I = d3.range(N.length).filter(i => !isNaN(V[i]));

  // Unique the names.
  if (names === undefined) names = N;
  names = new d3.InternSet(names);

  // Chose a default color scheme based on cardinality.
  if (colors === undefined) colors = d3.schemeSpectral[names.size];
  if (colors === undefined) colors = d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), names.size);

  // Construct scales.
  const color = d3.scaleOrdinal(names, colors);

  // Compute titles.
  if (title === undefined) {
    const formatValue = d3.format(format);
    title = i => `${N[i]}\n${formatValue(V[i])}`;
  } else {
    const O = d3.map(data, d => d);
    const T = title;
    title = i => T(O[i], i, data);
  }

  // Construct arcs.
  const arcs = d3.pie().padAngle(padAngle).sort(null).value(i => V[i])(I);
  const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);
  const arcLabel = d3.arc().innerRadius(labelRadius).outerRadius(labelRadius);
  
  const svg = d3.create("svg")
      .attr("width", width)
      .attr("height", height)
      .attr("viewBox", [-width / 2, -height / 2, width, height])
      .attr("style", "max-width: 100%; height: auto; height: intrinsic;");

  svg.append("g")
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth)
      .attr("stroke-linejoin", strokeLinejoin)
    .selectAll("path")
    .data(arcs)
    .join("path")
      .attr("fill", d => color(N[d.data]))
      .attr("d", arc)
    .append("title")
      .text(d => title(d.data));

  svg.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 20)
      .attr("text-anchor", "middle")
    .selectAll("text")
    .data(arcs)
    .join("text")
      .attr("transform", d => `translate(${arcLabel.centroid(d)})`)
    .selectAll("tspan")
    .data(d => {
      const lines = `${title(d.data)}`.split(/\n/);
      return (d.endAngle - d.startAngle) > 0.25 ? lines : lines.slice(0, 1);
    })
    .join("tspan")
      .attr("x", 0)
      .attr("y", (_, i) => `${i * 1.1}em`)
      .attr("font-weight", (_, i) => i ? null : "bold")
      .text((d, i) => i == 0 ? d : d + " %");

  return Object.assign(svg.node(), {scales: {color}});
}

繪製出的圖片

結論

審閱資料的方式

  • 在搜集資料的時候,我沒有找到逢甲客層來源的相關資料
    • 不知道新聞上的數字是怎麼得到的。
    • 或許是他們當場做田野調查,接訪得到的數字。
  • 新聞或許想要塑造成陸客不來,對於攤商的衝擊很大,卻忽略了陸客本身的消費能力,例如十個人吃一份臭豆腐、十個人吃一份大腸包小腸等。
  • 就我個人的經驗,應該鄰近的大學生消費力應該比陸客高。
  • 我都去買風茹茶、黃金左腿、黃金右腿、冰火菠蘿油,吃吃喝喝難免上想上廁所,所以還會去買當勞。
  • 上一次去逢甲,已經是五年前的事情了。

其他心得

  • 陸客本來就是外來客,佔外來客的三成、不是整體的三成!!
    • 陸客頂多只佔整體的二成四,真的有那麼多嗎?
  • 陸客不來沒差,我們有報復性旅遊。
  • 經濟火車頭、經濟最好、房價最高的一年,是市場淘汰了租不起的攤販、淘汰了消費不起的群眾呀。

傳產人員的觀點

  • 老闆最喜歡會畫大餅的員工了!! 學會畫餅,好處無窮!! 還不一起加入畫餅工程師的行列?!

上一篇
【Day 8】 多系列的"D3"折線圖
下一篇
【Day 10】 依然是簡單的 “D3” 圓餅圖
系列文
適用於傳產從業人員的實用報表製圖術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言