Adapting D3 to PowerBI D3 Visual - d3.js

I am trying to adapt some code, in order to make it work in PowerBI. Although i am pretty sure i know where the problem is (the function), i can't figure it out.
I followed this tutorial https://www.mssqltips.com/sqlservertip/5273/how-to-render-d3js-custom-charts-in-power-bi-desktop/
-added pbi.height and pbi.width
-removed .append("svg") from svg var
-tried messing with the function by adding pbi.dsv
-i have a pbi object with 2 columns and 2 values
-i also have the css in place
Original code: https://codepen.io/herudea/pen/YpEeRW
var width = pbi.width,
height = pbi.height,
twoPi = 2 * Math.PI,
progress = 0,
allocated = "completed",
total = "total",
formatPercent = d3.format(".0%");
var arc = d3.svg.arc()
.startAngle(0)
.innerRadius(58)
.outerRadius(66);
var svg = d3.select("#docsChart")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var meter = svg.append("g")
.attr("class", "funds-allocated-meter");
meter.append("path")
.attr("class", "background")
.attr("d", arc.endAngle(twoPi));
var foreground = meter.append("path")
.attr("class", "foreground");
var percentComplete = meter.append("text")
.attr("text-anchor", "middle")
.attr("class", "percent-complete")
.attr("dy", "0em");
var description = meter.append("text")
.attr("text-anchor", "middle")
.attr("class", "description")
.attr("dy", "2.3em")
.text("Total Complete");
var i = d3.interpolate(progress, allocated / total);
d3.transition().duration(1000).tween("progress", function() {
return function(t) {
progress = i(t);
foreground.attr("d", arc.endAngle(twoPi * progress));
percentComplete.text(formatPercent(progress));
};
});
I expect to have a working gauge based on the created pbi object.

Related

d3.js lines not showing for part of filtered then nested data (added plot images)

I have a data that is filtered by year then nested by group. The first few years there is one group, while the remaining years have seven to eight groups for which the lines are plotted. Along with lines I also plot points of actual data.
here is the plot for the year 2007 that has only one group:
2007 line plot
Notice the y-axis is missing and only the points are showing but no lines.
Here is the plot for the year 2010 that has eight groups:
2010 lines plot
Notice the y-axis, all points and lines are showing.
Here is the associated code:
Defining line
var line = d3.svg.line()
.x(function(d) { return LoanAmount_scale(d['amount_mean']); })
.y(function(d) { return Investor_scale(d['Investors_mean']); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right )
.attr("height", height + margin.top + margin.bottom);
var plot = svg.append("g")
.attr("class", "chart")
.attr("width" , width- margin.left)
.attr("height", height)
.attr("transform","translate ("+ margin.left + "," + margin.top + ")");
menu selection
// setting stage for dropdown menu
// years
var years = ['Select Year'];
var i= 2006;
for( i = 2006; i<= 2014; i++){
years.push(i);
}
// selecting an option
var select = d3.select('body')
.append('select')
.attr('class','select')
.attr('id','select_id')
.on('change',change);
// available options on dropdown menu
var option = select.selectAll('option')
.data(years)
.enter()
.append('option')
.text(function(d){ return d});
// menu selection of year
function change(){
var select_year = d3.select('select')
.property('value')
// reset chart before updating year
update(data,'Select Year');
// update year
update(data,select_year);
}
Update function filters year and nests by group, then plots lines and points
function update(data, year){
var Filtered = data.filter(function(d){ return d["LoanOrig_Yr"] == year});
var nested_by_rating = d3.nest()
.key(function(d) {
return d['ProsperRating']
}).sortKeys(d3.ascending)
.entries(Filtered);
// add lines for each rating
var lines = plot.selectAll("path")
.data(nested_by_rating)
// remove old lines
lines.exit().remove();
plot.selectAll("circle")
.data(nested_by_rating)
.exit().remove();
nested_by_rating.forEach( function(d,i){
var prosperR = d.key
// entering data
lines.enter()
.append("path")
.attr("class", "line")
.attr("d", line(d.values))
.attr("stroke", RatingColor(prosperR))
.attr("stroke-width", 3)
.attr("stroke-opacity",0.3)
.attr("fill","none");
debugger;
plot.selectAll("path.dot")
.data(d.values)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5)
.attr("fill","Black");
debugger;
// dynamic legend
svg.append("text")
.attr("x", 1300)
.attr("y", 100 + i*20)
.attr("class", "legend") // style the legend
.style("fill", function() { // Add the colours dynamically
return d.color = RatingColor(prosperR); })
.text(prosperR);
Reset chart between year selection
// reseting chart between years
if (year == 'Select Year'){
gX.call(xAxis);
gY.call(yAxis);
svg.append("text")
.attr("x", 1300)
.attr("y", 100)
.attr("class", "legend") // style the legend
.style("fill", "white")
.text(["NR"]);
svg.selectAll("legend_text")
.data(["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"])
.enter()
.append("text")
.attr("class","legend")
.attr("x", 1300)
.attr("y", function(d,i){return 100 + i*20;
})
.attr("class", "legend") // style the legend
.style("fill", "white")
.style("stroke","white")
.text(function(d,i){return d;});
} \\ end if statement
} \\end .forEach statement
When tracking data entry and exit using the browser inspector, I find the data in both cases move in and out as expected. Observe the points data are passed using line.x() and line.y(), so it is very strange that the line does not show in the year 2007 and other similar years with one group.
I would appreciate any help fixing this bug. Thank you.
You delete the lines by selecting all paths, and the axis line is a path.
Also the update of the legend was adding each time a lot of entries.
Black legend text is hidden/shown by setting fill to white or black.
The axis do not need update drawing for the Select Year, they stay the same for all graphs.
var data = d3.csv.parse(d3.select("#dataset").text());
data.forEach(function(d){
d.LoanOrig_Yr = parseInt(+d.LoanOrig_Yr);
d.amount_median = parseFloat(+d.amount_median);
d.Investors_median = parseInt(+d.Investors_median);
});
function draw(data) {
var margin = {top: 5, right: 100, bottom: 150, left: 80},
width = 1460 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var margin2 = {top: 430, right: 200, bottom: 110, left:80},
height2 = 600 - margin2.top - margin2.bottom;
var margin3 = {top: 40, right:20, bottom: 80, left:180},
width2 = 1300 - margin3.left - margin3.right;
// display title
d3.select("body")
.append("h2")
.text("Funding low risk loans is shared by more investors than high risk loans");
// guiding text
d3.select("body")
.append("p")
.text("An investor may fund the whole loan or fractions of several loans with minimum"+
" investment amount of $25."+
" Prosper rating started in July 2009." +
" Prosper Rating (ordered): AA (lowest risk), A, B, C, D, E, HR (high risk), NR (not rated)." +
" Points on the line reflect loan amount average computed by "+
" agregating loans over brackets of size $1500, starting at $1000." +
" Data for 2014 is only for the first quarter."
);
var Investor_extent = d3.extent(data, function(d){
return d.Investors_median;
});
var Investor_scale = d3.scale.linear()
.domain(Investor_extent)
.range([height,margin.top]);
var LoanAmount_extent = d3.extent(data, function(d){
return d.amount_median;
});
var LoanAmount_scale = d3.scale.linear()
.range([margin.left, width])
.domain(LoanAmount_extent);
var ProsperRating = ["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"];
var colors = ['Brown','Red','Orange','Yellow','Green','Blue','Purple','Gray'];
var RatingColor = d3.scale.ordinal()
.domain(ProsperRating)
.range(colors);
var xAxis = d3.svg.axis()
.scale(LoanAmount_scale)
.orient("bottom")
.ticks(12);
var yAxis = d3.svg.axis()
.scale(Investor_scale)
.orient("left")
.ticks(10);
var line = d3.svg.line()
.x(function(d) { return LoanAmount_scale(d.amount_median); })
.y(function(d) { return Investor_scale(d.Investors_median); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right )
.attr("height", height + margin.top + margin.bottom);
var plot = svg.append("g")
.attr("class", "chart")
.attr("width" , width- margin.left)
.attr("height", height)
.attr("transform","translate ("+ margin.left + "," + margin.top + ")");
var div = d3.select("body")
.append("div")
.attr("class","tooltip")
.style("opacity",0);
// setting stage for dropdown menu
// years
var years = ['Select Year', 2007, 2010];
// selecting an option
var select = d3.select('body')
.append('select')
.attr('class','select')
.attr('id','select_id')
.on('change',change);
// available options on dropdown menu
var option = select.selectAll('option')
.data(years)
.enter()
.append('option')
.text(function(d){ return d});
// menu selection of year
function change(){
var select_year = d3.select('select')
.property('value')
// reset chart before updating year
update(data,'Select Year');
// update year
update(data,select_year);
}
// add x axis
var gX = plot.append("g")
.attr("class","x axis")
.attr("transform","translate (0 " + height + ")")
.call(xAxis);
// add y axis
var gY = plot.append("g")
.attr("class", "y axis")
.attr("transform" , "translate( " + margin.left + " ,0)")
.call(yAxis);
//add x axis label
plot.append("text")
.attr("class", "x label")
.attr("text-anchor", "middle")
.attr("x", width/2 )
.attr("y", height + 40)
.text("Loan Amount median (US dollars)");
//add y axis label
plot.append("text")
.attr("class", "y label")
.attr("text-anchor", "middle")
.attr("x",0-(height/2))
.attr("y", 20)
.attr("dy", "1em")
.attr("transform", "rotate(-90)")
.text("Median number of investors who share funding one loan");
// legend title
svg.append("text")
.attr("x",1300)
.attr("y",70)
.attr("class","legend_title")
.style("fill","white")
.text("Legend: Prosper Rating");
svg.append("text")
.attr("x",1300)
.attr("y",30)
.attr("class","legend_title")
.style("fill","white")
.text("point mouse at line to determine rating");
function update(data, year){
var Filtered = data.filter(function(d){ return d.LoanOrig_Yr == year});
var nested_by_rating = d3.nest()
.key(function(d) { return d.ProsperRating })
.sortKeys(d3.ascending)
.entries(Filtered);
// add lines for each rating
var lines = plot.selectAll(".line")
.data(nested_by_rating)
// remove old lines
lines.exit().remove();
plot.selectAll("circle")
.data(nested_by_rating)
.exit().remove();
nested_by_rating.forEach( function(d,i){
var prosperR = d.key;
// entering data
lines.enter()
.append("path")
.attr("class", "line")
.attr("d", line(d.values))
.attr("stroke", RatingColor(prosperR))
.attr("stroke-width", 3)
.attr("stroke-opacity",0.3)
.attr("fill","none");
plot.selectAll("path.dot")
.data(d.values)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5)
.attr("fill","Black");
// dynamic legend
svg.append("text")
.attr("x", 1300)
.attr("y", 100 + i*20)
.attr("class", "legend") // style the legend
.style("fill", function() { return RatingColor(prosperR); }) // Add the colours dynamically
.text(prosperR);
// mouse hover tip tool
lines.on("mouseover", function(d){
div.transition()
.duration(200)
.style("opacity", 0.9);
div.html("Prosper Rating : " + prosperR)
.style("left", (d3.event.pageX) + "px")
style("top", (d3.event.pageY - 2) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
});
// reseting chart between years
if (year == 'Select Year'){
svg.selectAll(".legend_title").style("fill","white");
svg.selectAll(".legend").remove();
} else {
svg.selectAll(".legend_title").style("fill","black");
}
}
}
draw(data);

Adding a legend to a pie chart in D3js

I'm trying to plot a pie chart with a legend inside of it. And I got into troubles to get it plotted, since I get the errors abound undefined variables. I managed to draw the chart itself and the half of the legend, but not in the right colors, what should match the pie chart.
function drawPieChart(d3div, chart_data) {
// chart_data.data is a list of data elements.
// each should contain fields: val, col, name
d3div.html(""); // clear the div
var title = getopt(chart_data, 'title', '');
// desired width and height of chart
var w = getopt(chart_data, 'width', 300);
var h = getopt(chart_data, 'height', 300);
var pad = getopt(chart_data, 'pad', 50);
var textmargin = getopt(chart_data, 'textmargin', 20);
var r = Math.min(w, h) / 2 - pad; // radius of pie chart
var div = d3div.append('div');
if(title !== '') {
div.append('p').attr('class', 'pietitle').text(title);
}
var arc = d3.svg.arc()
.outerRadius(r)
.cornerRadius(20)
.innerRadius(150);
var arcLarge = d3.svg.arc()
.innerRadius(150)
.cornerRadius(20)
.outerRadius(r + 50);
var toggleArc = function(p){
p.state = !p.state;
var dest = p.state ? arcLarge : arc;
d3.select(this).select("path").transition()
.duration(160)
.attr("d", dest);};
var pie = d3.layout.pie()
.padAngle(.03)
.sort(null)
.value(function(d) { return d.val; });
var svg = d3.select("#piechart").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(chart_data.data))
.enter().append("g")
.attr("class", "arc")
.attr("stroke", "#999")
.attr("id",function(d){return d.data;})
.on("mouseover",toggleArc)
.on("mouseout",toggleArc);
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return d.data.col; });
var color = d3.scale.category20b();
var legendRectSize = 18;
var legendSpacing = 4;
// FROM here the code is not produced the desired result
var legend = svg.selectAll('.legend')
.data(chart_data.data)
.enter()
.append('g')
.attr('class', 'legend')
.attr("id",function(d){return d.data;})
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * chart_data.data.length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.data(chart_data.data)
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style("fill", function(d) { return d.data.col; });
legend.append("text")
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d.data.name; });
}
The code actually works fine untill the line var legend = svg.selectAll('.legend')
Then i start to define the legend, but D3 complains about undefined d.data every time i try to access d.data below the line I written above(also in the last line of the code).
I don't understand where i got on the wrong way.
If instead of defining the whole non working part(var legend...) i write this code:
g.append("text")
.attr("stroke", "none")
.attr("fill", function(d) { return d.data.col; })
.text(function(d) { return d.data.name; });
I'm able to access the d.data.name.
Unfortunately wrong colors of the boxes and not description.
Thanks!

D3 Pie Error: Invalid value for <text> attribute transform="translate(NaN,NaN)"

Keep getting a console error of
Error: Invalid value for attribute transform="translate(NaN,NaN)"
Re-created the error here.
http://jsfiddle.net/9f9wonoc/
var width = 360;
var height = 360;
var radius = Math.min(width, height) / 2;
var color = {
'Pass': '#66B51B',
'Fail': '#d03324'
}
var data = [
{ label: 'Pass', count: 12 },
{ label: 'Fail', count: 10 },
];
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
var arc = d3.svg.arc()
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) { return d.count; })
.sort(null);
var path = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
path.append("path")
.attr("d", arc)
.style("fill", function(d) { return color[d.data.label]; });
path.append("text")
.attr("transform", function(d, i) {
return "translate(" + arc.centroid(d, i) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.count; });
What am I doing wrong?
You missed one small thing :
you need to set the inner radius 0 as below:
var arc = d3.svg.arc()
.outerRadius(radius).innerRadius(0);
Working code here
Hope this helps!

How to fill a single value in the 'Donut Chart'

I am getting current status from the server. from the server information i need to show the current status of the finished works.
like this :
I am trying here, but i am not getting result.
here is my code :
var data = [45] //say value i get.
var width = 400,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#ffff00", "#1ebfc5"]);
var arc = d3.svg.arc()
.outerRadius(radius - 90)
.innerRadius(radius - 80);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(color[0]); });
Live Demo
You probably just get a full circle, right?
Well, d3 stands for data driven documents, which means that it cannot show data that isn't there.
So basically, to fix it, you just need the counter value in your dataset: I have fixed your code below:
var data = [45, 55] //as you see, i have added 55 (100-45).
var width = 400,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.domain(data)
.range(["#ffff00", "#1ebfc5"]);
var arc = d3.svg.arc()
.outerRadius(radius - 90)
.innerRadius(radius - 80);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d,i) { return color(d.data); });
EDIT
I also changed your coloring. in the end of your code you do "return color(color[0])" which always returns the same color. So even if you would have 2 different parts in your donut chart, they whould have been colored the same and you wouldn't have noticed the difference :-). Use the d3 built in data variable. For arcs/pies, the variable d also returns more then just the original data, it returns a custom object. Your data is stored in d.data, which you can see in the code I included.

Fill the inside of a pie donut chart d3

I am trying to create the image above using d3
http://jsfiddle.net/Spwizard/LBzx7/1/
var dataset = {
hddrives: [20301672, 9408258, 2147483, 21474836, 35622,32210000],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#2DA7E2"]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 70);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset.hddrives))
.enter().append("path")
.attr("class", "arc")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "inside")
.text(function(d) { return '56%'; });
svg.append("text")
.attr("dy", "2em")
.style("text-anchor", "middle")
.attr("class", "data")
.text(function(d) { return 'some text'; });
Im struggling to see how to deal with the background color of the inner circle and dealing with the space left for storage
Thanks
To get a "background", you can add another circle with the respective fill colour. To deal with the free space, you can selectively set the opacity of one of the segments to 0. In your example, I've done that for the last slice:
.style("opacity", function(d, i) { return i == dataset.hddrives.length - 1 ? 0 : 1; })
Complete example (provided by OP) here.
Just append text:
svg.append("text")
.attr("text-anchor", "middle")
.attr('font-size', '20px')
.attr('y', '5')
.text(dataset.hddrives + "%");

Resources