Organizing data in a bar chart with D3.js through the d3.nest() function

I am new to d3.js and my JavaScript skills are at a basic level. Thank you for your assistance, it is greatly appreciated.

Total Clicks per Campaign

I have a CSV file with columns: "Campaign" and "Clicked". The "Clicked" column contains values: Clicked / No Click. I want to show the total number of clicks per campaign, excluding "No Click". I created a function to count the clicks and store the count in each data member. I used d.count to set the y domain. The y-axis and x-axis display correctly, but the values in the bars do not appear. I suspect that these two lines of code are incorrect:

.attr("y", f

unction(d) { return y(d.key); })
.attr("height"
, function(d) { return height - y(d.key); });

The console shows this error:

d3.v4.min

.js:2 Error: attribute height: Expected length, "NaN". d3.v4.min.j

s:2 Error: attribute height: Expected length, "NaN"

Since there are two values for the "Clicked" column: "Clicked" and "No Click", should I exclude "No Click" from my d3.nest() function?

How can I display the values of "Clicked" per Campaign? What am I overlooking?

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>barChart</title>
</head>
<style> /* set the CSS */

.bar { fill: steelblue; }

</style>
<body>

<!-- load the d3.js library -->   
<script src="d3.v4.js"></script>    
<script src="https://d3js.org/d3.v4.min.js"></script>
<!--<script src="d3.min.js"></script>-->

<script>

// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// set the ranges
var x = d3.scaleBand()
          .range([0, width])
          .padding(0.1);
var y = d3.scaleLinear()
          .range([height, 0]);

// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", 
          "translate(" + margin.left + "," + margin.top + ")");

// get the data 
    d3.csv("EmailMarketingCampaign_Data.csv")
    .row(function(d){ return {Campaign: (d.Campaign), Clicked: (d.Clicked)}; })
    .get(function(error,data){
    console.log(data[0]);

 // format all data from strings
  data.forEach(function(d) {
    d.data = +d.data;   
  });

// Array [ Object, Object ] Key: Clicked, Key: No Clicked
     var nested_data = d3.nest()
     .key(function(d) { return d.Clicked; })
    .rollup(function(values) {
     return values.length; 
     })
     .entries(data);        
     console.log(nested_data);

// count all clicked to set range for y axis

      var countObj = {};

        // count Clicked 
        data.forEach(function(d) {
        var Clicked = d.Clicked;
        if(countObj[Clicked] === undefined) {
        countObj[Clicked] = 0;
        } else {
        countObj[Clicked] = countObj[Clicked] + 1;
        }
        });
        // now store the count in each data member
        data.forEach(function(d) {
        var Clicked = d.Clicked;
        d.count = countObj[Clicked];
        });
        console.log(countObj);


  // Scale the range of the data in the domains
  x.domain(data.map(function(d) { return d.Campaign; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]);

  // append the rectangles for the bar chart
  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")

      .attr("class", "bar")
      .attr("x", function(d) { return x(d.Campaign); })
      .attr("width", x.bandwidth())

      .attr("y", function(d) { return y(d.key); })
      .attr("height", function(d) { return height - y(d.key); });



  // add the x Axis
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // add the y Axis
  svg.append("g")
      .call(d3.axisLeft(y));

});

</script>
</body>

</html>

EmailMarketingCampaign_Data.csv

...

screenshot

Answer №1

In order to calculate the scales and data binding for your visualization, it is recommended that you implement two-level grouping in your nested_data with a focus on the "Clicked" group.

var dataString = "Campaign,Click_Date,Start,End,Clicked,clickedFlag,Customer ID,weekDay,Age,Country,Demographic,Gender \nEXTORTION,30/12/2012,30/12/2012,29/01/2013,Clicked,1,10,Sun,30,UK,Adult,Male \nSALES,31/12/2012,30/12/2012,29/01/2013,Clicked,1,11,Mon,26,UK,Adult,Female..."; // Your data string here

// Consider setting graph dimensions and margins
var margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  },
  width = 300 - margin.left - margin.right,
  height = 200 - margin.top - margin.bottom;

// Define ranges
var x = d3.scaleBand()
  .range([0, width])
  .padding(0.1);
var y = d3.scaleLinear()
  .range([height, 0]);

// Append SVG element to body
var svg = d3.select("body").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

// Parse CSV data
var data = d3.csvParse(dataString);

// Update data format
data.forEach(function(d) {
  d.data = +d.data;
});

// Group data based on conditions
var nested_data = d3.nest()
  .key(function(d) {
    return d.Clicked;
  })
  .key(function(d) {
    return d.Campaign
  })
  .rollup(function(values) {
    return values.length;
  })
  .entries(data);

var clicked_data = nested_data.find(d => d.key == "Clicked").values

// Scale data domains
x.domain(clicked_data.map(function(d) {
  return d.key;
}));
y.domain([0, d3.max(clicked_data, function(d) {
  return d.value;
})]);

// Create bar chart
svg.selectAll(".bar")
  .data(clicked_data)
  .enter().append("rect")

  .attr("class", "bar")
  .attr("x", function(d) {
    return x(d.key);
  })
  .attr("width", x.bandwidth())

  .attr("y", function(d) {
    return y(d.value);
  })
  .attr("height", function(d) {
    return height - y(d.value);
  });
  
// Add X Axis
svg.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));

// Add Y Axis
svg.append("g")
  .call(d3.axisLeft(y));
<script src="https://d3js.org/d3.v4.min.js"></script>

Answer №2

After making adjustments based on your recommendations, my code now functions correctly. I want to use nested_data to present the clicked_data in a line using d3.line generator with the x-axis as "Click_Date" and y-axis as "Clicked" values, categorized by the 3 campaigns: Jan, Xmas, and Undefined. To achieve this, I am employing d3.line() and path to draw the line. However, I encountered an error in console.log: Error: attribute d: Expected number, "MNaN,0Z". When I used console.log, I observed that clicked_data is being interpreted as [Object]0: Objectlength: 1__proto__: Array(0)concat: function concat()constructor: function Array() I believe I need to focus on this segment of the code:

svg.append("path") .data([clicked_data]) .attr("class", "line") .attr("d", valueline);

// define the dimensions and margins for the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");

// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

// specify the line
var valueline = d3.line()
    .x(function(d) { return x(d.key); })
    .y(function(d) { return y(d.value); });
console.log(valueline);

// append the svg object to the body of the page
// adds a 'group' element to 'svg'
// positions the 'group' element at the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

// Retrieve the data
d3.csv("EmailMarketingCampaign_Data.csv", function(error, data) {
  if (error) throw error;

// format the data
  data.forEach(function(d) {
      d.Click_Date = parseTime(d.Click_Date);
      d.value = +d.value;
  });

    /* Two level grouping in nested_data: Clicked and Campaign */

var nested_data = d3.nest()
  .key(function(d) {
    return d.Clicked;
  })
  .key(function(d) {
    return d.Click_Date
  })
  .rollup(function(values) {
    return values.length;
  })
  .entries(data);

/* Get values from key "Clicked" and place them in clicked_date */

var clicked_data = nested_data.find(d => d.key == "Clicked").values

console.log(clicked_data);

  // Set the range for the data
  x.domain(d3.extent(clicked_data, function(d) { return d.key; }));
  y.domain([0, d3.max(clicked_data, function(d) { return d.value; })]);

  // Add the valueline path.
  svg.append("path")
      .data([clicked_data])
      .attr("class", "line")
      .attr("d", valueline);

  // Add the X Axis
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // Add the Y Axis
  svg.append("g")
      .call(d3.axisLeft(y));

});

var dataString = "Campaign,Click_Date,Start,End,Clicked,clickedFlag,Customer ID,weekDay,Age,Country,Demographic,Gender \nEXTORTION,30/12/2012,30/12/2012,29/01/2013,Clicked,1,10,Sun,30,UK,Adult,Male \nSALES,31/12/2012,30/12/2012,29/01/2013,Clicked,1,11,Mon,26,UK,Adult,Female \nSALES,01/01/2013,30/12/2012,29/01/2013,Clicked,1,12,Tue,59,UK,Adult,Male \nSALES,02/01/2013,30/12/2012,29/01/2013,Clicked,1,13,Wed,3,UK,Child,Male \nSALES,03/01/2013,30/12/2012,29/01/2013,Clicked,1,14,Thu,59,Germany,Adult,Female \nSALES,04/01/2013,30/12/2012,29/01/2013,No Click,0,15,Fri,39,UK,Adult,Male \nSALES,...

// Additional codes including graph dimensions, bar chart creation, and axis addition omitted for brevity

<script src="https://d3js.org/d3.v4.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

After removing an item from the array, React fails to display the updated render

As a newcomer, I am struggling with a particular issue. I have implemented a delete button for each item in a list. When the button is clicked, the object in the firstItems array is successfully deleted (as confirmed by logging the array to the console), b ...

Background of jQuery-UI Slider

Can the background color of a jQuery-UI Slider widget be set using JavaScript? This is the default setting: What I am trying to accomplish is the following: The green range should be determined based on historical data. I want to visually show the user ...

Is there a method to extract the y value for a specific x value that is not part of the current data set in a Chart.js graph

I have a set of data points like { x: 5, y: 10 }, { x: 10, y: 15 }, { x: 20, y: 25 }. With Chart.js, I can easily create a chart based on these points. Is there a method to retrieve the y value for a specific x value, such as 13, from the rendered chart? ...

Learn how to cycle through three different texts that appear in the same spot using smooth transitions

I am working with three different rows that contain the Typography component. My goal is to display the first row, followed by the second, then the third, and back to the first one in a continuous loop. All three rows should be shown in the same location, ...

Mask the input numbers to a specific format

I have a custom input component that I use to manage user inputs. export const MyCustomInput = (props) => { const { name, required, value, label, onChange, type, icon, } = props const Icon = () => (icon ? <div cl ...

Answer found: How to effectively filter data arrays in a React client application

I've been working on mapping the GraphQL data onto a React app and I'm facing an issue with making the filtration dynamic for user input. Currently, I am using .filter() to apply client-side filtration but struggling to figure out how to make it ...

When attempting to make a post using Prisma's ORM, users may encounter an error message indicating that the post function

Creating a basic prisma application using express and node to query a local mysql database. Encountering an error when calling await prisa.list.create(), details provided below. Here is the script.js code snippet from the HTML page: addItemForm.addEvent ...

I'm having trouble getting onClick to function properly in CodeIgniter

While attempting to utilize onClick in the PHP page as shown below: <a href="javascript:void(0);" onClick="deleteCourse('<?php echo $row->courseId;?>');" class="delete">Delete</a> And in the JavaScript page, the function is ...

Unusual behavior of .replace() function observed in Chrome browser

<div> <input type="text" class="allownumericwithdecimal"/>saadad </div> $(".allownumericwithdecimal").live("keypress keyup ", function (event) { $(this).val($(this).val().replace(/[^0-9\.]/g, '')); var text = ...

Passing a class as a parameter in Typescript functions

When working with Angular 2 testing utilities, I usually follow this process: fixture = TestBed.createComponent(EditableValueComponent); The EditableValueComponent is just a standard component class that I use. I am curious about the inner workings: st ...

AJAX is delivering a unique random hash instead of the expected text

I am in the process of developing a live notification script, and I have encountered an issue. Instead of receiving plain text from the external file, the script is returning a random hash... Here is the function responsible for fetching data from test.ph ...

Implementing child components in React using TypeScript and passing them as props

Is it possible to append content to a parent component in React by passing it through props? For example: function MyComponent(props: IMyProps) { return ( {<props.parent>}{myStuff}{</props.parent>} } Would it be feasible to use the new compone ...

Is there a shared instance for regular expressions created using expression literals?

In the book "Javascript: The Good Parts" by Crockford, there is a code snippet that highlights how RegExp objects created using regular expression literals share a single instance: function create_matcher() { return /a/gi; } var x = create_matcher(); ...

Express.js encountered a FetchError due to receiving an invalid JSON response body from https://api.twitter.com

I am currently working on a project that involves getting the tweet id as form input and using the Twitter API to retrieve data about that specific tweet. However, I have encountered an issue where the JSON data is not being returned properly. router.post( ...

Executing operations on checkboxes on a webpage without access to model files

I am facing an issue with accessing the models file due to encryption in the software. Currently, my checkboxes are implemented using Ajax within a PHP query. Since it is Ajax-based, I am unable to manipulate actions through the URL. My goal is to extract ...

Unlocking the power of accessing nested data in JSON files dynamically

Building a new feature that allows users to input a word, choose the language, and receive the definition along with an example using the API service. To retrieve the desired data at position 0 of "exclamation" in the "meaning" section. This ensures that ...

Issues with LocalStrategy not executing in passport authentication

I am currently facing an issue with authenticating using Passport and LocalStrategy. It seems like the strategy is not being called and when I log the user object in passport.authenticate, it returns "false". Below is my client-side code: logIn = () =& ...

Send an AJAX request to the server without waiting for a response using a JavaScript variable

My click counter is not sending variables to the server. I have tried finding examples on how to do this, but no matter what I attempt, the data is not being sent to the server. It seems like using AJAX would be the best option, but I must be doing someth ...

How can I link a Vue.js webpage with a WordPress site?

Looking to integrate a Vue.js 2 single page into an existing WordPress website? Perhaps you've already got an old WordPress site and now want to develop a new Vue tool or page that can be added seamlessly to the menu. How can this be achieved within W ...

Encountered an issue while installing the "sharp" module on MAC M1

When I run npm run dev (gatsby develop) on my MacBook Pro M1 chip, it exits with the error message: Error: Something went wrong installing the "sharp" module However, when I run npm run dev on a MacBook Pro with an Intel chip, everything works fine. I&ap ...