Skip to content

Commit

Permalink
update charts for #209 - style updates: for X-axis, unique colors for…
Browse files Browse the repository at this point in the history
… each dependent column value, move chart legend, add pop up tooltip for X-axis and add check for `.` in dataset column names
  • Loading branch information
joshc0044 committed Aug 7, 2019
1 parent 3f29747 commit 468f6a5
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 113 deletions.
55 changes: 41 additions & 14 deletions lab/webapp/src/components/Dataset/components/BarChart/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,61 @@ class BarChart extends Component {

// adapted from https://bl.ocks.org/d3noob/bdf28027e0ce70bd132edc64f1dd7ea4
createBarGraph(){
const { dataPreview, valByRowObj, tempKey } = this.props;
const { dataPreview, valByRowObj, depCol, cleanKey } = this.props;
let margin = { top: 10, right: 30, bottom: 20, left: 40 },
width = 400 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;

let chartInnerHTML = "";
//let valByRowObj = this.getDataValByRow();
if(document.getElementById("test_bar_chart_" + tempKey)) {
chartInnerHTML = document.getElementById("test_bar_chart_" + tempKey).innerHTML;
if(document.getElementById("test_bar_chart_" + depCol)) {
chartInnerHTML = document.getElementById("test_bar_chart_" + depCol).innerHTML;
};

if(chartInnerHTML === "") {
width = 460 - margin.left - margin.right;
let data_sorted = valByRowObj[tempKey].sort(d3.ascending);
let data_sorted = valByRowObj[depCol].sort(d3.ascending);

let classCountObj = {};
let tempData = [];
let chartData = [];

data_sorted.forEach(val => {
classCountObj[val] = classCountObj[val] ? ++classCountObj[val] : 1;
})
//tempData.push(classCountObj);
let testSet = [... new Set(valByRowObj[tempKey])];
//chartData.push(classCountObj);
let testSet = [... new Set(valByRowObj[depCol])].sort();

/**---- *************** ----**** ---- Color stuff here ----****---- *************** ----**/
// for every entry in testSet, map keys to color
let colorObjList = [];
testSet.forEach((depVal, i) => {
// use https://github.com/d3/d3-scale-chromatic#schemePaired for 12 colors
// to select unique color per class in dataset
let colorString;
if(i < 12) {
colorString = d3.schemePaired[i];
} else {
// if more than 12 classes in dataset use sequential color range
// https://github.com/d3/d3-scale-chromatic#sequential-multi-hue

// given num between 0 & 1 get color value
let normI = i / depColSet.length; // normalize index
colorString = d3.interpolateSinebow(normI);
}
colorObjList.push({[depVal]: colorString});
})
//window.console.log('colorobjlist', colorObjList);
/**---- *************** ----****---- *************** ----****---- *************** ----**/

testSet.forEach(tKey => tempData.push({

testSet.forEach(tKey => chartData.push({
entry: {
testKey: tKey,
testValue: classCountObj[tKey]
}
}));

let svg = d3.select("#test_bar_chart_" + tempKey)
let svg = d3.select("#test_bar_chart_" + cleanKey)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
Expand All @@ -65,18 +88,22 @@ class BarChart extends Component {

// y - axis
let yStuff = d3.scaleLinear()
.domain([0, d3.max(tempData, (d) => d.entry.testValue)])
.domain([0, d3.max(chartData, (d) => d.entry.testValue)])
.range([height, 0]);

svg.append('g')
.style("color", "white")
.call(d3.axisLeft(yStuff));

svg.selectAll("rect")
.data(tempData).enter()
.data(chartData).enter()
.append("rect").merge(svg)
.style("stroke", "gray")
.style("fill", "rgb(125, 91, 166)")
.style("fill", (d, i) => {
//window.console.log('colorobjlist', d);
let colorString = colorObjList[i][d.entry.testKey];
return colorString;
})
.attr("x", (d, t, s, a) => {
//window.console.log('x stuff', d);
return xStuff(d.entry.testKey);
Expand Down Expand Up @@ -130,9 +157,9 @@ class BarChart extends Component {
}

render() {
const { tempKey } = this.props;
const { cleanKey } = this.props;
return (
<div id={"test_bar_chart_" + tempKey} style={{position:'relative', left:'-60px'}}/>
<div id={"test_bar_chart_" + cleanKey} style={{position:'relative', left:'-60px'}}/>
);
}
}
Expand Down
122 changes: 92 additions & 30 deletions lab/webapp/src/components/Dataset/components/BarCharts/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class BarCharts extends Component {

};
this.createBarGraph = this.createBarGraph.bind(this);
// upper limit/max number of unique values for making chart
this.chartCutoff = 99;
}

componentDidMount() {
Expand All @@ -20,15 +22,15 @@ class BarCharts extends Component {
// and also using https://www.d3-graph-gallery.com/graph/barplot_stacked_basicWide.html
// as examples
createBarGraph(){
const { depCol, dataPreview, valByRowObj, colKey, keys } = this.props;
let margin = { top: 10, right: 30, bottom: 50, left: 70 },
width = 560 - margin.left - margin.right,
const { depCol, dataPreview, valByRowObj, colKey, keys, cleanKey } = this.props;
let margin = { top: 10, right: 65, bottom: 50, left: 35 },
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;

let chartInnerHTML = "";
// check DOM to see if graphic already exists
if(document.getElementById("stacked_bar_charts_" + colKey)) {
chartInnerHTML = document.getElementById("stacked_bar_charts_" + colKey).innerHTML;
if(document.getElementById("stacked_bar_charts_" + cleanKey)) {
chartInnerHTML = document.getElementById("stacked_bar_charts_" + cleanKey).innerHTML;
};
// only attempt to create graphic once
if(chartInnerHTML === "") {
Expand Down Expand Up @@ -57,9 +59,20 @@ class BarCharts extends Component {
//let colorObj = [{0:'#e41a1c'}, {1:'#377eb8'}];
let colorObjList = [];
depColSet.forEach((depVal, i) => {
// normalize index
let normI = i / depColSet.length;
let colorString = d3.interpolateSinebow(normI);
// use https://github.com/d3/d3-scale-chromatic#schemePaired for 12 colors
// to select unique color per class in dataset
let colorString;
if(i < 12) {
colorString = d3.schemePaired[i];
} else {
// if more than 12 classes in dataset use sequential color range
// https://github.com/d3/d3-scale-chromatic#sequential-multi-hue

// given num between 0 & 1 get color value
let normI = i / depColSet.length; // normalize index
colorString = d3.interpolateSinebow(normI);
}

colorObjList.push({[depVal]: colorString});
// i % 2 === 0
// ? colorObjList.push({[depVal]: colorList[0]})
Expand All @@ -70,6 +83,15 @@ class BarCharts extends Component {

// count proportion of given column name/key with dataset dependent_col


//======================================================================//
/********** Chart cutoff here **********/
//======================================================================//
// if number of unique values for given chart exceeds cutoff value
if(givenColSet.length > this.chartCutoff) {
this.setState({chartCutoff: true});
return;
}
// for every unique value of given colKey in dataset, collect all matches
givenColSet.forEach(tKey => {
columnValueObj[tKey] = [];
Expand Down Expand Up @@ -98,15 +120,15 @@ class BarCharts extends Component {
// for this example depCol is 'target_class' which can be '0' or '1'
// and colKey is 'cat' which can be 'a', 'b' ...

let stackedData = []; // stack data as per examples to use with d3
// having data formatted this way helps facilitating use of d3
depColSet.forEach(classKey => {
let tempObj = {"class": classKey};
givenColSet.forEach(tKey => {
tempObj[tKey] = proportionObj[tKey][classKey];
})
stackedData.push(tempObj);
})
// let stackedData = []; // stack data as per examples to use with d3
// // having data formatted this way helps facilitating use of d3
// depColSet.forEach(classKey => {
// let tempObj = {"class": classKey};
// givenColSet.forEach(tKey => {
// tempObj[tKey] = proportionObj[tKey][classKey];
// })
// stackedData.push(tempObj);
// })

// Transpose the data into layers
let stack = d3.stack()
Expand All @@ -128,7 +150,7 @@ class BarCharts extends Component {
.range(colorList);

// stacked stuff - svg elem using stacked data
let stackedSvg = d3.select("#stacked_bar_charts_" + colKey)
let stackedSvg = d3.select("#stacked_bar_charts_" + cleanKey)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
Expand All @@ -138,7 +160,22 @@ class BarCharts extends Component {
stackedSvg.append('g')
.attr('transform', `translate(0, ${height})`)
.style("color", "white")
.call(d3.axisBottom(xStuff));
.call(d3.axisBottom(xStuff))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end")
.on("mouseover", () => { xAxisTooltip.style("display", null); }) // tooltip - hover over bars and display value
.on("mouseout", function() {
window.setTimeout(() => xAxisTooltip.style("display", "none"), 3500);
})
.on("mousemove", function(d, t, s, a) {
let xPosition = 0;
let yPosition = 0;
//let yPosition = d3.mouse(this)[1] - 25; //+ stackedY(d[1] - d[0])
//window.console.log('tool tip thing for x axis', d);
xAxisTooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
xAxisTooltip.select("text").text('x-axis: ' + d);
});

// y-axis
let stackedY = d3.scaleLinear()
Expand All @@ -155,20 +192,19 @@ class BarCharts extends Component {
.style("color", "white")
.call(d3.axisLeft(stackedY));

//window.console.log('colorobjlist', colorObjList);
let groups = stackedSvg.selectAll("g.stack")
.data(stackThing)
.enter().append("g")
.style("fill", (d) => {
//window.console.log('color thing', d);
let normI = d.index / depColSet.length;
let colorString = d3.interpolateSinebow(normI);
.style("fill", (d, i) => {
let colorString = colorObjList[d.index][d.key];
return colorString;
//return colorStack(d.key)
});

let stackRect = groups.selectAll("rect")
.data((d) => {
//window.console.log('stacked thing', d);
//window.console.log('tempThing', d);
//let tempThing = {...d, classKey: d.key};
return d;
})
.enter()
Expand All @@ -191,9 +227,10 @@ class BarCharts extends Component {
.on("mouseout", function() {
window.setTimeout(() => tooltip.style("display", "none"), 3500);
})
.on("mousemove", function(d) {
.on("mousemove", function(d, t, s, a) {
let xPosition = d3.mouse(this)[0] - 15;
let yPosition = d3.mouse(this)[1] - 25; //+ stackedY(d[1] - d[0])
//window.console.log('tool tip thing', d);
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d[1] - d[0]);
});
Expand All @@ -203,7 +240,9 @@ class BarCharts extends Component {
.data(stackColorThing)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(20,"+ (i * 19) + ")";
//let legendWidth = width + margin.right;
let legendWidth = 555;
return "translate(" + legendWidth + "," + (-(i * 19) - margin.bottom) + ")";
});

legend.append("rect")
Expand Down Expand Up @@ -234,7 +273,7 @@ class BarCharts extends Component {
.style("display", "none");

tooltip.append("rect")
.attr("width", 30)
.attr("width", 35)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
Expand All @@ -245,13 +284,36 @@ class BarCharts extends Component {
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");

// tooltip elem to display x axis labels
let xAxisTooltip = stackedSvg.append("g")
.attr("class", "tooltip")
.style("display", "none");

xAxisTooltip.append("rect")
.attr("width", width)
.attr("height", 20)
.attr("fill", "black")
.style("opacity", 0.75);

xAxisTooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "start")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.style("fill", "white");
}
}

render() {
const { dataPreview, valByRowObj, colKey } = this.props;
const { dataPreview, valByRowObj, colKey, cleanKey } = this.props;
const { chartCutoff } = this.state;
return (
<div id={"stacked_bar_charts_" + colKey} />
<div>
<div id={"stacked_bar_charts_" + cleanKey} />
{chartCutoff ? (<p>Too many value to plot</p>) : null}
</div>
);
}
}
Expand Down
Loading

0 comments on commit 468f6a5

Please sign in to comment.