Search code examples
javascriptd3.jsplotscaleordinal

categorical data with D3 scale


I am trying to plot the data with d3. My data looks like this:

dataset = [
["HBO", 23, "tv show"],
["HBO", 45, "movie"],
["Hulu", 40, "tv show"],
["Hulu", 56, "movie"]
]

I am using scaleLinear() for numerical data (d[1]) and scaleBand() for categorical data. Here is my code:

const dataset = [
  ["HBO", 23, "tv show"],
  ["HBO", 45, "movie"],
  ["Hulu", 40, "tv show"],
  ["Hulu", 56, "movie"]
];

const width = 600, height = 400;
const padding = 20, marginTop = 50;

// Min and max for y axis for Revenue values
const maxRevenue = d3.max(dataset, (d) => d[1]);
const minRevenue = d3.min(dataset, (d) => d[1]);

const yScale = d3
  .scaleLinear()
  .domain([minRevenue, maxRevenue])
  .range([height - padding, marginTop]);

const xScale = d3
  .scaleBand()
  .domain([dataset, (d) => d[2]])
  .range([0, 100]);

const rScale = d3
  .scaleBand()
  .domain([dataset, (d) => d[2]])
  .range([3, 3]);

// positioning the whole chart
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);

const svg = d3
  .select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("class", "chart")
  .attr("id", "chart");

svg
  .selectAll("circle")
  .data(dataset)
  .enter()
  .append("circle")
  .attr("cx", (d) => xScale(d[2]))
  .attr("cy", (d) => yScale(d[1]))
  .attr("r", (d) => rScale(d[2]))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

The expected output is the screenshot below. My code doesn't display the circles... enter image description here


Solution

  • For some reason, you used [dataset, (d) => d[2]], but that doesn't do anything. Well, it creates an array of a dataset and a function, but that's not what you wanted.

    To actually create the array that you want (["tv show", "movie", "tv show", "movie"]), you need to call dataset.map((d) => d[2]) yourself:

    const dataset = [
      ["HBO", 23, "tv show"],
      ["HBO", 45, "movie"],
      ["Hulu", 40, "tv show"],
      ["Hulu", 56, "movie"]
    ];
    
    const width = 600, height = 400;
    const padding = 20, marginTop = 50;
    
    // Min and max for y axis for Revenue values
    const maxRevenue = d3.max(dataset, (d) => d[1]);
    const minRevenue = d3.min(dataset, (d) => d[1]);
    
    const yScale = d3
      .scaleLinear()
      .domain([minRevenue, maxRevenue])
      .range([height - padding, marginTop]);
    
    const xScale = d3
      .scaleBand()
      // You need to actually call `Array.prototype.map` yourself
      .domain(dataset.map((d) => d[2]))
      .range([0, 100]);
    
    const rScale = d3
      .scaleBand()
      .domain(dataset.map((d) => d[2]))
      .range([3, 3]);
    
    // positioning the whole chart
    const xAxis = d3.axisBottom(xScale);
    const yAxis = d3.axisLeft(yScale);
    
    const svg = d3
      .select("body")
      .append("svg")
      .attr("width", width)
      .attr("height", height)
      .attr("class", "chart")
      .attr("id", "chart");
    
    svg
      .selectAll("circle")
      .data(dataset)
      .enter()
      .append("circle")
      .attr("cx", (d) => xScale(d[2]))
      .attr("cy", (d) => yScale(d[1]))
      .attr("r", (d) => rScale(d[2]))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>