Search code examples
javascriptchartsamcharts4

Stacked Area Amchart passing dynamic data


**The reference - https://www.amcharts.com/demos/stacked-area/ It only plots one single user on y axis whereas I want all users data in stacked line area.

I guess I need to create a function on data series, but really don't know how to do that. I want X axis to be Date , Y axis to be value scale showing multi-line users [A,B,C,D] **

< script >
  var df = [{
    "User": "A",
    "Date": 1570492800000,
    "value_act": 3.4
  }, {
    "User": "B",
    "Date": 1570492800000,
    "value_act": 1.6
  }, {
    "User": "C",
    "Date": 1570492800000,
    "value_act": 4.7
  }, {
    "User": "D",
    "Date": 1570492800000,
    "value_act": 0.0
  }, {
    "User": "A",
    "Date": 1570579200000,
    "value_act": 3.4
  }, {
    "User": "B",
    "Date": 1570579200000,
    "value_act": 1.6
  }, {
    "User": "C",
    "Date": 1570579200000,
    "value_act": 4.7
  }, {
    "User": "D",
    "Date": 1570579200000,
    "value_act": 0.0
  }, {
    "User": "A",
    "Date": 1570838400000,
    "value_act": 3.4
  }, {
    "User": "B",
    "Date": 1570838400000,
    "value_act": 1.6
  }, {
    "User": "C",
    "Date": 1570838400000,
    "value_act": 4.7
  }, {
    "User": "D",
    "Date": 1570838400000,
    "value_act": 0.0
  }]


console.log(df);

am4core.ready(function() {

  // Themes begin
  am4core.useTheme(am4themes_animated);
  // Themes end

  var chart = am4core.create("lines1", am4charts.XYChart);

  chart.data = df;

  chart.dateFormatter.inputDateFormat = "yyyy";
  var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
  dateAxis.renderer.minGridDistance = 60;
  dateAxis.startLocation = 0.5;
  dateAxis.endLocation = 0.5;
  dateAxis.baseInterval = {
    timeUnit: "Date",
    count: 1
  }

  var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  valueAxis.tooltip.disabled = true;


  var series = chart.series.push(new am4charts.LineSeries());
  series.dataFields.dateX = "Date";
  series.name = "User";
  series.dataFields.valueY = "value_act";
  series.tooltipText = "[#000]{valueY.value}[/]";
  series.tooltip.background.fill = am4core.color("#FFF");
  series.tooltip.getStrokeFromObject = true;
  series.tooltip.background.strokeWidth = 3;
  series.tooltip.getFillFromObject = false;
  series.fillOpacity = 0.6;
  series.strokeWidth = 2;
  series.stacked = true;

  chart.cursor = new am4charts.XYCursor();
  chart.cursor.xAxis = dateAxis;
  chart.scrollbarX = new am4core.Scrollbar();

  // Add a legend
  chart.legend = new am4charts.Legend();
  chart.legend.position = "top";

});
// end am4core.ready()
<
/script>
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="lines1"></div>


Solution

  • to have multiple lines, need to add multiple series to the chart

    each series needs a specific key in the data

    so, instead of all having a key for "User",
    we need 4 separate keys --> "UserA", "UserB", "UserC", "UserD"

    var df = [{
      "UserA": "A",
      "Date": 1570492800000,
      "value_act": 3.4
    }, {
      "UserB": "B",
      "Date": 1570492800000,
      "value_act": 1.6
    }, {
      "UserC": "C",
      "Date": 1570492800000,
      "value_act": 4.7
    }, {
      "UserD": "D",
      "Date": 1570492800000,
      "value_act": 0.0
    }, {
    

    see following working snippet...

    am4core.ready(function() {
    
    // Themes begin
    am4core.useTheme(am4themes_animated);
    // Themes end
    
      var df = [{
        "UserA": "A",
        "Date": 1570492800000,
        "value_act": 3.4
      }, {
        "UserB": "B",
        "Date": 1570492800000,
        "value_act": 1.6
      }, {
        "UserC": "C",
        "Date": 1570492800000,
        "value_act": 4.7
      }, {
        "UserD": "D",
        "Date": 1570492800000,
        "value_act": 0.0
      }, {
        "UserA": "A",
        "Date": 1570579200000,
        "value_act": 3.4
      }, {
        "UserB": "B",
        "Date": 1570579200000,
        "value_act": 1.6
      }, {
        "UserC": "C",
        "Date": 1570579200000,
        "value_act": 4.7
      }, {
        "UserD": "D",
        "Date": 1570579200000,
        "value_act": 0.0
      }, {
        "UserA": "A",
        "Date": 1570838400000,
        "value_act": 3.4
      }, {
        "UserB": "B",
        "Date": 1570838400000,
        "value_act": 1.6
      }, {
        "UserC": "C",
        "Date": 1570838400000,
        "value_act": 4.7
      }, {
        "UserD": "D",
        "Date": 1570838400000,
        "value_act": 0.0
      }]
    
    am4core.ready(function() {
    
      // Themes begin
      am4core.useTheme(am4themes_animated);
      // Themes end
    
      var chart = am4core.create("lines1", am4charts.XYChart);
    
      chart.data = df;
    
      chart.dateFormatter.inputDateFormat = "yyyy";
      var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
      dateAxis.renderer.minGridDistance = 60;
      dateAxis.startLocation = 0.5;
      dateAxis.endLocation = 0.5;
      dateAxis.baseInterval = {
        timeUnit: "Date",
        count: 1
      }
    
      var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
      valueAxis.tooltip.disabled = true;
    
    
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.dateX = "Date";
      series.name = "UserA";
      series.dataFields.valueY = "value_act";
      series.tooltipText = "[#000]{valueY.value}[/]";
      series.tooltip.background.fill = am4core.color("#FFF");
      series.tooltip.getStrokeFromObject = true;
      series.tooltip.background.strokeWidth = 3;
      series.tooltip.getFillFromObject = false;
      series.fillOpacity = 0.6;
      series.strokeWidth = 2;
      series.stacked = true;
    
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.dateX = "Date";
      series.name = "UserB";
      series.dataFields.valueY = "value_act";
      series.tooltipText = "[#000]{valueY.value}[/]";
      series.tooltip.background.fill = am4core.color("#FFF");
      series.tooltip.getStrokeFromObject = true;
      series.tooltip.background.strokeWidth = 3;
      series.tooltip.getFillFromObject = false;
      series.fillOpacity = 0.6;
      series.strokeWidth = 2;
      series.stacked = true;
    
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.dateX = "Date";
      series.name = "UserC";
      series.dataFields.valueY = "value_act";
      series.tooltipText = "[#000]{valueY.value}[/]";
      series.tooltip.background.fill = am4core.color("#FFF");
      series.tooltip.getStrokeFromObject = true;
      series.tooltip.background.strokeWidth = 3;
      series.tooltip.getFillFromObject = false;
      series.fillOpacity = 0.6;
      series.strokeWidth = 2;
      series.stacked = true;
    
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.dateX = "Date";
      series.name = "UserD";
      series.dataFields.valueY = "value_act";
      series.tooltipText = "[#000]{valueY.value}[/]";
      series.tooltip.background.fill = am4core.color("#FFF");
      series.tooltip.getStrokeFromObject = true;
      series.tooltip.background.strokeWidth = 3;
      series.tooltip.getFillFromObject = false;
      series.fillOpacity = 0.6;
      series.strokeWidth = 2;
      series.stacked = true;
    
      chart.cursor = new am4charts.XYCursor();
      chart.cursor.xAxis = dateAxis;
      chart.scrollbarX = new am4core.Scrollbar();
    
      // Add a legend
      chart.legend = new am4charts.Legend();
      chart.legend.position = "top";
    
    });
    
    });
    #lines1 {
      width: 100%;
      height: 500px;
    }
    <script src="https://www.amcharts.com/lib/4/core.js"></script>
    <script src="https://www.amcharts.com/lib/4/charts.js"></script>
    <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
    <div id="lines1"></div>


    EDIT

    to build the separate series dynamically,
    let's first revert back to the original dataset,
    where each row has the same "User" key with a different value.

    var df = [{
      "User": "A",
      "Date": 1570492800000,
      "value_act": 3.4
    }, {
      "User": "B",
      "Date": 1570492800000,
      "value_act": 1.6
    }, {
      "User": "C",
      "Date": 1570492800000,
      "value_act": 4.7
    }, {
    ...
    

    first, we use an array to find the distinct user values. (A, B, C, D, etc...)
    in this routine, we also modify the data, to create the separate keys we need for each series.
    in other words, we create a new key by appending the value --> "UserA"
    and delete the original "User" key (although this may not be necessary).

    var distinctUsers = [];
    df.forEach(function (row, index) {
      // find distinct user values
      if (distinctUsers.indexOf(row.User) === -1) {
        distinctUsers.push(row.User);
      }
    
      // create new key
      df[index]['User' + row.User] = row.User;
    
      // delete old key
      delete df[index].User;
    });
    

    next, we need to combine rows, such that each date has only one row,
    as follows...

    var df = [{
      "UserA": 3.4,
      "UserB": 1.6,
      "UserC": 4.7,
      "UserD": 0.0,
      "Date": 1570492800000,
    }, {
      "UserA": 3.4,
      "UserB": 1.6,
      "UserC": 4.7,
      "UserD": 0.0,
      "Date": 1570579200000,
    }, {
      "UserA": 3.4,
      "UserB": 1.6,
      "UserC": 4.7,
      "UserD": 0.0,
      "Date": 1570838400000,
    }];
    

    we can use the map method for this...

    // combine date rows
    df = distinctDates.map(function (date) {
      // build new combined row
      var combinedRow = {
        Date: date
      };
    
      // add user values for date
      distinctUsers.forEach(function (user) {
        df.forEach(function (row) {
          if ((row.hasOwnProperty("User" + user)) && (row.Date === date)) {
            combinedRow["User" + user] = row["User" + user];
          }
        });
      });
    
      return combinedRow;
    });
    

    then we use the array of distinct values,
    to create each unique series.

    // create unique series
    distinctUsers.forEach(function (user) {
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.dateX = "Date";
      series.name = "User" + user;  // <-- use new key for series
      series.dataFields.valueY = "value_act";
      series.tooltipText = "[#000]{valueY.value}[/]";
      series.tooltip.background.fill = am4core.color("#FFF");
      series.tooltip.getStrokeFromObject = true;
      series.tooltip.background.strokeWidth = 3;
      series.tooltip.getFillFromObject = false;
      series.fillOpacity = 0.6;
      series.strokeWidth = 2;
      series.stacked = true;
    });
    

    see following working snippet...

    am4core.ready(function() {
    
    // Themes begin
    am4core.useTheme(am4themes_animated);
    // Themes end
      var df = [{
        "User": "A",
        "Date": 1570492800000,
        "value_act": 3.4
      }, {
        "User": "B",
        "Date": 1570492800000,
        "value_act": 1.6
      }, {
        "User": "C",
        "Date": 1570492800000,
        "value_act": 4.7
      }, {
        "User": "D",
        "Date": 1570492800000,
        "value_act": 0.0
      }, {
        "User": "A",
        "Date": 1570579200000,
        "value_act": 3.4
      }, {
        "User": "B",
        "Date": 1570579200000,
        "value_act": 1.6
      }, {
        "User": "C",
        "Date": 1570579200000,
        "value_act": 4.7
      }, {
        "User": "D",
        "Date": 1570579200000,
        "value_act": 0.0
      }, {
        "User": "A",
        "Date": 1570838400000,
        "value_act": 3.4
      }, {
        "User": "B",
        "Date": 1570838400000,
        "value_act": 1.6
      }, {
        "User": "C",
        "Date": 1570838400000,
        "value_act": 4.7
      }, {
        "User": "D",
        "Date": 1570838400000,
        "value_act": 0.0
      }];
    
      // find distinct users & dates, apply value to user key
      var distinctUsers = [];
      var distinctDates = [];
      df.forEach(function (row, index) {
        // find distinct user values
        if (distinctUsers.indexOf(row.User) === -1) {
          distinctUsers.push(row.User);
        }
    
        // find distinct date values
        if (distinctDates.indexOf(row.Date) === -1) {
          distinctDates.push(row.Date);
        }
    
        // create new key
        df[index]['User' + row.User] = row.value_act;
    
        // delete old key
        delete df[index].User;
      });
    
      // combine date rows
      df = distinctDates.map(function (date) {
        // build new combined row
        var combinedRow = {
          Date: date
        };
    
        // add user values for date
        distinctUsers.forEach(function (user) {
          df.forEach(function (row) {
            if ((row.hasOwnProperty("User" + user)) && (row.Date === date)) {
              combinedRow["User" + user] = row["User" + user];
            }
          });
        });
    
        return combinedRow;
      });
    
    
    am4core.ready(function() {
    
      // Themes begin
      am4core.useTheme(am4themes_animated);
      // Themes end
    
      var chart = am4core.create("lines1", am4charts.XYChart);
    
      chart.data = df;
    
      chart.dateFormatter.inputDateFormat = "yyyy";
      var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
      dateAxis.renderer.minGridDistance = 60;
      dateAxis.startLocation = 0.5;
      dateAxis.endLocation = 0.5;
      dateAxis.baseInterval = {
        timeUnit: "Date",
        count: 1
      }
    
      var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
      valueAxis.tooltip.disabled = true;
    
      // create unique series
      distinctUsers.forEach(function (user) {
        var series = chart.series.push(new am4charts.LineSeries());
        series.dataFields.dateX = "Date";
        series.name = "User" + user;
        series.dataFields.valueY = "User" + user;
        series.tooltipText = "[#000]{valueY.value}[/]";
        series.tooltip.background.fill = am4core.color("#FFF");
        series.tooltip.getStrokeFromObject = true;
        series.tooltip.background.strokeWidth = 3;
        series.tooltip.getFillFromObject = false;
        series.fillOpacity = 0.6;
        series.strokeWidth = 2;
        series.stacked = true;
      });
    
      chart.cursor = new am4charts.XYCursor();
      chart.cursor.xAxis = dateAxis;
      chart.scrollbarX = new am4core.Scrollbar();
    
      // Add a legend
      chart.legend = new am4charts.Legend();
      chart.legend.position = "top";
    
    });
    
    });
    #lines1 {
      width: 100%;
      height: 500px;
    }
    <script src="https://www.amcharts.com/lib/4/core.js"></script>
    <script src="https://www.amcharts.com/lib/4/charts.js"></script>
    <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
    <div id="lines1"></div>