Published
Edited
Feb 7, 2021
Importers
Insert cell
md`# Bar data`
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell
import {select} from "@jashkenas/inputs"
Insert cell
Insert cell
Insert cell
duration=1000
Insert cell
formatDate = d3.utcFormat("%Y-%m-%d")
Insert cell
viewof keyframe = Scrubber(timef, {
format: (date) => date,
delay: duration,
loop: false
})
Insert cell
keyframe
Insert cell
d3=require('d3@6')
Insert cell
data_load = d3.csv('https://ann-cors.herokuapp.com/https://docs.google.com/spreadsheets/d/e/2PACX-1vSSbzPs4_34pfTJmr41be7q2dZIZb5MJyfqN3AdwYIdP78stoFhnn1EP0KgrtWPVwnFnyaVcLA1pIM9/pub?gid=2131265611&single=true&output=csv')
Insert cell
data={return data_load.map(obj => {
let rObj = {}
rObj.value = (obj.value_fill=='-50')? 0 :(+obj.value_fill)
rObj.Ticker=obj.Ticker
rObj.date=obj.date
rObj['Model Type']=obj['Model Type']
return rObj
})}
Insert cell
names = new Set(data.map(d => d.Ticker))
Insert cell
timeparser = d3.timeParse("%m/%d/%Y")
Insert cell
Insert cell
Insert cell
data_p2[360]
Insert cell
cal=(key)=>{
let ka, a, kb, b;
let t,data_between,date_between;
for ([[ka, a], [kb, b]] of data_p2){
if ((key.getTime() >ka.getTime() )&(key.getTime() <kb.getTime() )){
t= (key-ka)/(kb-ka)
data_between = rank(name => ((a.get(name)|| 0) * (1 - t) + (b.get(name) || 0) * t))
date_between = new Date(ka * (1 - t) + kb * t)
console.log(ka,kb,t,a,b,data_between,date_between)
return [date_between,data_between]
}
if (ka.getTime()===key.getTime()){
return [ka,rank(name => a.get(name))]
}
else if (kb.getTime()===key.getTime()){
return [kb,rank(name => b.get(name))]
}
}
}
Insert cell
Insert cell
Insert cell
Insert cell
single_data={
let new_Data=[];
new_Data[0]=current_data[1][0]
new_Data[1]=current_data[1][1].map(obj=>{
let newobj={}
newobj.name=obj.name
newobj.value2=obj.value2
newobj.cur_rank=obj.rank
newobj.cur_value=obj.value
newobj.pre_value=current_data[0][1].filter(d=>d.name==obj.name)[0].value
newobj.pre_rank=current_data[0][1].filter(d=>d.name==obj.name)[0].rank
newobj.next_value=current_data[2][1].filter(d=>d.name==obj.name)[0].value
newobj.next_rank=current_data[2][1].filter(d=>d.name==obj.name)[0].rank
return newobj
})
return new_Data
}
Insert cell
Insert cell
Insert cell
Insert cell
function rank(value) {
const data = Array.from(names, name => ({name, value: value(name)}));
data.map(d=>d.value2 = (d.value==0?-50:d.value))
data.sort((a, b) => d3.descending(a.value2, b.value2));
for (let i = 0; i < data.length; ++i) data[i].rank = Math.min(i,n);
return data;
}
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

const updateBars = bars(svg);
// const updateAxis = axis(svg);
// const updateLabels = labels2(svg)
// const updateTicker = ticker(svg);

invalidation.then(() => svg.interrupt());
return Object.assign(svg.node(), {
update(da) {
const transition = svg.transition()
.duration(duration)
.ease(d3.easeLinear);

// Extract the top bar’s value.
const data_keyframe=da[1].slice(0,n)
const min_x=d3.min(data_keyframe.map(d=>d.cur_value))<0?Math.ceil(d3.min(data_keyframe.map(d=>d.cur_value))/(-0.1))*(-0.1):0
const max_x = Math.ceil (da[1][0].cur_value/0.1)*0.1
x.domain([min_x,max_x]);
// const key_length= data_keyframe.filter(d=>d.value2!=-50).length
// console.log(key_length,n)
// if (key_length<n){
// y.domain(d3.range(key_length + 1))
// y2.domain(data_keyframe.filter(d=>d.value2!=-50).map(d=>d.name))
// }
// else
// {y.domain(d3.range(n + 1))
// y2.domain(data_keyframe.map(d=>d.name))
// }
// updateAxis(da, transition);
updateBars(da, transition);
// updateLabels(da, transition);
// updateTicker(da, transition);
}
});
}
Insert cell
y2 = y.copy()
Insert cell
y = d3.scaleBand()
.domain(d3.range(n + 1))
.range([margin.top, margin.top +600])
.padding(0.3)
Insert cell
x = d3.scaleLinear([0, 1], [margin.left, width - margin.right])
Insert cell
color_scale = d3.scaleOrdinal().domain(['Quant','Quark','Quint']).range(['green','blue','orange'])
Insert cell
margin = ({top: 35, right: 45, bottom: 6, left: 45})
Insert cell
color = {
const scale = d3.scaleOrdinal().domain(['Quant','Quark','Quint']).range(['green','blue','orange']);
const categoryByName = new Map(data.map(d => [d.Ticker, d['Model Type']]))
return d => scale(categoryByName.get(d.name))
}
Insert cell
function bars(svg) {
let bar = svg.append("g")
.attr("fill-opacity", 0.6)
.selectAll("rect");

return ([date, data], transition) => bar = bar
.data(data.slice(0, n), d => d.name)
.join(
enter =>
enter.append("rect")
.attr("fill", color)
.attr("height", y.bandwidth())
.attr("x", d => d.pre_value <0? x(d.pre_value): x(0))
.attr("y", d => y(d.pre_rank))
.attr("width", d => Math.abs(x(d.pre_value) - x(0))),
update =>
update.transition(transition)
.attr("x", d => d.cur_value<0? x(d.cur_value): x(0))
.attr("y", d => y(d.cur_rank))
.attr("width", d => Math.abs( x(d.cur_value) - x(0))),
exit =>
exit.transition(transition)
.attr("x", d => d.next_value<0? x(d.next_value): x(0))
.attr("y", d => y(n))
.attr("width", d => Math.abs( x(d.next_value) - x(0)))
.remove()
)}
// .join(enter => enter.append("rect")
// .attr("fill", color)
// .attr("height", y.bandwidth())
// .attr("x", d =>d.pre_value <0? x(d.pre_value): x(0))
// .attr("y", d => y(d.pre_rank))
// .attr("width", d => Math.abs(x(d.pre_value) - x(0))),
// update => update.transition(transition)
// .attr("x", d => d.cur_value<0? x(d.cur_value): x(0))
// .attr("y", d => y(d.cur_rank))
// .attr("width", d => Math.abs( x(d.cur_value) - x(0))),
// exit => exit.transition(transition).remove()
// .attr("x", d => d.next_value<0? x(d.next_value): x(0))
// .attr("y", d => y(n))
// .attr("width", d => Math.abs( x(d.next_value) - x(0)))
// )
// .call(bar => bar.transition(transition)
// .attr("x", d => d.cur_value<0? x(d.cur_value): x(0))
// .attr("y", d => y(d.cur_rank))
// .attr("width", d => Math.abs( x(d.cur_value) - x(0)))
// );
// }
Insert cell
function labels(svg) {
let label = svg.append("g")
.attr("text-anchor", "end")
.selectAll("text");
return ([date, data], transition) => label = label
.data(data.slice(0, n), d => d.name)
.join(
enter => enter.append("text")
.attr("transform", d => `translate(${x(0)},${y((prev.get(d) || d).rank)})`)
.attr("y", y.bandwidth() / 2)
.attr("x", 0)
.attr("dy", "0.35em")
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.text(d => d.name.substring(0, d.name.length - 2))
.call(text =>
text.append("tspan").attr("dx", d=> ((prev.get(d) || d).value>0?(x((prev.get(d) || d).value)-x(0))+(y.bandwidth()>14? 12: y.bandwidth())*4:(x((prev.get(d) || d).value)-x(0))))
.attr("fill-opacity", 0.7)
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.attr("x", 0)
.attr("dy", "0.1em")
),

update => update,
exit => exit.transition(transition).remove()
// .attr("transform", d => `translate(${x((next.get(d) || d).value)},${y((next.get(d) || d).rank)})`)
.attr("transform", d => `translate(${x(0)},${y((next.get(d) || d).rank)})`)
.call(g => g.select("tspan")
.attr("dx", d => (next.get(d) || d).value>0? (x((next.get(d) || d).value)-x(0))+(y.bandwidth()>14? 12: y.bandwidth())*4:(x((next.get(d) || d).value)-x(0)))
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.tween("text", d => textTween(d.value, (next.get(d) || d).value)))
)
.call(bar => bar
// I added this line
.attr("transform", d => (prev.get(d)|n>100)?`translate(${x(0)},${y((prev.get(d) || d).rank)})`:`translate(${x(0)},${y(d.rank)})`)
.transition(transition)
.attr("transform", d => `translate(${x(0)},${y(d.rank)})`)
// .attr("dy",d=>y(d.rank)-y((prev.get(d) || d).rank))
.attr("y", y.bandwidth() / 2)
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.attr('x',0)
.call(g => g.select("tspan")
.attr("dx", d => d.value>0?(x(d.value)-x(0)+(y.bandwidth()>14? 12: y.bandwidth())*4):(x(d.value)-x(0)))
.tween("text", d => textTween((prev.get(d) || d).value, d.value))
.attr("font-weight", "bold")
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
));
}
Insert cell
function labels2(svg) {
let label = svg.append("g")
.attr("text-anchor", "end")
.selectAll("text");

return ([date, data], transition) => label = label
.data(data.slice(0, n), d => d.name)
.join(
enter => enter.append("text")
.attr("transform", d => `translate(0,${y((prev.get(d) || d).rank)})`)
.attr("y", y.bandwidth() / 2)
.attr("x", x(0))
.attr("dy", "0.35em")
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.text(d => d.name.substring(0, d.name.length - 2))
.call(text => text.append("tspan")
.attr("dx", d=> ((prev.get(d) || d).value>0?(x((prev.get(d) || d).value)-x(0))+(y.bandwidth()>14? 12: y.bandwidth())*4:(x((prev.get(d) || d).value)-x(0))))
.attr("fill-opacity", 0.7)
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr('font-weight','bold')
.attr("x", x(0))
.attr("dy", "0.1em")
)
,
update => update,
exit => exit.transition(transition).remove()
.attr("transform", d => `translate(0,${y((next.get(d) || d).rank)})`)
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr("x", x(0))
.call(g => g.select("tspan")
.attr("dx", d => (next.get(d) || d).value>0? (x((next.get(d) || d).value)-x(0))+(y.bandwidth()>14? 12: y.bandwidth())*4:(x((next.get(d) || d).value)-x(0)))
.tween("text", d => textTween(d.value, (next.get(d) || d).value))
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr("x", x(0))
)
)
.call(bar => bar.transition(transition)
.attr("transform", d => `translate(0,${y(d.rank)})`)
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr("x", x(0))
.call(g => g.select("tspan")
.attr("dx", d => d.value>0?(x(d.value)-x(0)+(y.bandwidth()>14? 12: y.bandwidth())*4):(x(d.value)-x(0)))
.tween("text", d => textTween((prev.get(d) || d).value, d.value))
.attr("font-size", y.bandwidth()>14? 14: y.bandwidth())
.attr("x", x(0))
)
)
}
Insert cell
function textTween(a, b) {
const i = d3.interpolateNumber(a, b);
return function(t) {
if (i(t)>10){this.textContent = d3.format(".0%")(i(t));}
else if( i(t)>1){this.textContent = d3.format(".1%")(i(t));}
else {this.textContent = formatNumber(i(t));}
};
}
Insert cell
function axis(svg) {
const g = svg.append("g")
.attr("transform", `translate(0,${margin.top})`)
.attr('font-weight','bold')
.attr('font-size',12)
// const g_y = svg.append("g")
// .attr("transform", `translate(${margin.left},0)`);
const axis = d3.axisTop(x)
.ticks(width / 80)
.tickSizeOuter(0)
.tickSizeInner(-barSize * (n + y.padding()));
g.call(axis)
const axitxt=g.append('g').attr('class','textaxis')
.attr("text-anchor", "end")
.attr('fill','black')
axitxt.append("text")
.attr("y",0)
.attr("x", width)
.attr("font", "18px")
.attr('fill','black')
.attr('font-weight','bold')
// .attr("dx","-1em")
// .attr("dy", "1em")
.text("Increase");
return (_, transition) => {
g.transition(transition).call(axis);
// g_y.transition(transition).call(axis_y);

// g.select(".tick:first-of-type text").remove();
g.selectAll(".tick line").attr("stroke", "none");
// :not(:first-of-type)
g.select(".domain").remove();
};
}
Insert cell
function ticker(svg) {
const now = svg.append("text")
.attr('class','ticker')
.attr("font-size", 48)
// .attr("font-variant-numeric", "tabular-nums")
.attr("text-anchor", "end")
.attr("x", width - 6)
.attr("y", height-barSize-margin.bottom)
.attr("dy", "0.32em")
.text(formatDate(keyframe));

return ([date], transition) => {
transition.end().then(() => now.text(formatDate(date)));
};
}
Insert cell
formatNumber = d3.format(".2%")
Insert cell
height = margin.top + 600 + margin.bottom
Insert cell
barSize = 48
Insert cell
update = chart.update(single_data)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more