Encountered a problem when using react-charts. To draw a graph, you need an array of objects similar to the data:
data: [
{ "x": new Date("2020.03.18"), "y": 60 },
{ "x": new Date("2020.03.19"),"y": 23 },
{ "x": new Date("2020.03.20"),"y": 23 }
]
My data is stored in the react hook in this form:
Here is my failed attempt to split a hook into an array of objects:
const initialState = [
dataCharts.map((units) => {
{x: new Date(units.y), y: units.x}
})
]
You can do something like this.
dataCharts.map(d => {
return {
"x": d.x.split(" ")[0].split(".").reverse().join("."),
"y": d.y
}
})
Related
I am using a timeline chart from Apexcharts 3.19.0 and I noticed that every time I add a new vertical "category" the bars start to shrink. Is it possible to set the bar height a fixed size?
I am building a timeline chart that represents a production line. Bars are the production and the categories are the machines. And one build is related only to one machine.
This is the series that I pass and if I continue to add new machines bars continue to shrink.
I noticed that Apexcharts makes every bar with such height that every row can take all bars, but I don't need this in my case.
[
{
"name": "B-2004281001-6763",
"data": [
{
"x": "Cube 3-1",
"y": [
1588068083109,
1588071676403
],
}
]
},
{
"name": "B-2004281000-8133",
"data": [
{
"x": "BiZon Prusa i3 Steel-2",
"y": [
1588068021615,
1588075213496
],
}
]
},
{
"name": "B-2004281001-9110",
"data": [
{
"x": "BiZon Prusa i3 Steel-2",
"y": [
1588068068356,
1588078856311
],
}
]
}
]
That's how my chart looks like
My Chart
I had a similar issue and I got around it by using a common shared "x" value ("Production" in your example) and setting "name" values in the series data arrays that were then displayed by dataLabels.
So your modified data:
[
{
name: "B-2004281001-6763",
data: [{ name: "Cube 3-1", x: "Production", y: [ 1588068083109, 1588071676403 ] }]
},
{
name: "B-2004281000-8133",
data: [{ name: "BiZon Prusa i3 Steel-2", x: "Production", y: [1588068021615, 1588075213496 ] }]
},
{
name: "B-2004281001-9110",
data: [{ name: "BiZon Prusa i3 Steel-2", x: "Production", y: [1588068068356, 1588078856311 ], } ]
}
]
and the corresponding dataLabels option (black text color for visibility)
dataLabels: {
enabled: true,
formatter: function(val, opts) {
return opts.w.config.series[opts.seriesIndex].data[opts.dataPointIndex].name;
},
style: {
colors: ["#000000"]
}
}
Check it out here
I have a function that is updating every chart of a sheet and my main issue is that the updateChart() function is really slow (about 50 seconds) :
I already made a script that parallelize the function but due to the 20 triggers per script limitation I can only run my thread twice. So I wanted to know if there was anything that can speed up the update of my charts.
function ModifyVAxisChart()
{
var ss=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DCN Dashboard Data");
var rangeMax=ss.getRange("O3:O231").getValues();//Range to modify if you add charts
var rangeMinId=ss.getRange("P3:P232").getValues();//Range to modify if you add charts
var i=0;
var nbChart=39;
//Logger.log("range Max ="+rangeMax + "autre="+rangeMinId);
var Vmin=0;
var Vmax=0;
var id=-1;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DCN Dashboard");
var chart = sheet.getCharts();
for(i=0;i<nbChart;i++)
{
Vmax=rangeMax[i*6];
Vmin=rangeMinId[i*6];
id=rangeMinId[(i*6)+1];
var delta=(Vmax-Vmin)*0.1;
Logger.log("Vmax="+Vmax+"Vmin="+Vmin+"id="+id+"i="+i);
var currChart = chart[id];
if(currChart.getType()!="COLUMN")
{
Vmin-=delta
Vmax=Number(Vmax)+(delta*1.5)//Number() function to avoid Vmax becoming a string for no reason
}
Logger.log("Vmax="+Vmax+"Vmin="+Vmin+"id="+id+"i="+i);
currChart = currChart.modify()
.setOption('vAxes', {0: {textStyle: {fontSize: 10}, titleTextStyle: {fontSize : 8}, viewWindow: {min: Vmin, max:Vmax}}})//adpative vaxis for AREA and COMBO
.build();
sheet.updateChart(currChart);
}
}
As the App script functions aren't asynchronous, they will wait until the request is complete to keep running the code (and thus to run the rest of the requests). What you could do is to make the requests using the Sheets API [1] in either JavaScript (you would need to serve and html) or using the UrlFetchApp class [2], this way you could initiate the requests without needing to wait for the response from the previous request.
I implemented the fetchAll() method [2] with one request to update a chart and worked successfully, here is the code (you need to put the sheetID):
function uploadChart() {
var data = SpreadsheetApp.getActiveSpreadsheet();
var sheet = data.getSheets()[1];
var chart = sheet.getCharts()[0];
var chartId = chart.getChartId();
var token = ScriptApp.getOAuthToken();
var url = "https://sheets.googleapis.com/v4/spreadsheets/SHEET_ID:batchUpdate";
var chartBody = {
"updateChartSpec": {
"chartId": chartId,
"spec": {
"title": "Model Q1 Sales",
"basicChart": {
"chartType": "BAR",
"legendPosition": "RIGHT_LEGEND",
"axis": [
{
"format": {
"bold": true,
"italic": true,
"fontSize": 24
},
"position": "BOTTOM_AXIS",
"title": "Sales"
},
{
"format": {
"bold": true,
"italic": true,
"fontSize": 24
},
"position": "LEFT_AXIS",
"title": "Model Numbers"
}
],
"domains": [
{
"domain": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 0,
"endColumnIndex": 1
}
]
}
}
}
],
"series": [
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 1,
"endColumnIndex": 2
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
},
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 2,
"endColumnIndex": 3
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
},
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 3,
"endColumnIndex": 4
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
}
],
"headerCount": 1
}
}
}
}
var requestBody = {
'requests': [chartBody]
}
var request1 = {
'url': url,
'headers': {
'Authorization': 'Bearer ' + token,
},
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(requestBody),
'muteHttpExceptions': true
};
var requests = [request1]
var response = UrlFetchApp.fetchAll(requests);
Logger.log(response)
}
I used the update chart json from the "edit a chart" example [1].
To add more requests, you can either add more request jsons in the fetchAll() array parameter or add more update chart jsons in the 'requests' array on requestBody.
[1] https://developers.google.com/sheets/api/samples/charts
[2] https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchAll(Object)
I have data in a multidimensional array in the form of:
data = [
[
{
"x": 329, "y": 484.8333333333333
},
{
"x": 439, "y": 484.8333333333333
},
{
"x": 439, "y": 484.8333333333333
},
{
"x": 549, "y": 484.8333333333333
}
], [
{
"x": 559, "y": 484.8333333333333
},
{
"x": 669, "y": 484.8333333333333
},
{
"x": 669, "y": 484.8333333333333
},
{
"x": 779, "y": 484.8333333333333
}
], [
{
"x": 329, "y": 313.8333333333333
},
{
"x": 439, "y": 313.8333333333333
},
{
"x": 439, "y": 253.83333333333331
},
{
"x": 549, "y": 253.83333333333331
}
], [
{
"x": 559, "y": 313.8333333333333
},
{
"x": 669, "y": 313.8333333333333
},
{
"x": 669, "y": 253.83333333333331
},
{
"x": 779, "y": 253.83333333333331
}
], etc.
]
Each array is the coordinates for one step-before path connecting two svg elements. I've defined a function for generating a path:
stepFuction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("step-before");
I am attempting to instantiate the paths like so:
step = svg.selectAll("path")
.data(_.each(stepData, (d) => { return d; }))
.enter()
.append("path")
.attr("class", "line")
.attr("d", stepFuction);
Rather than a step-before path, I'm getting a bowtie looking shape (see attachment). Obviously, I am doing something wrong, I'm assuming it has something to do with using _.each inside the data method.
1) What is the correct approach to creating one path per array in my data array?
I would like to be able to drag the elements around and have these paths update. I am using d3.on("tick") for nodes, labels and groups with something like:
node.attr("x", function (d) { return d.x - d.width / 2 + pad; })
.attr("y", function (d) { return d.y - d.height / 2 + pad; });
and it is working correctly but I'm not sure how to update paths as there are multiple values that need to be recalculated on every tick.
2) What is the correct approach to updating these step-before paths on each tick?
I created a fiddle if anything wasn't clear in my description:
d3 step-before fiddle
I think this is actually to do with stylings.
Fiddle
Added this css code:
path {
stroke-width: 1px;
fill: none;
stroke: black;
}
I'm trying to add text labels to certain data points with xCharts. For every point that has a "name" property I want a label near the point with the "name" value as text. This picture shows what I want:
And here is the data set:
var data = {
"xScale": "time",
"yScale": "linear",
"type": "line",
"main": [{
"className": ".pizza",
"data": [{
"x": "2012-11-05",
"y": 1
}, {
"x": "2012-11-06",
"y": 6
}, {
"x": "2012-11-07",
"y": 13,
"name": "Name 1"
}, {
"x": "2012-11-08",
"y": -3
}, {
"x": "2012-11-09",
"y": -4
}, {
"x": "2012-11-10",
"y": 9,
"name": "Name 2"
}, {
"x": "2012-11-11",
"y": 6
}]
}]
};
From what I understand this kind of customization is not supported out of the box in xCharts and must be done with d3 and I'm guessing it something along the lines as described in the documentation about Custom Vis Types. But I'm a complete newbie on d3 so I can't figure out how to create something useful.
How many plotting libraries are there built on top of d3?
I studied there documentation and this is the best I can come up with:
var lineDot = xChart.getVis('line-dotted');
var myVis = {
enter: function(self, storage, className, data) {
lineDot.enter.apply(this, arguments);
// Do your custom actions here
self._g.append('g')
.selectAll('.myText')
.data(data[0].data)
.enter()
.append('text')
.attr('x', function(d,i){
return self.xScale(d.x);
})
.attr('y', function(d,i){
return self.yScale(d.y);
})
.text(function(d,i){
return d.name;
})
},
update: function(self, storage, timing) {
lineDot.update.apply(this, arguments);
// Do your custom actions here
},
exit: function(self, storage, timing) {
lineDot.exit.apply(this, arguments);
// Do your custom actions here
},
destroy: function(self, storage, timing) {
lineDot.destroy.apply(this, arguments);
// Do your custom actions here
},
};
xChart.setVis('myvis', myVis);
Note, I only coded up the enter. You should probably handle the update case as well.
Example here.
I'm trying to create a stack bar graph using the stack layout.
I can make it work only if I pass it an array of x,y coordinates. But I want to be able to add meta data to it, such as series title.
I've read the docs (https://github.com/mbostock/d3/wiki/Stack-Layout), and seen how it's done on a steamgraph (Correct usage of stack.values([accessor]) in D3 Streamgraph?). The problem with these examples is that they don't take into account things like y scale, making it difficult to establish variables such as yStackMax.
I also need the data to be passed to the stack() function early on, because I'm planning to redraw this and other things when the data is refreshed. In short, instead of:
var data = [
[
{ "x": 0, "y": 91},
{ "x": 1, "y": 290}
],
[
{ "x": 0, "y": 9},
{ "x": 1, "y": 49}
],
[
{ "x": 0, "y": 10},
{ "x": 1, "y": 25}
]
];
var layers = d3.layout.stack()(data);
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
... which works, I want to be able to do:
var data = [
{
"name": "apples",
"values": [
{ "x": 0, "y": 91},
{ "x": 1, "y": 290}
]
},
{
"name": "oranges",
"values": [
{ "x": 0, "y": 9},
{ "x": 1, "y": 49}
]
},
{
"name": "potatoes",
"values": [
{ "x": 0, "y": 10},
{ "x": 1, "y": 25}
]
}
];
var layers = d3.layout.stack()(data).values(function(d) { return d.values; });
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
... which doesn't work.
This is the working code fiddle: http://jsfiddle.net/StephanTual/E6FeP/
This is the fiddle for the code that doesn't work: http://jsfiddle.net/StephanTual/Tnj8W/
Here's an updated fiddle.
The key part was:
// define the accessor before adding in the data
var layers = d3.layout.stack().values(function(d) { return d.values; })(data);
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); });
And then I made a couple other adjustments as necessary to access the .values.