Public
Edited
Apr 22, 2023
Insert cell
Extract_Forbes_50__2122170147_edited@3.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
df = FileAttachment("EditedDataset@3.csv").csv({typed:true})
Insert cell
Insert cell
Insert cell
highcategory = df.filter(d => d["category"] == "High")
Insert cell
lowcategory = df.filter(d => d["category"] == "Low")
Insert cell
mediumcategory = df.filter(d => d["category"] == "Medium")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// basic structure of this section from https://observablehq.com/@mikatalk/scrollable-d3-timeline
// changed much within, but the structure is similar as I used it to build off of
chart = {
const globalScale = width / 5000

const svg = d3.create('svg')
.attr('viewBox', [0, 0, width+350, height+100]) //changed scope of viewbox
.attr('fill', 'transparent')

// Append a rectangle to the SVG as the background
svg.append("rect")
.attr("width", "100%") // Set the width to 100% of the SVG container
.attr("height", "100%") // Set the height to 100% of the SVG container
.attr("fill", "transparent"); // Set the fill color of the rectangle to light gray
svg // added a container for the legend
.append('rect')
.attr('width', 235)
.attr('height', 70)
.attr('x', 5)
.attr('y', 5)
.attr('fill', '#636465')
svg // added color swatch for NLP to legend
.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('x', 10)
.attr('y', 10)
.attr('fill', '#9547D2')
svg // added color swatch for CV to legend
.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('x', 10)
.attr('y', 30)
.attr('fill', '#E6FA03')
svg // added color swatch for VL to legend
.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('x', 10)
.attr('y', 50)
.attr('fill', '#6FF803')
svg // added text for NLP to legend
.append('text')
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '12px')
.attr("fill", '#D1D5DB')
.attr('x', 30)
.attr('y', 22.5)
.text("Funding above $1B")
svg // added text for CV to legend
.append('text')
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '12px')
.attr("fill", '#D1D5DB')
.attr('x', 30)
.attr('y', 42.5)
.text("Funding between $500M and $1B")
svg // added text for VL to legend
.append('text')
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '12px')
.attr("fill", '#D1D5DB')
.attr('x', 30)
.attr('y', 62.5)
.text("Funding below $500M")

const g = svg.append('g')
.attr('cursor', 'grab')
g.selectAll('rect') // creates line for timeline
.data(epochs)
.join('rect')
.attr('x', ({from}) => from)
.attr('y', (height/1.5)+100)
.attr('width', ({duration}) => duration)
.attr('height', height/50)
.attr('fill', (d, i) => d3.interpolateGreens(1-(i/20))) //changed color and range
.on('click', (event) => {
console.log('Clicked', event)
})
const groupEventLabels = svg.append('g')
.attr('class', 'event-labels')
.attr('cursor', 'grab')

const dur = 400
const eventLabels = groupEventLabels.selectAll('event-labels')
.data(highcategory) //changed data
.join('g')
.attr('transform', ({Time}) => 'translate('+ Time * globalScale +' '+height/2+')')
eventLabels.append('circle') // changed appearance and placement of circles
.attr('cx', 0)
.attr('cy', 225)
.attr('r', 5)
.style('opacity', 0)
.attr('transform-origin', '50% 50%')
.attr('stroke', '#9547D2')
.attr('stroke-width', 2)
.attr('fill', '#9547D2')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+4) * dur)
.style('opacity', 1);
eventLabels.append('text') // changed appearance and placement of text
.attr('x', 10)
.attr('y', 30+100)
.style('opacity', 0)
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'middle')
.text(d => d.Name)
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '14px')
.attr("fill", '#9547D2')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+4) * dur)
.style('opacity', 1);
eventLabels.append('rect') // changed appearance and placement of lines
.attr('x', -0.5)
.attr('y', 21+100)
.style('opacity', 0)
.attr('width', 1)
.attr('height', height*0.5)
.attr('stroke', '#D1D5DB')
.attr('stroke-width', 0)
.attr('fill', '#9547D2')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+4) * dur)
.style('opacity', 1);

const eventLabels2 = groupEventLabels.selectAll('event-labels') // added group due to more data
.data(mediumcategory) // changed data
.join('g')
.attr('transform', ({Time}) => 'translate('+ Time * globalScale +' '+height/2+')')
eventLabels2.append('circle') // changed appearance and placement of circles
.attr("cy", 225)
.attr('cx', 0)
.style('opacity', 0)
.attr('r', 5)
.attr('transform-origin', '50% 50%')
.attr('stroke', '#E6FA03')
.attr('stroke-width', 2)
.attr('fill', '#E6FA03')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => i * (dur-100))
.style('opacity', 1);
eventLabels2.append('text') // changedchanged appearance and placement of text
.attr('x', 10)
.attr('y', 51+100)
.style('opacity', 0)
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'middle')
.text(d => d.Name)
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '14px')
.attr("fill", '#E6FA03')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => i * (dur-100))
.style('opacity', 1);
eventLabels2.append('rect') // changchanged appearance and placement of lines
.attr('x', -0.5)
.attr('y', 45+100)
.style('opacity', 0)
.attr('width', 1)
.attr('height', height*0.375)
.attr('stroke', '#D1D5DB')
.attr('stroke-width', 0)
.attr('fill', '#E6FA03')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => i * (dur-100))
.style('opacity', 1);

const eventLabels3 = groupEventLabels.selectAll('event-labels') // added group due to more data
.data(lowcategory) // changed data
.join('g')
.attr('transform', ({Time}) => 'translate('+ Time * globalScale +' '+height/2+')')
eventLabels3.append('circle') // changed appearance and placement of circles
.attr('cx', 0)
.attr('cy', 225)
.style('opacity', 0)
.attr('r', 5)
.attr('transform-origin', '50% 50%')
.attr('stroke', '#6FF803')
.attr('stroke-width', 2)
.attr('fill', '#6FF803')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+14) * dur)
.style('opacity', 1);
eventLabels3.append('text') // changed appearance and placement of text
.attr('x', 10)
.attr('y', 76+100)
.style('opacity', 0)
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'middle')
.text(d => d.Name)
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '14px')
.attr("fill", '#6FF803')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+14) * dur)
.style('opacity', 1);
eventLabels3.append('rect') // changed appearance and placement of lines
.attr('x', -0.5)
.attr('y', 70+100)
.style('opacity', 0)
.attr('width', 1)
.attr('height', height*0.25)
.attr('stroke', '#6FF803')
.attr('stroke-width', 0)
.attr('fill', '#6FF803')
.transition() // this transition makes the data points appear roughly in order
.duration(dur)
.ease(d3.easeLinear)
.delay((d, i) => (i+14) * dur)
.style('opacity', 1);
const groupTimeLabels = svg.append('g')
.attr('class', 'time-labels')
.attr('cursor', 'grab')
const timeLabels = groupTimeLabels.selectAll('time-labels') // adding dates to the timeline
.data(timeLabelsData)
.join('g')
.attr('transform', ({Time}) => 'translate('+ Time * globalScale +' '+height/2+')')
timeLabels.append('text') // adds the labels
.attr('x', 10)
.attr('y', (height - 20)+100)
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'middle')
.text(d => d.text)
.attr('font-family', 'Verdana, sans-serif')
.attr("font-size", '14px')
.attr("fill", '#D1D5DB')
timeLabels.append('rect') // adds the lines down from the timeline to the dates
.attr('x', -0.5)
.attr('y', (height*0.62)+100)
.attr('width', 1)
.attr('height', height*0.31)
.attr('stroke', '#D1D5DB')
.attr('stroke-width', 0)
.attr('fill', '#D1D5DB')
timeLabels.append('rect') // added line underneath dates for style
.attr('x', -0.5)
.attr('y', (height*0.93)+100)
.attr('width', width*0.05)
.attr('height', 1)
.attr('stroke', '#D1D5DB')
.attr('stroke-width', 0)
.attr('fill', '#D1D5DB')


const zoom = d3.zoom() // zoom interaction
.extent(extent)
.scaleExtent(scaleExtent)
.translateExtent(translateExtent)
.on('zoom', () => {
const {k, x, y} = d3.event.transform
g.attr('transform', 'translate(' + x + ' 0) scale(' + (k) + ' 1)')
eventLabels.attr('transform', ({Time}) => 'translate(' + (x + Time * k) + ' '+ 10 +')')
eventLabels2.attr('transform', ({Time}) => 'translate(' + (x + Time * k) + ' '+ 10 +')') // added zoom interaction for data
eventLabels3.attr('transform', ({Time}) => 'translate(' + (x + Time * k) + ' '+ 10 +')') // added zoom interaction for data
timeLabels.attr('transform', ({time}) => 'translate(' + (x + time * k) + ' '+ 10 +')')
})
svg.call(zoom)
svg.call(zoom.scaleBy, globalScale)
return svg.node();
}
Insert cell
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