<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【數(shù)據(jù)可視化】D3.js實(shí)現(xiàn)動(dòng)態(tài)氣泡圖

          共 25654字,需瀏覽 52分鐘

           ·

          2021-03-17 13:36



          大家好,歡迎來到 Crossin的編程教室 !


          數(shù)據(jù)處理及可視化是Python的一大應(yīng)用場(chǎng)景。不過為了實(shí)現(xiàn)更好的動(dòng)態(tài)演示效果,實(shí)際應(yīng)用中常常還需要和js相結(jié)合。


          今天我們就來給大家分享一個(gè)用D3.js實(shí)現(xiàn)的動(dòng)態(tài)氣泡圖案例。


          本文用到的語(yǔ)言主要 js,不過主要是做一些配置,所以閱讀起來并不困難。另外也建議大家有空可以了解一下基礎(chǔ)的js語(yǔ)法,會(huì)很有幫助。


          首先我們來看下 D3.js 的氣泡圖效果:



          項(xiàng)目地址:

          https://observablehq.com/@unkleho/covid-19-bubble-chart-with-d3-render


          GitHub地址:

          https://github.com/unkleho/d3-render


          要想實(shí)現(xiàn)這個(gè)項(xiàng)目的話,首先需要安裝 Node.js 以及 npm,具體的安裝步驟這里贅述,百度一下就有,還是比較簡(jiǎn)單的。


          接下來就可以安裝 Vue.js 及 Vue腳手架3.0。


          # 安裝Vue.js
          npm install vue

          # 安裝Vue-cli3腳手架
          npm install -g @vue/cli


          如此便可以創(chuàng)建項(xiàng)目了。

          # 創(chuàng)建名為bubblechart的項(xiàng)目
          vue create bubblechart


          結(jié)果如下,選擇默認(rèn)模式即可。



          由于里面有eslint(編碼規(guī)范)的存在,記得在配置文件package.json中添加下面的代碼。


          "rules": {
              "no-unused-vars""off",
              "no-undef""off"
          }


          要不然會(huì)出現(xiàn)報(bào)錯(cuò),無(wú)法運(yùn)行。



          項(xiàng)目創(chuàng)建成功后,修改App.vue文件內(nèi)容如下。


          <template>
            <div id="app">
              <svg id="bubble-chart" width="954" height="450" />
            </div>
          </template>


          <script>
          export default {
            name'App',
            components: {
            }
          }
          </script>

          <style></
          style>


          保存成功后,打開bubblechart文件夾下的終端,運(yùn)行下面這個(gè)命令。


          npm run serve


          瀏覽器便會(huì)跳出一個(gè)標(biāo)題為bubblechart的空白網(wǎng)頁(yè)。


          安裝一些項(xiàng)目依賴d3,d3-render,d3-selection,d3-transition,axios。


          npm install d3@5.16.0 --save-dev
          npm install d3-render@0.2.4 --save-dev
          npm install d3-selection@1.4.2 --save-dev
          npm install d3-transition@2.0.0 --save-dev
          npm install axios --save-dev


          最好是指定版本,要不然可能會(huì)報(bào)錯(cuò)。


          在main.js文件中引用axios,用于請(qǐng)求數(shù)據(jù)。


          import axios from 'axios'
          Vue.prototype.$axios = axios


          在App.vue的script標(biāo)簽中引用d3,d3-render。


          import * as d3 from "d3";
          import render from "d3-render";


          設(shè)置初始數(shù)據(jù),各式各樣的氣泡顏色。


          data() {
              return {
                covidDatanull,
                countriesnull,
                colours: {
                  pink"#D8352A",
                  red"#D8352A",
                  blue"#48509E",
                  green"#02A371",
                  yellow"#F5A623",
                  hyperGreen"#19C992",
                  purple"#B1B4DA",
                  orange"#F6E7AD",
                  charcoal"#383838",
                }
              };
          }


          獲取各地區(qū)的新冠數(shù)據(jù),兩個(gè)CSV文件放在Public文件夾下,可直接訪問。


          methods: {
              async getdata() {
                //獲取新冠數(shù)據(jù)      
                await this.$axios.get("data.csv").then((res) => {
                  this.covidData = d3.csvParse(res.data);
                });
                //獲取國(guó)家數(shù)據(jù)
                await this.$axios.get("countries.csv").then((res) => {
                  this.countries = d3.csvParse(res.data);
                });
                //畫圖
                this.drawType();
              },
          }



          開始畫圖的操作,先定義一下畫布大小以及各大洲的顏色。


          drawType() {
                //設(shè)置svg大小
                const width = 954;
                const height = 450;
                //設(shè)置各個(gè)大洲的參數(shù)
                const continents = [
                  {
                    id"AF",
                    name"Africa",
                    fillthis.colours.purple,
                    colourthis.colours.charcoal,
                  },
                  {
                    id"AS",
                    name"Asia",
                    fillthis.colours.yellow,
                    colourthis.colours.charcoal,
                  },
                  {
                    id"EU",
                    name"Europe",
                    fillthis.colours.blue,
                    colourthis.colours.charcoal,
                  },
                  {
                    id"NA",
                    name"N. America",
                    fillthis.colours.pink,
                  },
                  {
                    id"OC",
                    name"Oceania",
                    fillthis.colours.orange,
                    colourthis.colours.charcoal,
                  },
                  {
                    id"SA",
                    name"S. America",
                    fillthis.colours.green,
                    colourthis.colours.charcoal,
                  },
                ];
          }


          定義圓圈組件,其中duration很重要,起到一個(gè)動(dòng)畫過渡的效果。


          //定義圓圈組件
          const circleComponent = ({ r, cx, cy, fill, duration }) => {
            return {
              append"circle",
              r,
              cx,
              cy,
              fill,
              duration,
            };
          };


          定義文字組件,設(shè)置字體、大小、顏色等。


          //定義文字組件
          const textComponent = ({
              key,
              text,
              x = 0,
              y = 0,
              fontWeight = "bold",
              fontSize = "12px",
              textAnchor = "middle",
              fillOpacity = 1,
              colour,
              r,
              duration = 1000,
          }) => {
              return {
                  append"text",
                  key,
                  text,
                  x,
                  y,
                  textAnchor,
                  fontFamily"sans-serif",
                  fontWeight,
                  fontSize,
                  fillOpacity: { enter: fillOpacity, exit0 },
                  fill: colour,
                  duration,
                  style: {
                      pointerEvents"none",
                  },
              };
          };


          數(shù)值轉(zhuǎn)換,對(duì)較大的數(shù)值進(jìn)行處理。


          //對(duì)數(shù)值進(jìn)行轉(zhuǎn)換,比如42288變?yōu)?2k
          const format = (value) => {
              const newValue = d3.format("0.2s")(value);
              if (newValue.indexOf("m") > -1) {
                  return parseInt(newValue.replace("m""")) / 1000;
              }
              return newValue;
          };


          動(dòng)態(tài)變化標(biāo)簽信息,包含名稱及數(shù)值。


          //將各地區(qū)名稱長(zhǎng)度和數(shù)值與圓圈大小相比較,實(shí)現(xiàn)信息動(dòng)態(tài)變化
          const labelComponent = ({ isoCode, countryName, value, r, colour }) => {
              // Don't show any text for radius under 12px
              if (r < 12) {
                  return [];
              }
              //console.log(r);
              const circleWidth = r * 2;
              const nameWidth = countryName.length * 10;
              const shouldShowIso = nameWidth > circleWidth;
              const newCountryName = shouldShowIso ? isoCode : countryName;
              const shouldShowValue = r > 18;

              let nameFontSize;

              if (shouldShowValue) {
                  nameFontSize = shouldShowIso ? "10px" : "12px";
              } else {
                  nameFontSize = "8px";
              }

              return [
                  textComponent({
                      key: isoCode,
                      text: newCountryName,
                      fontSize: nameFontSize,
                      y: shouldShowValue ? "-0.2em" : "0.3em",
                      fillOpacity1,
                      colour,
                  }),
                  ...(shouldShowValue
                      ? [
                          textComponent({
                              key: isoCode,
                              text: format(value),
                              fontSize"10px",
                              y: shouldShowIso ? "0.9em" : "1.0em",
                              fillOpacity0.7,
                              colour,
                          }),
                      ]
                      : []),
              ];
          };


          設(shè)置氣泡組件。


          //設(shè)置氣泡組件
          const bubbleComponent = ({
              name,
              id,
              value,
              r,
              x,
              y,
              fill,
              colour,
              duration = 1000,
          }) => {
              return {
                  append"g",
                  key: id,
                  transform: {
                      enter`translate(${x + 1},${y + 1})`,
                      exit`translate(${width / 2},${height / 2})`,
                  },
                  duration,
                  delayMath.random() * 300,
                  children: [
                      circleComponent({ key: id, r, fill, duration }),
                      ...labelComponent({
                          key: id,
                          countryName: name,
                          isoCode: id,
                          value,
                          r,
                          colour,
                          duration,
                      }),
                  ],
              };
          };


          劃分?jǐn)?shù)據(jù)的層次結(jié)構(gòu),生成氣泡圖的結(jié)構(gòu)。

          后續(xù)的 d.r、d.x、d.y 數(shù)據(jù)都是從中獲取的。

          //d3.pack - 創(chuàng)建一個(gè)新的圓形打包圖
          //d3.hierarchy - 從給定的層次結(jié)構(gòu)數(shù)據(jù)構(gòu)造一個(gè)根節(jié)點(diǎn)并為各個(gè)節(jié)點(diǎn)指定深度等屬性
          const pack = (data) =>
              d3
                  .pack()
                  .size([width - 2, height - 2])
                  .padding(2)(d3.hierarchy({ children: data }).sum((d) => d.value));


          //生成氣泡圖表
          const renderBubbleChart = (selection, data) => {
              const root = pack(data);
              const renderData = root.leaves().map((d) => {
                  return bubbleComponent({
                      id: d.data.id,
                      name: d.data.name,
                      value: d.data.value,
                      r: d.r,
                      x: d.x,
                      y: d.y,
                      fill: d.data.fill,
                      colour: d.data.colour,
                  });
              });
              return render(selection, renderData);
          };

          const renderBubbleChartContainer = (data) => {
              return renderBubbleChart("#bubble-chart", data);
          };

          最后便可以加入數(shù)據(jù),生成動(dòng)態(tài)的氣泡圖表。

          對(duì)數(shù)據(jù)進(jìn)行處理,進(jìn)行日期限定及排序,以及選取相關(guān)的數(shù)據(jù)類型。

          //定義新冠數(shù)據(jù)
          const covidData_result = this.covidData;
          //定義各地區(qū)數(shù)據(jù)
          const countries_result = this.countries;

          //選擇數(shù)據(jù)類型為所有確診病例數(shù)量
          const dataKey = "total_cases";
          //定義開始時(shí)間及結(jié)束時(shí)間
          const startDate = new Date('2020-01-12')
          const endDate = new Date('2020-06-02')
          //d3.map - 創(chuàng)建一個(gè)新的空的 map 映射
          const dates = d3
              .map(this.covidData, (d) => d.date)
              .keys()
              .map((date) => new Date(date))
              .filter((date) => date >= startDate && date <= endDate)
              .sort((a, b) => a - b);
          //各大洲全選
          const selectedContinents = ["AF""AS""EU""NA""OC""SA"];
          //最小數(shù)值
          const minimumPopulation = 0;
          //排序
          const order = "desc";

          //轉(zhuǎn)換日期格式為2020-01-01
          const getIsoDate = (date) => {
              const IsoDate = new Date(date);
              return IsoDate.toISOString().split("T")[0];
          };

          //獲取最終的數(shù)據(jù)
          function getDataBy({
              dataKey,
              date,
              selectedContinents,
              order,
              minimumPopulation,
          }
          {
              return (
                  covidData_result
                      .filter((d) => d)
                      .filter((d) => d.iso_code !== "OWID_WRL")
                      // Filter out countries with populations under 1 million
                      .filter((d) => d.population > parseInt(minimumPopulation))
                      .filter((d) => {
                          return d.date === getIsoDate(date);
                      })
                      .filter((d) => d[dataKey])
                      .filter((d) => {
                          const country = countries_result.find(
                              (c) => c.iso3 === d.iso_code
                          );
                          const continent = continents.find((c, i) => {
                              if (!country) {
                                  return false;
                              }

                              return c.id === country.continentCode;
                          });

                          if (!continent) {
                              return false;
                          }

                          return selectedContinents.includes(continent.id);
                      })
                      .map((d) => {
                          const country = countries_result.find(
                              (c) => c.iso3 === d.iso_code
                          );
                          const continent = continents.find(
                              (c) => c.id === country.continentCode
                          );

                          const name = country.shortName || country.name;

                          return {
                              name,
                              id: country.iso3,
                              value: d[dataKey],
                              fill: continent.fill,
                              colour: continent.colour || "white",
                          };
                      })
                      .filter((d) => d.value !== "0.0")
                      .sort(function (a, b{
                          const mod = order === "desc" ? -1 : 1;
                          return mod * (a.value - b.value);
                      })
              );
          }

          設(shè)置For循環(huán)延時(shí),完成動(dòng)態(tài)氣泡圖的實(shí)現(xiàn)。

          //延時(shí)執(zhí)行,閉包
          for (var i = 0; i < dates.length; i++) {
              (function (i{
                  setTimeout(function () {
                      const date = dates[i];
                      console.log(date);
                      const data = getDataBy({
                          dataKey,
                          date,
                          selectedContinents,
                          minimumPopulation,
                          order,
                      });
                      renderBubbleChartContainer(data);
                  }, 2000 * i);
              })(i);
          };


          運(yùn)行項(xiàng)目,打開瀏覽器,訪問http://localhost:8080/


          如此便完成了一個(gè)動(dòng)態(tài)的氣泡圖,這個(gè)案例用了疫情隨時(shí)間變化的數(shù)據(jù),這種圖表可以比較直觀地展現(xiàn)數(shù)據(jù)的變化趨勢(shì)。

          項(xiàng)目代碼及數(shù)據(jù):
          https://github.com/Tobby-star/bubble-chart

          將項(xiàng)目下載到本地,運(yùn)行下面兩行命令,即可運(yùn)行。

          npm install
          npm run serve


          如果文章對(duì)你有幫助,歡迎轉(zhuǎn)發(fā)/點(diǎn)贊/收藏~


          作者:作者小F
          來源:法納斯特


          _往期文章推薦_

          用Plotly畫出炫酷的數(shù)據(jù)可視化圖表




          如需了解付費(fèi)精品課程教學(xué)答疑服務(wù)
          請(qǐng)?jiān)?strong>Crossin的編程教室內(nèi)回復(fù): 666

          瀏覽 256
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕在线观看就是爽 | 免费看黄色一级片 | 国产一级婬乱A片免费 | 天堂网久久 | 成人特级毛片全部免费播放 |