dc.js average value over the summed field - d3.js

I have the following data array:
ID Name Number
---- ------ --------
1 G 1
1 G 2
1 F 3
I want to do the following conversion to calculate the average, but I don’t know how to do it.
ID Name Number_sum
---- ------ ------------
1 G 3
1 F 3
after summing calculate the average
ID Number_avg
---- ------------
1 3
If you do not pre-sum, then the average value is calculated incorrectly:
ID Number_avg
---- ------------
1 2
I want to calculate the average value for each ID, but with an even field "Name".
Next, I plan to build a graph for each ID. I have a road identifier - 1. This road consists of 2 sections: G and F. Moreover, section G is divided into 2 more small sections, 1 and 2 km each.
If we consider the usual average value, then we get the average value over the maximum section of the value - a sub-section of the road. But I want to make a calculation based on the average value of the road sections.
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
return p;
}
function reduceInitial() {
return {count: 0, total: 0};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.count > 0 ? p.value.total / p.value.count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>

In order to calculate the average, taking into account the "Name" field, it is necessary to consider the unique occurrence of this field in the reduce function. As a result, when calculating the average value, divide the sum of values ​​by the number of unique values ​​in the "Name" field
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
if(v.Name in p.Names){
p.Names[v.Name] += 1
}
else{
p.Names[v.Name] = 1;
p.Name_count++;
};
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
p.Names[v.Name]--;
if(p.Names[v.Name] === 0){
delete p.Names[v.Name];
p.Name_count--;
};
return p;
}
function reduceInitial() {
return {count: 0, total: 0, Name_count: 0, Names: {}};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.Name_count > 0 ? p.value.total / p.value.Name_count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>

I'm not sure, but are you looking for something like this?
const arr = [
{id: 1, name: 'G', number: 1},
{id: 2, name: 'G', number: 2},
{id: 3, name: 'F', number: 3}
]
const res = arr.reduce((acc, e) => {
const idx = acc.findIndex(x => x.name === e.name)
if (idx !== -1) {
acc[idx].number += e.number
} else {
acc.push(e)
}
return acc
}, [])
console.log(res)

I'm not sure if I completely understand, but if you want to group by Name, sum, and and then take the average of all groups, you could put your dimension on Name and use regular reduceSum:
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
Then pass a "fake groupAll" which returns all the rows from the group to the number display, and calculate the average in the value accessor:
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
inlineND
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>

Related

Mapbox dash line animation

I am exploring ways to illustrate a directional walking route via animating dots (that are all smoothly moving in 1 direction) and I wanted to see if there is anything that I am missing. I am running into a similar issue in "#react-native-mapbox-gl/maps": "8.5.0" with MapboxGL.LineLayer and then updating style.lineDasharray via setInterval to simulate an animation. The issue also seem to persist in mapbox-gl-js/v0.34.
It appears that the animation looks correct when using lineCap: 'butt', but it breaks down when I switch to lineCap: 'round'. Even when it works correctly, you can see a 1 pixel static lines with line-cap: "butt". I assume it's it looks broken when we switch to "round" because the library code attempts to create a round shape and adds left & right pieces. Basically, it looks like some dashes are moving and some are static (see attached videos). Any tricks to make this into a smooth mono-directional animation? Here is a code snippet that demonstrates the issue.
https://codepen.io/prototypersf/pen/XWYwBYN?editors=1010
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
]
}
}
},
"layout": {
//"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 6
}
});
var dashLength = 1;
var gapLength = 13;
// We divide the animation up into 40 steps to make careful use of the finite space in LineAtlas //
var steps = 24;
// A # of steps proportional to the dashLength are devoted to manipulating the dash
var dashSteps = steps * dashLength / (gapLength + dashLength);
// A # of steps proportional to the gapLength are devoted to manipulating the gap
var gapSteps = steps - dashSteps;
// The current step #
var step = 0;
setInterval(function() {
step = step + 1;
if (step >= steps) step = 0;
var t, a, b, c, d;
if (step < dashSteps) {
t = step / dashSteps;
a = (1 - t) * dashLength;
b = gapLength;
c = t * dashLength;
d = 0;
} else {
t = (step - dashSteps) / (gapSteps);
a = 0;
b = (1 - t) * gapLength;
c = dashLength;
d = t * gapLength;
}
map.setPaintProperty("route", "line-dasharray", [a, b, c, d]);
}, 125);
map.setPaintProperty("route", "line-dasharray", [1, 2, 1, 4]);
});
</script>
</body>
</html>

Kendo UI Charts - Diplay text instead of value in yAxis labels

Is there any way to display a label other than the value in the Value Axis in Kendo Chart?
What I want is the right image's labels instead of the left one's (the original).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2021.1.330/styles/kendo.default-v2.min.css"/>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2021.1.330/js/kendo.all.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// these are the labels I want to replace for the number values.
const yAxisLabels = ["A", "B", "C", "D", "E"];
$("#chart").kendoChart({
series: [
{
type: "line",
data: [2, 3, 4]
}
],
valueAxis: {
min: 1,
max: 5,
majorUnit: 1
}
});
</script>
</body>
</html>
There is - templates are what you are looking for. However, from your question there is no way to help with exactly how to accomplish what you want since there is no code/or explanation of where your alternate values come from. That said, the following is one way to customize those labels.
Dojo example: https://dojo.telerik.com/OsuWirih
<div id="chart"></div>
<script>
$("#chart").kendoChart({
series: [
{
type: "scatter",
data: [ [1, 2] ]
}
],
yAxis: {
labels: {
template: "X: #: value #"
}
}
});
</script>
More info here: https://docs.telerik.com/kendo-ui/api/javascript/dataviz/ui/chart/configuration/yaxis.labels#yaxislabelstemplate
EDIT:
From your comment, I'm still not sure what is driving the A,B,C to replace values. Here is one way to accomplish it if the underlying values line up with the A, B, C... labels you want:
<script>
let dict = {0:'A',0.5:'B',1:'C',1.5:'D',2:'E',2.5:'F'};
$("#chart").kendoChart({
series: [
{
type: "scatter",
data: [ [1, 2] ]
}
],
yAxis: {
labels: {
template: "#: dict[value] #"
}
}
});
</script>
and in a dojo: https://dojo.telerik.com/EpElOFAy/2

How to change kendo chart legend item color on unselect?

dynamically Change The Kendo chart legend unselect on apply custom color
$("#chart").kendoChart({
series: [
{ data: [6, 2, 3], name: "Task 1" },
{ data: [1, 5, 2], name: "Task 2" }
],
legendItemClick: function(e){
e.sender.options.legend.inactiveItems.markers.color = "red";
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</title>
<script src="https://kendo.cdn.telerik.com/2018.3.911/js/kendo.all.min.js"></script>
</head>
<body>
<div id="chart"></div>
</body>
</html>
$("#chart").kendoChart({
series: [
{ data: [6, 2, 3], name: "Task 1" },
{ data: [1, 5, 2], name: "Task 2" }
],
legendItemClick: function(e){
e.sender.options.legend.inactiveItems.markers.color = "red";
}
});
e.sender.options.legend.inactiveItems.markers.color = "<Use Your Custom Color>";
This Line Of Code Place At LegendItemClick Templet Or Function.I think.... This is Use full To Succeed Your need.

javascript canvasjs Get data points dynamically from file.txt

Objective:
I want to get data from file.txt thats is saved local in /var/www/html/file.txt and use it for the doughnut chart on my webpage dynamically on a interval of 2 seconds
file.txt only has one entry and looks like:
34
the javascript i have tried:
$.get("file.txt", function(data) {
var x = 0;
var allLines = data.split('\n');
if(allLines.length > 0) {
for(var i=0; i< allLines.length; i++) {
dataPoints.push({y: parseInt(allLines[i])});
x += .25;
}
}
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "doughnut",
dataPoints : dataPoints,
}]
});
chart.render();
});
}
Entire html looks like
<!DOCTYPE html>
<html>
<head>
<title>Chart using txtfile Data</title>
<script type="text/javascript" src="http://canvasjs.com/assets/script /jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="http://canvasjs.com/assets/script/canvasjs.min.js"></script>
</head>
<body>
<script type="text/javascript">
$.get("graph.txt", function(data) {
var xVal = 0;
var allLines = data.split('\n');
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "line",
dataPoints : [function()
{
if(allLines.length > 0) {
for(var i=0; i< allLines.length; i++) {
xVal +=.25;
dataPoints.push({x : xVal, y: parseInt(allLines[i])});
}
}
}]
}]
});
chart.render();
},'text');
</script>
<script type="text/javascript" src="canvasjs.min.js"></script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
</body>
</html>
I believe something like this may work for you. You may have to use Firefox as Chrome doesn't like cross origin requests
First, dataPoints was undefined, I moved the code into a function inside dataPoints. I changed your variable name from x to xVal. Added the 'text' word so the $get knows what format it's reading and also there was an extra bracket it seemed. Give this a try.
$.get("graph.txt", function(data) {
var xVal = 0;
var allLines = data.split('\n');
var dps = [];
for(var i=0; i< allLines.length; i++) {
xVal +=.25;
dps.push({x : xVal, y: Number(allLines[i])});
}
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "line",
dataPoints : dps
}]
});
chart.render();
},'text');

Valid JSON feed | blank page

Here is my jsonp feed: http://www.letheatredelorient.fr/saison/data.jsonp (JSONLint valid)
Here is my getJSON script:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(document).ready(function () {
$.getJSON("http://www.letheatredelorient.fr/saison/data.jsonp?callback=", function (data) {
$.each(data.Items, function (i, node) {
var title = node.titre;
$("#myTitle").html(title);
});
});
});
</script>
</head>
<body>
<div id="myTitle"></div>
</body>
</html>
It's really simple. But, it gets the feed, but it doesn't parse it. Any ideas?
Try this:
var title = node.node.titre;
In your code the node is the Item object, the node is in that, is this a little clearer?
$.getJSON("http://www.letheatredelorient.fr/saison/data.jsonp?callback=", function (data) {
$.each(data.Items, function (i, item) {
//For each item in Items
var title = item.node.titre;
$("#myTitle").html(title);
});
});
This is your json, I've added comments, you're looping through items which contain node:
{
"Items": [
-item{
"node": {
"titre": "La Faculté",
"image": "http://www.letheatredelorient.fr/sites/default/files/imagecache/130/saison/spectacles/faculte/photos/faculte-web2calainfonteray.jpg"
}
},
-item{
"node": {
"titre": "Nouveau Roman",
"image": "http://www.letheatredelorient.fr/sites/default/files/imagecache/130/saison/spectacles/nouveau-roman/photos/1210-nouveauroman-04cjeanlouisfernandez.jpg"
}
}
]
}

Resources