My goal here is to draw parallelograms connecting the rank of one company in one business quarter to its new rank in the next business quarter. I am using rects to represent each company or manager
. (each column is a business quarter). My steps/progress:
- used
.each()
to store the parallelograms vertices by retrieving each rect's.attr()
forx
,y
andheight
. - Since the order of companies is different for each column, I had to store these x & y coordinates into two arrays.
- Then I had to merge them on the condition that the xs and ys belonged to the same manager. (for example,
Fidelity
goes from being #1 to #2 after the first column).
This is where the trouble began, basically, my y1
s and y0
s are all stuck at the same value and I'm not sure why.
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var outerRadius = (400 / 2);
var innerRadius = 15;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+ ")");
var heightScale = d3.scaleLinear()
.domain([.01,.09])
.range([7,50]);
/*
var tsvData = d3.tsv('market-share-change.tsv');
tsvData.then(function(rawData) {
var data = rawData.map(function(d) {
return { manager:d.manager, t1:+d.t1, t2:+d.t2, t3:+d.t3}
});
})
*/
var data = [[{'manager': 'Mirae Asset', 'share': 0.016},
{'manager': 'Manulife', 'share': 0.015},
{'manager': 'ChinaAMC', 'share': 0.012},
{'manager': 'Principal', 'share': 0.015},
{'manager': 'Aberdeen Standard', 'share': 0.013},
{'manager': 'CSOP', 'share': 0.015},
{'manager': 'BOCI-Prudential', 'share': 0.019},
{'manager': 'Allianz', 'share': 0.016},
{'manager': 'HSBC', 'share': 0.027},
{'manager': 'Deutsche Bank', 'share': 0.014},
{'manager': 'Invesco', 'share': 0.025},
{'manager': 'First State', 'share': 0.033},
{'manager': 'JP Morgan', 'share': 0.041},
{'manager': 'Value Partners', 'share': 0.04},
{'manager': 'Schroders', 'share': 0.051},
{'manager': 'Hang Seng', 'share': 0.063},
{'manager': 'UBS', 'share': 0.056},
{'manager': 'SSgA', 'share': 0.066},
{'manager': 'Fidelity', 'share': 0.088},
{'manager': 'BlackRock', 'share': 0.084}],
[{'manager': 'Mirae Asset', 'share': 0.013},
{'manager': 'Manulife', 'share': 0.014},
{'manager': 'ChinaAMC', 'share': 0.013},
{'manager': 'Principal', 'share': 0.018},
{'manager': 'Aberdeen Standard', 'share': 0.014},
{'manager': 'CSOP', 'share': 0.019},
{'manager': 'BOCI-Prudential', 'share': 0.02},
{'manager': 'Allianz', 'share': 0.015},
{'manager': 'HSBC', 'share': 0.023},
{'manager': 'Deutsche Bank', 'share': 0.02},
{'manager': 'Invesco', 'share': 0.02},
{'manager': 'First State', 'share': 0.029},
{'manager': 'JP Morgan', 'share': 0.04},
{'manager': 'Value Partners', 'share': 0.039},
{'manager': 'Schroders', 'share': 0.051},
{'manager': 'Hang Seng', 'share': 0.063},
{'manager': 'UBS', 'share': 0.055},
{'manager': 'SSgA', 'share': 0.065},
{'manager': 'Fidelity', 'share': 0.088},
{'manager': 'BlackRock', 'share': 0.101}],
[{'manager': 'Mirae Asset', 'share': 0.012},
{'manager': 'Manulife', 'share': 0.014},
{'manager': 'ChinaAMC', 'share': 0.014},
{'manager': 'Principal', 'share': 0.017},
{'manager': 'Aberdeen Standard', 'share': 0.017},
{'manager': 'CSOP', 'share': 0.018},
{'manager': 'BOCI-Prudential', 'share': 0.018},
{'manager': 'Allianz', 'share': 0.018},
{'manager': 'HSBC', 'share': 0.023},
{'manager': 'Deutsche Bank', 'share': 0.023},
{'manager': 'Invesco', 'share': 0.023},
{'manager': 'First State', 'share': 0.029},
{'manager': 'JP Morgan', 'share': 0.041},
{'manager': 'Value Partners', 'share': 0.044},
{'manager': 'Schroders', 'share': 0.048},
{'manager': 'Hang Seng', 'share': 0.056},
{'manager': 'UBS', 'share': 0.061},
{'manager': 'SSgA', 'share': 0.062},
{'manager': 'Fidelity', 'share': 0.087},
{'manager': 'BlackRock', 'share': 0.096}]];
var poly = [
{'x':0,'y':0},
{'x':0,'y':0}
];
var multiPoly1 = d3.range(20).map(()=>JSON.parse(JSON.stringify(poly)));
var multiPoly2 = d3.range(20).map(()=>JSON.parse(JSON.stringify(poly)));
/*
for (var k=0; k <(data[0].length); k++) {
polyMaster[0][k].push({'manager':data[0][k].manager})
};
*/
var colorMap = {
'Fidelity':"#003366",
'BlackRock':"#366092",
'SSgA':"#4f81b9",
'Hang Seng':"#95b3d7",
'UBS':"#b8cce4",
'Schroders':"#e7eef8",
'JP Morgan':"#a6a6a6",
'Value Partners':"#d9d9d9",
'Yuanta':"#ffffcc",
'First State':"#ffffcc",
'HSBC':'#f6d18b',
'Invesco':'#e4a733',
'BOCI-Prudential':"#b29866",
'Allianz':'#a6a6a6',
'Mirae Asset':'#d9d9d9',
'Manulife':'#e7eef8',
'CSOP':'#b8cce4',
'Principal':'#95b3d7',
'Deutsche Bank':'#4f81b9',
'Aberdeen Standard':'#366092',
'ChinaAMC':'#003366'
};
for (var j=0; j <(data.length); j++) {
var className = "column"+String(j);
let counterRect = 0,
counterText = 0;
var spacing = 170;
var sortedData = data[j].sort(function(a,b) {
return b.share - a.share;
});
var column = graphGroup.selectAll(className)
.data(sortedData)
.attr('class', className)
.enter().append("g");
column.append("rect")
.attr('class','rect'+String(j))
.attr("width", 120)
.attr("height", function(d) {
return heightScale(d.share)
})
.attr('x', function(d) {return j*spacing})
.attr('y', function(d, i) {
let previous = counterRect;
return (counterRect += heightScale(d.share)+2, previous)
})
.each(function(d,i) {
if (j==0) {
multiPoly1[i][0].x = 120;
}
})
.each(function(d,i) {
if (j==0) {
multiPoly1[i][0].y = parseFloat(d3.select(this).attr('y'));
}
})
.each(function(d,i) {
if (j==0) {
multiPoly1[i][1].x = 120;
}
})
.each(function(d,i) {
if (j==0) {
multiPoly1[i][1].y = parseFloat(d3.select(this).attr('y'))+parseFloat(d3.select(this).attr('height'));
}
})
.each(function(d,i) {
if (j==1) {
multiPoly2[i][1].x = parseFloat(d3.select(this).attr('x'));
}
})
.each(function(d,i) {
if (j==1) {
multiPoly2[i][1].y = parseFloat(d3.select(this).attr('y'));
}
})
.each(function(d,i) {
if (j==1) {
multiPoly2[i][0].x = parseFloat(d3.select(this).attr('x'));
}
})
.each(function(d,i) {
if (j==1) {
multiPoly2[i][0].y = parseFloat(d3.select(this).attr('y'))+parseFloat(d3.select(this).attr('height'));
}
})
.style('fill',function(d,i) {return colorMap[d.manager]});
column.append("text")
.attr('x', function(d) {return j*spacing+60})
.attr('y', function(d, i) {
let previous = counterText;
return (counterText += heightScale(d.share)+2, previous + (heightScale(d.share)/2))
})
.attr("dominant-baseline", "central")
.attr('text-anchor', 'middle')
.text(function(d) {
return d.manager;
});
var managerList = [];
for (var k = 0; k < sortedData.length; k++)
managerList.push({'manager':sortedData[k].manager});
var tempList1 = [];
for (var k = 0; k < managerList.length; k++)
tempList1.push({'manager': managerList[k], 'x1': multiPoly1[k][0].x, 'y1':multiPoly1[k][0].y, 'x2':multiPoly1[k][1].x, 'y2':multiPoly1[k][1].y });
var tempList2 = [];
for (var k = 0; k < managerList.length; k++)
tempList2.push({'manager': managerList[k], 'x3': multiPoly2[k][1].x, 'y3':multiPoly2[k][1].y, 'x4':multiPoly2[k][0].x, 'y4':multiPoly2[k][0].y });
var combinedList = [];
const list1ByManager = tempList1.reduce((a, item) => {
a[item.manager] = item;
return a;
}, {});
var combinedList = tempList2.map((item2) => ({
...list1ByManager[item2.manager],
...item2
}));
console.log(combinedList)
var polyMaster = [];
for (var k = 0; k < managerList.length; k++) {
var tempItem = [
{'x':combinedList[k].x1, 'y':combinedList[k].y1},
{'x':combinedList[k].x2, 'y':combinedList[k].y2},
{'x':combinedList[k].x3, 'y':combinedList[k].y3},
{'x':combinedList[k].x4, 'y':combinedList[k].y4},
];
polyMaster.push(tempItem);
}
console.log(polyMaster)
};
graphGroup.selectAll("polygon")
.data(polyMaster)
.enter().append("polygon")
.attr("points",function(d) {
return d.map(function(d) { return [d.x,d.y].join(","); }).join(" ");})
.attr("stroke","black")
.attr("stroke-width",2);
<script src="https://d3js.org/d3.v5.min.js"></script>
Examining tempList1
and tempList2
in the console log confirms I have indeed stored all the values correctly, but for some reason when I try to merge these two arrays into combinedList
the y
s are not merged properly. Relevant code:
var combinedList = [];
const list1ByManager = tempList1.reduce((a, item) => {
a[item.manager] = item;
return a;
}, {});
var combinedList = tempList2.map((item2) => ({
...list1ByManager[item2.manager],
...item2
}));
Question
Did I merge tempList1
and tempList2
incorrectly? Why are the y
s not preserved as shown in the snippet?
(In other words the parallelograms (in black) should not be all drawn at the bottom of the first column, they should attach perfectly to the corresponding companies -- forming a bridge if you will, making it easy for the eye to see the rank change in any one company by tracing the company's position in the first column to its new position in the second column)
Note: I'm just concerned with the parallelograms between column one and two for now. I'm not going to try with column three until I get this much figured out.