2018-09-14_D3.js

Data Visualization with D3

D3: SVG中的jQurey


1. Add Document Elements with D3

  • select()

    • arguments: name of target element
    • return: target node
    • sample code:
      const element = d3.select(tagName);
  • append()

    • Description:
      The append() method takes an argument for the element you want to add to the document. It appends an HTML node to a selected item, and returns a handle to that node.
      (append()方法接受一個你想要添加到文檔的元素作為參數。它將附加HTML節點到選中的項,并且返回該節點的句柄)
  • text()

    • Description:
      The text() method either sets the text of the selected node, or gets the current text. To set the value, you pass a string as an argument inside the parentheses of the method.
      (text()方法要么設置所選節點的文本,要么獲取當前文本。要設置值,您需要在方法的圓括號內傳遞一個字符串作為參數)
  • Sample Code:

      d3.select("ul")
          .append("li")
          .text("Very important item");
    
  • Note:
    D3 allows you to chain several methods together with periods to perform a number of actions in a row.(D3允許您將多個方法與句點鏈接起來,以在一行中執行多個操作。)


2. Select a Group of Elements with D3

  • Description:
    D3 also has the selectAll() method to select a group of elements. It returns an array of HTML nodes for all the items in the document that match the input string.

  • Sample Code:

      const anchors = d3.selectAll("a");
    
  • Note:
    Like the select() method, selectAll() supports method chaining, and you can use it with other methods.


3. Work with Data in D3(處理D3中的數據)

  • Description:
    • The D3 library focuses on a data-driven approach. When you have a set of data, you can apply D3 methods to display it on the page. Data comes in many formats, but this challenge uses a simple array of numbers.
      (D3庫側重于數據驅動方法。當你有一組數據時,您可以應用D3方法在頁面上顯示它。數據有多種格式,但這個挑戰使用的是簡單的數字數組。)
  1. The first step is to make D3 aware of the data. The data() method is used on a selection of DOM elements to attach the data to those elements. The data set is passed as an argument to the method.
    (第一步是讓D3知道數據。data()方法用于選擇DOM元素,以便將數據附加到這些元素。數據集作為參數傳遞給方法.)

  2. A common workflow pattern is to create a new element in the document for each piece of data in the set. D3 has the enter()method for this purpose.
    (一個常見的工作流模式是在文檔中為集合中的每個數據塊創建一個新元素。D3具有來達成這個目的的enter()方法)

  3. When enter() is combined with the data() method, it looks at the selected elements from the page and compares them to the number of data items in the set. If there are fewer elements than data items, it creates the missing elements.
    (當enter()data()方法結合時,它會查看頁面中選中的元素,并將它們與集合中的數據項數量進行比較。)

  • Sample Code:

      <body>
        <ul></ul>
        <script>
          const dataset = ["a", "b", "c"];
          d3.select("ul").selectAll("li")
                 .data(dataset)
                 .enter()
                 .append("li")
                 .text("New item");
         </script>
      </body>
    
  • Explain:
    It may seem confusing to select elements that don't exist yet. This code is telling D3 to first select the ul on the page. Next, select all list items, which returns an empty selection. Then the data() method reviews the dataset and runs the following code three times, once for each item in the array. The enter() method sees there are no li elements on the page, but it needs 3 (one for each piece of data in dataset). New li elements are appended to the ul and have the text "New item".
    (選擇還不存在的元素似乎有些混亂。

    這段代碼告訴D3首先在頁面上選擇ul。

    接下來,選擇所有列表項,這將返回一個空的選擇。

    然后data()方法檢查數據集,并對數組中的每個項運行以下代碼三次。

    enter()方法看到頁面上沒有li元素,但是它需要3個元素(數據集中的每一塊數據都需要一個元素)。

    新的li元素被添加到ul,并具有文本“New item”。)


4. Work with Dynamic Data in D3

  • Description: The D3 text() method can take a string or a callback function as an argument(.eg selection.text((d) => d);In the example above, the parameter d refers to a single entry in the dataset that a selection is bound to.)
  • Sample Code:
      <body>
       <script>
         const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
    
         d3.select("body").selectAll("h2")
               .data(dataset)
               .enter()
               .append("h2")
               .text((d) => d + " string");
       </script>
      </body>
    


5. Add inline Styling to Elements

  • Description:
    D3 lets you add inline CSS styles on dynamic elements with the style() method.
    The style() method takes a comma-separated key-value pair as an argument. Here's an example to set the selection's text color to blue:
    selection.style("color","blue");

  • Sample Code:

    <body>
    <script>
      const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
      d3.select("body").selectAll("h2")
        .data(dataset)
        .enter()
        .append("h2")
        .text((d) => (d + " USD"))
        .style("font-family", "verdana");
    </script>
    </body>
    


6. Change Styles Based on Data

  • Description:

    • D3 is about visualization and presentation of data. It's likely you'll want to change the styling of elements based on the data. You can use a callback function in the style() method to change the styling for different elements.

    • For example, you may want to color a data point blue if has a value less than 20, and red otherwise. You can use a callback function in the style() method and include the conditional logic. The callback function uses the d parameter to represent the data point:

        selection.style("color", (d) => {
          /* Logic that returns the color based on a condition */
        });
      
  • Note:
    The style() method is not limited to setting the color - it can be used with other CSS properties as well.

  • Sample Code:

    <body>
    <script>
      const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
      d3.select("body").selectAll("h2")
       .data(dataset)
       .enter()
       .append("h2")
       .text((d) => (d + " USD"))
       .style("color",(d) => d < 20 ? "red" : "green");  
    </script>
    </body>
    


7. Add Classes with D3

  • Description:

    • Using a lot of inline styles on HTML elements gets hard to manage, even for smaller apps. It's easier to add a class to elements and style that class one time using CSS rules. D3 has the attr() method to add any HTML attribute to an element, including a class name.
    • The attr() method works the same way that style() does. It takes comma-separated values, and can use a callback function. Here's an example to add a class of "container" to a selection:
      selection.attr("class", "container");
  • Sample Code:

    <style>
      .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
      }
    </style>
    <body>
      <script>
        const dataset = [12, 31, 22, 17, 25, 18,
          29, 14, 9];
      
        d3.select("body").selectAll("div")
              .data(dataset)
              .enter()
              .append("div")
              .attr("class", "bar");
      </script>
    </body>
    


8. Update the Height of an Element Dynamically

  • Description:
    The previous challenges covered how to display data from an array and how to add CSS classes. You can combine these lessons to create a simple bar chart. There are two steps to this:

    • Create a div for each data point in the array
    • Give each div a dynamic height, using a callback function in the style() method that sets height equal to the data value
    • Recall the format to set a style using a callback function:
      selection.style("cssProperty", (d) => d)
  • Sample Code:

    <style>
      .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
      }
    </style>
    <body>
      <script>
        const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
        d3.select("body").selectAll("div")
          .data(dataset)
          .enter()
          .append("div")
          .attr("class", "bar")
          .style("height", (d) => d + "px");
      </script>
    </body>
    


9. Change the Presentation of a Bar Chart(改變條形圖的顯示/practice)

  • Description:
    The last challenge created a bar chart, but there are a couple of formatting changes that could improve it:

    1. Add space between each bar to visually separate them, which is done by adding a margin to the CSS for the bar class

    2. Increase the height of the bars to better show the difference in values, which is done by multiplying the value by a number to scale the height

  • Demand:
    First, add a margin of 2px to the bar class in the style tag. Next, change the callback function in the style() method so it returns a value 10 times the original data value (plus the "px").

  • Sample Code:

    <style>
      .bar {
        width: 25px;
        height: 100px;
        /* Add your code below this line */
        margin: 2px;
        /* Add your code above this line */
        display: inline-block;
        background-color: blue;
      }
    </style>
    <body>
      <script>
        const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
        d3.select("body").selectAll("div")
          .data(dataset)
          .enter()
          .append("div")
          .attr("class", "bar")
          // Add your code below this line
          .style("height", (d) => (d * 10 + "px"))
          // Add your code above this line
      </script>
    </body>
    
  • Note:
    Multiplying each data point by the same constant only alters the scale. It's like zooming in, and it doesn't change the meaning of the underlying data.


10. Learn About SVG in D3

SVG stands for Scalable Vector Graphics.

  • Description:
    • Here "scalable" means that, if you zoom in or out on an object, it would not appear pixelated. It scales with the display system, whether it's on a small mobile screen or a large TV monitor.

    • SVG is used to make common geometric shapes. Since D3 maps data into a visual representation, it uses SVG to create the shapes for the visualization. SVG shapes for a web page must go within an HTML svg tag.

    • CSS can be scalable when styles use relative units (such as vh, vw, or percentages), but using SVG is more flexible to build data visualizations.

*Demand:
Add an svg node to the body using append(). Give it a width attribute set to the provided w constant and a height attribute set to the provided h constant using the attr() method for each. You'll see it in the output because there's a background-color of pink applied to it in the style tag.

  • Sample Code

    <style>
      svg {
        background-color: pink;
      }
    </style>
    <body>
      <script>
        const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
        const w = 500;
        const h = 100;
      
        const svg = d3.select("body")
                      // Add your code below this line
                       .append("svg")
                       .attr("width", w)
                       .attr("height", h);
                      // Add your code above this line
      </script>
    </body>
    
  • Note:
    Width and height attributes do not have units. This is the building block of scaling - the element will always have a 5:1 width to height ratio, no matter what the zoom level is.


11. Display Shapes with SVG

(在SVG元素中顯示一個矩形)

  • Description:

    • The last challenge created an svg element with a given width and height, which was visible because it had a background-color applied to it in the style tag. The code made space for the given width and height.
      在上一個挑戰是用給定的高度和寬度創建一個SVG元素,這是可見的,因為它在style標簽中應用了背景色。代碼為給定的寬度和高度留出空間。

    • The next step is to create a shape to put in the svg area. There are a number of supported shapes in SVG, such as rectangles and circles. They are used to display data. For example, a rectangle (<rect>) SVG shape could create a bar in a bar chart.
      下一步是創建一個要放入svg區域的形狀。SVG支持多種形狀,比如矩形和圓形。它們用于顯示數據。例如,一個矩形(<rect>)SVG形狀可以在條形圖中創建條形。

    • When you place a shape into the svg area, you can specify where it goes with x and y coordinates. The origin point of (0, 0) is in the upper-left corner. Positive values for x push the shape to the right, and positive values for y push the shape down from the origin point.
      當您將形狀放入svg區域時,您可以指定它在x和y坐標下的位置。(0,0)的原點在左上角。x的正值將形狀向右推,y的正值將形狀從原點向下推。

    • To place a shape in the middle of the 500 (width) x 100 (height) svg from last challenge, the x coordinate would be 250 and the y coordinate would be 50.
      要在上次挑戰的500(寬)x 100(高)svg中間放置一個形狀,x坐標是250,y坐標是50。

    • An SVG rect has four attributes. There are the x and y coordinates for where it is placed in the svg area. It also has a height and width to specify the size.
      SVG rect有四個屬性。它在svg區域中的位置有x和y坐標。它還具有指定大小的高度和寬度。

  • Demand:
    Add a rect shape to the svg using append(), and give it a width attribute of 25 and height attribute of 100. Also, give the rect x and y attributes each set to 0.

  • Sample Code:

    <body>
    <script>
      const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
      const w = 500;
      const h = 100;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h)
                    // Add your code below this line
      const react = d3.select("svg")
                      .append("rect")
                      .attr("width", 25  )
                      .attr("height", 100)
                      .attr("x", 0)
                      .attr("y", 0);
                    // Add your code above this line
    </script>
    </body>
    


12. Creat a Bar for Each Data Point in the Set

  • Description:

    • The last challenge added only one rectangle to the svg element to represent a bar. Here, you'll combine what you've learned so far about data(), enter(), and SVG shapes to create and append a rectangle for each data point in dataset.

    • A previous challenge showed the format for how to create and append a div for each item in dataset:

      d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
      
    • There are a few differences working with rect elements instead of divs. The rects must be appended to an svg element, not directly to the body. Also, you need to tell D3 where to place each rect within the svg area. The bar placement will be covered in the next challenge.

  • Demand:
    Use the data(), enter(), and append() methods to create and append a rect for each item in dataset. The bars should display all on top of each other, this will be fixed in the next challenge.

  • Sample Code:

    <body>
    <script>
      const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
      const w = 500;
      const h = 100;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("rect")
         // Add your code below this line
         .data(dataset)
         .enter()
         .append("rect")
         // Add your code above this line
         .attr("x", 0)
         .attr("y", 0)
         .attr("width", 25)
         .attr("height", 100);
    </script>
    </body>
    


13. Dynamically Set the Coordinates for Each Bar

  • Description:

    • The last challenge created and appended a rectangle to the svg element for each point in dataset to represent a bar. Unfortunately, they were all stacked on top of each other.

    • The placement of a rectangle is handled by the x and y attributes. They tell D3 where to start drawing the shape in the svg area. The last challenge set them each to 0, so every bar was placed in the upper-left corner.

    • For a bar chart, all of the bars should sit on the same vertical level, which means the y value stays the same (at 0) for all bars. The x value, however, needs to change as you add new bars. Remember that larger x values push items farther to the right. As you go through the array elements in dataset, the x value should increase.

    • The attr() method in D3 accepts a callback function to dynamically set that attribute. The callback function takes two arguments, one for the data point itself (usually d) and one for the index of the data point in the array. The second argument for the index is optional. Here's the format:

      selection.attr("property", (d, i) => {
        /* 
        * d is the data point value
        * i is the index of the data point in the array
        */
      })
      
    • It's important to note that you do NOT need to write a for loop or use forEach() to iterate over the items in the data set. Recall that the data() method parses the data set, and any method that's chained after data() is run once for each item in the data set.
      重要的是要注意,您不需要編寫一個for循環或使用forEach()的迭代的項目數據集。記得的data()方法解析數據集,和任何方法鏈接的data()運行一次后的每一項數據集.

  • Demand:
    Change the x attribute callback function so it returns the index times 30.

  • Sample Code:

    <body>
    <script>
      const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
      const w = 500;
      const h = 100;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("rect")
         .data(dataset)
         .enter()
         .append("rect")
         .attr("x", (d, i) => {
           // Add your code below this line
           return i * 30;
           // Add your code above this line
         })
         .attr("y", 0)
         .attr("width", 25)
         .attr("height", 100);
    </script>
    </body>
    
  • Note:
    Each bar has a width of 25, so increasing each x value by 30 adds some space between the bars. Any value greater than 25 would work in this example.


14. Dynamically Change the Height of Each Bar

*Description:

  • The height of each bar can be set to the value of the data point in the array, similar to how the x value was set dynamically.

    selection.attr("property", (d, i) => {
      /* 
      * d is the data point value
      * i is the index of the data point in the array
      */
    })
    
  • Demand:
    Change the callback function for the height attribute to return the data value times 3.

  • Sample Code:

    <body>
    <script>
     const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
     const w = 500;
     const h = 100;
     
     const svg = d3.select("body")
                   .append("svg")
                   .attr("width", w)
                   .attr("height", h);
     
     svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("x", (d, i) => i * 30)
        .attr("y", 0)
        .attr("width", 25)
        .attr("height", (d, i) => {
          // Add your code below this line
          return d * 3;
          
          
          // Add your code above this line
        });
    </script>
    </body>
    
    • Note: Remember that multiplying all data points by the same constant scales the data (like zooming in). It helps to see the differences between bar values in this example.
      (記住,將所有數據點乘以相同的常數可以縮放數據(比如縮放)。)


15. Invert(反轉) SVG Elements

  • Description:

    • You may have noticed the bar chart looked like it's upside-down, or inverted. This is because of how SVG uses (x, y) coordinates.

    • In SVG, the origin point for the coordinates is in the upper-left corner. An x coordinate of 0 places a shape on the left edge of the SVG area. A y coordinate of 0 places a shape on the top edge of the SVG area. Higher x values push the rectangle to the right. Higher y values push the rectangle down.

    • To make the bars right-side-up, you need to change the way the y coordinate is calculated. It needs to account for both the height of the bar and the total height of the SVG area.

    • The height of the SVG area is 100. If you have a data point of 0 in the set, you would want the bar to start at the bottom of the SVG area (not the top). To do this, the y coordinate needs a value of 100. If the data point value were 1, you would start with a y coordinate of 100 to set the bar at the bottom. Then you need to account for the height of the bar of 1, so the final y coordinate would be 99.

    • The y coordinate that is y = heightOfSVG - heightOfBar would place the bars right-side-up.

  • Demand:
    Change the callback function for the y attribute to set the bars right-side-up. Remember that the height of the bar is 3 times the data value d.

  • Sample Code:

    <body>
    <script>
     const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
     const w = 500;
     const h = 100;
     
     const svg = d3.select("body")
                   .append("svg")
                   .attr("width", w)
                   .attr("height", h);
     
     svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => {
          // Add your code below this line
          return h - d * 3;
          // Add your code above this line
        })
        .attr("width", 25)
        .attr("height", (d, i) => 3 * d);
    </script>
    </body>
    
  • Note:
    In general, the relationship is y = h - m * d, where m is the constant that scales the data points.


16. Change the Color of an SVG Element

selection.attr("fill", "red")

  • Description:

    • The bars are in the right position, but they are all the same black color. SVG has a way to change the color of the bars.

    • In SVG, a rect shape is colored with the fill attribute. It supports hex codes, color names, and rgb values, as well as more complex options like gradients and transparency.

  • Demand:
    Add an attr() method to set the "fill" of all the bars to the color "navy".

  • Sample Code

    <body>
    <script>
     const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
     const w = 500;
     const h = 100;
     
     const svg = d3.select("body")
                   .append("svg")
                   .attr("width", w)
                   .attr("height", h);
     
     svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => h - 3 * d)
        .attr("width", 25)
        .attr("height", (d, i) => 3 * d)
        // Add your code below this line
        .attr("fill", "navy");
        // Add your code above this line
    </script>
    </body>
    


17. Add Labels to D3 Elements

  • Description:

    • D3 lets you label a graph element, such as a bar, using the SVG text element.

    • Like the rect element, a text element needs to have x and y attributes, to place it on the SVG canvas. It also needs to access the data to display those values.
      與rect元素類似,文本元素需要有x和y屬性,才能將其放到SVG畫布上。它還需要訪問數據來顯示這些值。

    • D3 gives you a high level of control over how you label your bars.

  • Demand:
    The code in the editor already binds the data to each new text element. First, append text nodes to the svg. Next, add attributes for the x and y coordinates. They should be calculated the same way as the rect ones, except the y value for the text should make the label sit 3 units higher than the bar. Finally, use the D3 text() method to set the label equal to the data point value.

  • Sample Code:

    <body>
    <script>
     const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
     const w = 500;
     const h = 100;
     
     const svg = d3.select("body")
                   .append("svg")
                   .attr("width", w)
                   .attr("height", h);
     
     svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => h - 3 * d)
        .attr("width", 25)
        .attr("height", (d, i) => 3 * d)
        .attr("fill", "navy");
     
     svg.selectAll("text")
        .data(dataset)
        .enter()
        // Add your code below this line
        .append("text")
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => h - d * 3 - 3)
        .text((d) => d);
        // Add your code above this line
    </script>
    <body>
    
  • Note:
    For the label to sit higher than the bar, decide if the y value for the text should be 3 greater or 3 less than the y value for the bar.


18. Style D3 Labels

selection.attr("fill", "red")

  • Description:

    • D3 methods can add styles to the bar labels. The fill attribute sets the color of the text for a text node. The style() method sets CSS rules for other styles, such as "font-family" or "font-size".
  • Demand:
    Set the font-size of the text elements to 25px, and the color of the text to red.

  • Sample Code:

    <body>
    <script>
     const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
     const w = 500;
     const h = 100;
     
     const svg = d3.select("body")
                   .append("svg")
                   .attr("width", w)
                   .attr("height", h);
     
     svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => h - 3 * d)
        .attr("width", 25)
        .attr("height", (d, i) => d * 3)
        .attr("fill", "navy");
     
     svg.selectAll("text")
        .data(dataset)
        .enter()
        .append("text")
        .text((d) => d)
        .attr("x", (d, i) => i * 30)
        .attr("y", (d, i) => h - (3 * d) - 3)
        // Add your code below this line
        .attr("fill", "red")
        .style("font-size", "25px");
        // Add your code above this line
    </script>
    </body>
    


19. Add a Hover Effect to a D3 Element

  • Description:

    • It's possible to add effects that highlight a bar when the user hovers over it with the mouse. So far, the styling for the rectangles is applied with the built-in D3 and SVG methods, but you can use CSS as well.

    • You set the CSS class on the SVG elements with the attr() method. Then the :hover pseudo-class for your new class holds the style rules for any hover effects.

  • Demand:
    Use the attr() method to add a class of bar to all the rect elements. This changes the fill color of the bar to brown when you mouse over it.

  • Sample Code:

    <style>
     .bar:hover {
       fill: brown;
     }
    </style>
    <body>
     <script>
       const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
     
       const w = 500;
       const h = 100;
     
       const svg = d3.select("body")
                     .append("svg")
                     .attr("width", w)
                     .attr("height", h);
     
       svg.selectAll("rect")
          .data(dataset)
          .enter()
          .append("rect")
          .attr("x", (d, i) => i * 30)
          .attr("y", (d, i) => h - 3 * d)
          .attr("width", 25)
          .attr("height", (d, i) => 3 * d)
          .attr("fill", "navy")
          // Add your code below this line
          .attr("class", "bar");
          // Add your code above this line
     
       svg.selectAll("text")
          .data(dataset)
          .enter()
          .append("text")
          .text((d) => d)
          .attr("x", (d, i) => i * 30)
          .attr("y", (d, i) => h - (3 * d) - 3);
        
     </script>
    </body>
    


20. Add a Tooltip(工具提示) to a D3 Element

  • Description:

    • A tooltip shows more information about an item on a page when the user hovers over that item. There are several ways to add a tooltip to a visualization, this challenge uses the SVG title element.

    • title pairs with the text() method to dynamically add data to the bars.

  • Demand:
    Append a title element under each rect node. Then call the text() method with a callback function so the text displays the data value.

  • Sample Code:

    <style>
      .bar:hover {
        fill: brown;
      }
    </style>
    <body>
      <script>
        const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9];
      
        const w = 500;
        const h = 100;
      
        const svg = d3.select("body")
                      .append("svg")
                      .attr("width", w)
                      .attr("height", h);
      
        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d)
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy")
           .attr("class", "bar")
           // Add your code below this line
           .append("title")
           .text((d) => d)
           // Add your code above this line
      
        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   
      
      </script>
    </body>
    


21. Create a Scatterplot with SVG Circles

  • Description:
    • A scatter plot is another type of visualization. It usually uses circles to map data points, which have two values each. These values tie to the x and y axes, and are used to position the circle in the visualization.

    • SVG has a circle tag to create the circle shape. It works a lot like the rect elements you used for the bar chart.

  • Demand:
    Use the data(), enter(), and append() methods to bind dataset to new circle elements that are appended to the SVG canvas.

  • Sample Code:

    <body>
    <script>
      const dataset = [
                    [ 34,    78 ],
                    [ 109,   280 ],
                    [ 310,   120 ],
                    [ 79,    411 ],
                    [ 420,   220 ],
                    [ 233,   145 ],
                    [ 333,   96 ],
                    [ 222,   333 ],
                    [ 78,    320 ],
                    [ 21,    123 ]
                  ];
      
      
      const w = 500;
      const h = 500;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("circle")
         // Add your code below this line
         .data(dataset)
         .enter()
         .append("circle")       
         // Add your code above this line
    
    </script>
    </body>
    


22. Add Attributes to the Circle Elements

  • Description:
    • The last challenge created the circle elements for each point in the dataset, and appended them to the SVG canvas. But D3 needs more information about the position and size of each circle to display them correctly.

    • A circle in SVG has three main attributes. ①②The cx and cy attributes are the coordinates. They tell D3 where to position the center of the shape on the SVG canvas. ③The radius (r attribute) gives the size of the circle.

    • Just like the rect y coordinate, the cy attribute for a circle is measured from the top of the SVG canvas, not from the bottom.

    • All three attributes can use a callback function to set their values dynamically. Remember that all methods chained after data(dataset) run once per item in dataset. The d parameter in the callback function refers to the current item in dataset, which is an array for each point. You use bracket notation, like d[0], to access the values in that array.

*Demand:
Add cx, cy, and r attributes to the circle elements. The cx value should be the first number in the array for each item in dataset. The cy value should be based off the second number in the array, but make sure to show the chart right-side-up and not inverted. The r value should be 5 for all circles.

  • Sample Code:

    <body>
    <script>
      const dataset = [
                    [ 34,    78 ],
                    [ 109,   280 ],
                    [ 310,   120 ],
                    [ 79,    411 ],
                    [ 420,   220 ],
                    [ 233,   145 ],
                    [ 333,   96 ],
                    [ 222,   333 ],
                    [ 78,    320 ],
                    [ 21,    123 ]
                  ];
      
      
      const w = 500;
      const h = 500;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("circle")
         .data(dataset)
         .enter()
         .append("circle")
         // Add your code below this line
         .attr("cx", (d, i) => d[0])
         .attr("cy", (d, i) => h - d[1])
         .attr("r", 5)
         // Add your code above this line
    
    </script>
    </body>
    


23. Add Labels to Scatter Plot Circles

  • Description:

    • You can add text to create labels for the points in a scatter plot.

    • The goal is to display the comma-separated values for the first (x) and second (y) fields of each item in dataset.

    • The text nodes need x and y attributes to position it on the SVG canvas. In this challenge, the y value (which determines height) can use the same value that the circle uses for its cy attribute. The x value can be slightly larger than the cx value of the circle, so the label is visible. This will push the label to the right of the plotted point.

  • Demand:
    Label each point on the scatter plot using the text elements. The text of the label should be the two values separated by a comma and a space. For example, the label for the first point is "34, 78". Set the x attribute so it's 5 units more than the value you used for the cx attribute on the circle. Set the y attribute the same way that's used for the cy value on the circle.

  • Sample Code:

    <body>
    <script>
      const dataset = [
                    [ 34,    78 ],
                    [ 109,   280 ],
                    [ 310,   120 ],
                    [ 79,    411 ],
                    [ 420,   220 ],
                    [ 233,   145 ],
                    [ 333,   96 ],
                    [ 222,   333 ],
                    [ 78,    320 ],
                    [ 21,    123 ]
                  ];
      
      
      const w = 500;
      const h = 500;
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("circle")
         .data(dataset)
         .enter()
         .append("circle")
         .attr("cx", (d, i) => d[0])
         .attr("cy", (d, i) => h - d[1])
         .attr("r", 5);
      
      svg.selectAll("text")
         .data(dataset)
         .enter()
         .append("text")
         // Add your code below this line
         .attr("x", (d, i) => d[0] + 5)
         .attr("y", (d, i) => h - d[1])
         .text((d, i) => {
           return d[0] + ", " + d[1];
         });
         // Add your code above this line
    </script>
    </body>
    


24. Create a Linear Scale with D3

  • Description:

    • The bar and scatter plot charts both plotted data directly onto the SVG canvas. However, if the height of a bar or one of the data points were larger than the SVG height or width values, it would go outside the SVG area.

    • In D3, there are scales to help plot data. Scales are functions that tell the program how to map a set of raw data points onto the pixels of the SVG canvas.

    • For example, say you have a 100x500-sized SVG canvas and you want to plot Gross Domestic Product (GDP) for a number of countries. The set of numbers would be in the billion or trillion-dollar range. You provide D3 a type of scale to tell it how to place the large GDP values into that 100x500-sized area.

    • It's unlikely you would plot raw data as-is. Before plotting it, you set the scale for your entire data set, so that the x and y values fit your canvas width and height.

    • D3 has several scale types. For a linear scale (usually used with quantitative data), there is the D3 method scaleLinear():
      const scale = d3.scaleLinear()

    • By default, a scale uses the identity relationship. The value of the input is the same as the value of the output. A separate challenge covers how to change this.
      默認情況下,scale使用標識關系。輸入的值與輸出的值相同。另一個挑戰是如何改變這種狀況。

  • Demand:
    Change the scale variable to create a linear scale. Then set the output variable to the scale called with an input argument of 50.

  • Sample Code:

    <body>
    <script>
      // Add your code below this line
      
      const scale = d3.scaleLinear(); // Create the scale here
      const output = scale(50); // Call the scale with an argument here
      
      // Add your code above this line
      
      d3.select("body")
        .append("h2")
        .text(output);
    
    </script>
    </body>
    


25. Set a Domain and a Range on a Scale

  • Description:

    • By default, scales use the identity relationship - the input value maps to the output value. But scales can be much more flexible and interesting.

    • Say a data set has values ranging from 50 to 480. This is the input information for a scale, and is also known as the domain.
      假設數據集的值在50到480之間。這是一個尺度的輸入信息,也稱為域

    • You want to map those points along the x axis on the SVG canvas, between 10 units and 500 units. This is the output information, which is also known as the range.
      你希望在SVG畫布上沿x軸映射這些點,在10個單位到500個單位之間。這是輸出信息,也稱為范圍

    • The domain() and range() methods set these values for the scale. Both methods take an array of at least two elements as an argument. Here's an example:

      // Set a domain
      // The domain covers the set of input values
      scale.domain([50, 480]);
      // Set a range
      // The range covers the set of output values
      scale.range([10, 500]);
      scale(50) // Returns 10
      scale(480) // Returns 500
      scale(325) // Returns 323.37    calc: (325 - 50) / (430 / 490)  + 10
      scale(750) // Returns 807.67
      d3.scaleLinear()
      
    • Notice that the scale uses the linear relationship between the domain and range values to figure out what the output should be for a given number. The minimum value in the domain (50) maps to the minimum value (10) in the range.

  • Demand:
    Create a scale and set its domain to [250, 500] and range to [10, 150].

  • Sample Code:

    <body>
    <script>
      // Add your code below this line
      const scale = d3.scaleLinear();
      scale.domain([250, 500]);
      scale.range([10, 500]);
      // Add your code above this line
      const output = scale(50);
      d3.select("body")
        .append("h2")
        .text(output);
    </script>
    </body>
    
  • Note:
    You can chain the domain() and range() methods onto the scale variable.


26. Use the d3.max and d3.min Functions to Find Minimum and Maximum Values in a Dataset

  • Description:
    • The D3 methods domain() and range() set that information for your scale based on the data. There are a couple methods to make that easier.

    • Often when you set the domain, you'll want to use the minimum and maximum values within the data set. Trying to find these values manually, especially in a large data set, may cause errors.

    • D3 has two methods - min() and max() to return this information. Here's an example:

      const exampleData = [34, 234, 73, 90, 6, 52];
      d3.min(exampleData) // Returns 6
      d3.max(exampleData) // Returns 234
      
    • A dataset may have nested arrays, like the [x, y] coordinate pairs that were in the scatter plot example. In that case, you need to tell D3 how to calculate the maximum and minimum.

    • Fortunately, both the min() and max() methods take a callback function.

    • In this example, the callback function's argument d is for the current inner array. The callback needs to return the element from the inner array (the x or y value) over which you want to compute the maximum or minimum. Here's an example for how to find the min and max values with an array of arrays:

      const locationData = [[1, 7],[6, 3],[8, 3]];
      // Returns the smallest number out of the first elements
      const minX = d3.min(locationData, (d) => d[0]);
      // minX compared 1, 6, and 8 and is set to 1
      

*Demand:
The positionData variable holds a 3-dimensional (3D) array. Use a D3 method to find the maximum value of the z coordinate (the third value) from the arrays and save it in the output variable.

  • Sample Code:

    <body>
    <script>
      const positionData = [[1, 7, -4],[6, 3, 8],[2, 8, 3]]
      // Add your code below this line
      
      const output = d3.max(positionData, (d) => d[2]); // Change this line
      
      // Add your code above this line
      
      d3.select("body")
        .append("h2")
        .text(output)
    </script>
    </body>
    
  • Note:
    Fun fact - D3 can plot 3D arrays.


27. Use Dynamic Scales

  • Description:

    • The D3 min() and max() methods are useful to help set the scale.

    • Given a complex data set, one priority is to set the scale so the visualization fits the SVG container's width and height. You want all the data plotted inside the SVG canvas so it's visible on the web page.

    • The example below sets the x-axis scale for scatter plot data. The domain() method passes information to the scale about the raw data values for the plot. The range() method gives it information about the actual space on the web page for the visualization.

    • In the example, the domain goes from 0 to the maximum in the set. It uses the max() method with a callback function based on the x values in the arrays. The range uses the SVG canvas' width (w), but it includes some padding, too. This puts space between the scatter plot dots and the edge of the SVG canvas.

      const dataset = [
        [ 34, 78 ],
        [ 109, 280 ],
        [ 310, 120 ],
        [ 79, 411 ],
        [ 420, 220 ],
        [ 233, 145 ],
        [ 333, 96 ],
        [ 222, 333 ],
        [ 78, 320 ],
        [ 21, 123 ]
      ];
      const w = 500;
      const h = 500;
      
      // Padding between the SVG canvas boundary and the plot
      const padding = 30;
      const xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, (d) => d[0])])
        .range([padding, w - padding]);
      
    • The padding may be confusing at first. Picture the x-axis as a horizontal line from 0 to 500 (the width value for the SVG canvas). Including the padding in the range() method forces the plot to start at 30 along that line (instead of 0), and end at 470 (instead of 500).

  • Demand:
    Use the yScale variable to create a linear y-axis scale. The domain should start at zero and go to the maximum y value in the set. The range should use the SVG height (h) and include padding.

  • Sample Code:

    <body>
    <script>
      const dataset = [
                    [ 34,    78 ],
                    [ 109,   280 ],
                    [ 310,   120 ],
                    [ 79,    411 ],
                    [ 420,   220 ],
                    [ 233,   145 ],
                    [ 333,   96 ],
                    [ 222,   333 ],
                    [ 78,    320 ],
                    [ 21,    123 ]
                  ];
      
      const w = 500;
      const h = 500;
      
      // Padding between the SVG canvas boundary and the plot
      const padding = 30;
      
      // Create an x and y scale
      
      const xScale = d3.scaleLinear()
                      .domain([0, d3.max(dataset, (d) => d[0])])
                      .range([padding, w - padding]);
      
      // Add your code below this line
      
      const yScale = d3.scaleLinear()
                       .domain([0, d3.max(dataset, (d) => d[1])])
                       .range([h - padding, padding]);
      // Add your code above this line
      
      const output = yScale(411); // Returns 30
      d3.select("body")
        .append("h2")
        .text(output)
    </script>
    </body>
    
  • Note:
    Remember to keep the plot right-side-up. When you set the range for the y coordinates, the higher value (height minus padding) is the first argument, and the lower value is the second argument.


28. Use a Pre-Defined Scale to Place Elements

  • Description:

    • With the scales set up, it's time to map the scatter plot again. The scales are like processing functions that turn the x and y raw data into values that fit and render correctly on the SVG canvas. They keep the data within the screen's plotting area.

    • You set the coordinate attribute values for an SVG shape with the scaling function. This includes x and y attributes for rect or text elements, or cx and cy for circles. Here's an example:

      shape
        .attr("x", (d) => xScale(d[0]))
      
    • Scales set shape coordinate attributes to place the data points onto the SVG canvas. You don't need to apply scales when you display the actual data value, for example, in the text() method for a tooltip or label.

  • Demand:

    • Use xScale and yScale to position both the circle and text shapes onto the SVG canvas. For the circles, apply the scales to set the cx and cy attributes. Give them a radius of 5 units, too.

    • For the text elements, apply the scales to set the x and y attributes. The labels should be offset to the right of the dots. To do this, add 10 units to the x data value before passing it to the xScale.

  • Sample Code:

    <body>
      <script>
        const dataset = [
                      [ 34,     78 ],
                      [ 109,   280 ],
                      [ 310,   120 ],
                      [ 79,   411 ],
                      [ 420,   220 ],
                      [ 233,   145 ],
                      [ 333,   96 ],
                      [ 222,    333 ],
                      [ 78,    320 ],
                      [ 21,   123 ]
                    ];
      
        const w = 500;
        const h = 500;
        const padding = 60;
      
        const xScale = d3.scaleLinear()
                         .domain([0, d3.max(dataset, (d) => d[0])])
                         .range([padding, w - padding]);
      
        const yScale = d3.scaleLinear()
                         .domain([0, d3.max(dataset, (d) => d[1])])
                         .range([h - padding, padding]);
      
        const svg = d3.select("body")
                      .append("svg")
                      .attr("width", w)
                      .attr("height", h);
      
        svg.selectAll("circle")
           .data(dataset)
           .enter()
           .append("circle")
    
           // Add your code below this line
           .attr("r", 5)
           .attr("cx", (d) => xScale(d[0]))
           .attr("cy", (d) => yScale(d[1]))
           // Add your code above this line
    
        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) =>  (d[0] + ", "+ d[1]))
    
           // Add your code below this line
           .attr("x", (d) => xScale(d[0] + 10))
           .attr("y", (d) => yScale(d[1]))
           // Add your code above this line
    
      </script>
    </body>
    


29. Add Axis to a Visualization

  • Description:

    • Another way to improve the scatter plot is to add an x-axis and a y-axis.

    • ①D3 has two methods axisLeft() and axisBottom() to render the y and x axes, respectively. (Axes is the plural form of axis). Here's an example to create the x-axis based on the xScale in the previous challenges:

      const xAxis = d3.axisBottom(xScale);

    • ②The next step is to render the axis on the SVG canvas. To do so, you can use a general SVG component, the g element. The g stands for group.

    • Unlike rect, circle, and text, an axis is just a straight line when it's rendered. Because it is a simple shape, using g works.

    • ③The last step is to apply a transform attribute to position the axis on the SVG canvas in the right place. Otherwise, the line would render along the border of SVG canvas and wouldn't be visible.

    • SVG supports different types of transforms, but positioning an axis needs translate. When it's applied to the g element, it moves the whole group over and down by the given amounts. Here's an example:

      const xAxis = d3.axisBottom(xScale);
      
      svg.append("g")
         .attr("transform", "translate(0, " + (h - padding) + ")")
         .call(xAxis);
      
    • The above code places the x-axis at the bottom of the SVG canvas. Then it's passed as an argument to the call() method.

    • The y-axis works is the same way, except the translate argument is in the form (x, 0). Because translate is a string in the attr() method above, you can use concatenation to include variable values for its arguments.

  • Demand:
    The scatter plot now has an x-axis. Create a y-axis in a variable named yAxis using the axisLeft() method. Then render the axis using a g element. Make sure to use a transform attribute to translate the axis by the amount of padding units right, and 0 units down. Remember to call() the axis.

  • Sample Code:

    <body>
    <script>
      const dataset = [
                    [ 34,     78 ],
                    [ 109,   280 ],
                    [ 310,   120 ],
                    [ 79,   411 ],
                    [ 420,   220 ],
                    [ 233,   145 ],
                    [ 333,   96 ],
                    [ 222,    333 ],
                    [ 78,    320 ],
                    [ 21,   123 ]
                  ];
      
      const w = 500;
      const h = 500;
      const padding = 60;
      
      const xScale = d3.scaleLinear()
                       .domain([0, d3.max(dataset, (d) => d[0])])
                       .range([padding, w - padding]);
      
      const yScale = d3.scaleLinear()
                       .domain([0, d3.max(dataset, (d) => d[1])])
                       .range([h - padding, padding]);
      
      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
      
      svg.selectAll("circle")
         .data(dataset)
         .enter()
         .append("circle")
         .attr("cx", (d) => xScale(d[0]))
         .attr("cy",(d) => yScale(d[1]))
         .attr("r", (d) => 5);
      
      svg.selectAll("text")
         .data(dataset)
         .enter()
         .append("text")
         .text((d) =>  (d[0] + "," + d[1]))
         .attr("x", (d) => xScale(d[0] + 10))
         .attr("y", (d) => yScale(d[1]))
      
      const xAxis = d3.axisBottom(xScale);
      // Add your code below this line
      const yAxis = d3.axisLeft(yScale);
      // Add your code above this line
      
      svg.append("g")
         .attr("transform", "translate(0," + (h - padding) + ")")
         .call(xAxis);
      
      // Add your code below this line
      svg.append("g")
          .attr("transform", "translate(0" + padding + ")")
          .call(yAxis)
      
      
      // Add your code above this line
    
    </script>
    </body>
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377