score:2
Accepted answer
Here's an example that resolves your three issues.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="legend"></div>
<div id="chart"></div>
<script>
// margin convention set up
const margin = { top: 20, bottom: 20, left: 20, right: 20 };
const width = 600 - margin.left - margin.right;
const height = 125 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// data
const data = [
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "a", year: 2014 },
{ label: "b", year: 2014 },
{ label: "b", year: 2014 },
{ label: "b", year: 2014 },
{ label: "c", year: 2014 },
{ label: "c", year: 2014 },
{ label: "c", year: 2014 },
{ label: "d", year: 2014 },
{ label: "d", year: 2014 },
{ label: "d", year: 2014 },
{ label: "d", year: 2014 },
{ label: "e", year: 2014 },
{ label: "e", year: 2014 },
{ label: "e", year: 2014 },
{ label: "e", year: 2014 },
{ label: "a", year: 2015 },
{ label: "a", year: 2015 },
{ label: "a", year: 2015 },
{ label: "a", year: 2015 },
{ label: "a", year: 2015 },
{ label: "a", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "b", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "c", year: 2015 },
{ label: "d", year: 2015 },
{ label: "d", year: 2015 },
{ label: "d", year: 2015 },
{ label: "d", year: 2015 },
{ label: "d", year: 2015 },
{ label: "e", year: 2015 },
{ label: "a", year: 2016 },
{ label: "a", year: 2016 },
{ label: "a", year: 2016 },
{ label: "a", year: 2016 },
{ label: "a", year: 2016 },
{ label: "a", year: 2016 },
{ label: "b", year: 2016 },
{ label: "b", year: 2016 },
{ label: "b", year: 2016 },
{ label: "b", year: 2016 },
{ label: "b", year: 2016 },
{ label: "b", year: 2016 },
{ label: "c", year: 2016 },
{ label: "c", year: 2016 },
{ label: "c", year: 2016 },
{ label: "c", year: 2016 },
{ label: "c", year: 2016 },
{ label: "d", year: 2016 },
{ label: "d", year: 2016 },
{ label: "d", year: 2016 },
{ label: "d", year: 2016 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "a", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "b", year: 2017 },
{ label: "c", year: 2017 },
{ label: "c", year: 2017 },
{ label: "c", year: 2017 },
{ label: "c", year: 2017 },
{ label: "c", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "d", year: 2017 },
{ label: "a", year: 2018 },
{ label: "a", year: 2018 },
{ label: "a", year: 2018 },
{ label: "a", year: 2018 },
{ label: "b", year: 2018 },
{ label: "b", year: 2018 },
{ label: "b", year: 2018 },
{ label: "c", year: 2018 },
{ label: "c", year: 2018 },
{ label: "c", year: 2018 },
{ label: "c", year: 2018 },
{ label: "e", year: 2018 },
{ label: "a", year: 2019 },
{ label: "a", year: 2019 },
{ label: "a", year: 2019 },
{ label: "a", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "b", year: 2019 },
{ label: "c", year: 2019 },
{ label: "c", year: 2019 },
{ label: "c", year: 2019 },
{ label: "e", year: 2019 },
{ label: "e", year: 2019 },
{ label: "f", year: 2019 },
{ label: "a", year: 2020 },
{ label: "a", year: 2020 },
{ label: "a", year: 2020 },
{ label: "a", year: 2020 },
{ label: "a", year: 2020 },
{ label: "b", year: 2020 },
{ label: "b", year: 2020 },
{ label: "b", year: 2020 },
{ label: "b", year: 2020 },
{ label: "b", year: 2020 },
{ label: "b", year: 2020 },
{ label: "c", year: 2020 },
{ label: "c", year: 2020 },
{ label: "c", year: 2020 },
{ label: "d", year: 2020 },
{ label: "d", year: 2020 },
{ label: "e", year: 2020 },
];
// map from the year to the label to the array
// of meetings for that year and label
const yearToLabelToMeetings = d3.rollup(
data,
// group is an array of all of the meetings
// that have the same year and label.
// add the y index for each meeting
group => group.map((d, i) => ({...d, y: i + 1})),
// first group by year
d => d.year,
// then group by label
d => d.label
);
// get the max number of meetings for any year and label
const maxCount = d3.max(
yearToLabelToMeetings,
([year, labelToMeetings]) => d3.max(
labelToMeetings,
([label, meetings]) => meetings.length
)
);
// sorted lists of the labels and years
const labels = [...new Set(data.map(d => d.label))].sort();
const years = [...new Set(data.map(d => d.year))].sort(d3.ascending);
// scales
// for setting the y position of the dots
const y = d3.scaleLinear()
.domain([0, maxCount])
.range([height, 0]);
// for setting the x position of the groups for the years
const yearX = d3.scaleBand()
.domain(years)
.range([0, width])
.padding(0.3);
// for setting the x position of the columns of dots
// within a year group
const labelX = d3.scalePoint()
.domain(labels)
.range([0, yearX.bandwidth()]);
// for setting the color of the dots
const color = d3.scaleOrdinal()
.domain(labels)
.range(d3.schemeCategory10);
// drawing the data
// create one group for each year and set the group's horizontal position
const yearGroups = g.selectAll('g')
.data(yearToLabelToMeetings)
.join('g')
.attr('transform', ([year, labelToMeetings]) => `translate(${yearX(year)})`);
// inside each year group, create one group for each label and set its
// horizontal position in the group
const labelGroups = yearGroups.selectAll('g')
.data(([year, labelToMeetings]) => labelToMeetings)
.join('g')
.attr('transform', ([label, meetings]) => `translate(${labelX(label)})`);
// add the dots
labelGroups.selectAll('circle')
.data(([label, meetings]) => meetings)
.join('circle')
.attr('cy', d => y(d.y))
.attr('r', 4)
.attr('fill', d => color(d.label));
// axes
// x axis
g.append('g')
// move the axis to the bottom of the chart
.attr('transform', `translate(0,${height})`)
// add the axis
.call(d3.axisBottom(yearX).tickSizeOuter(0))
// move the tick marks to be in between the groups
.call(g =>
g.selectAll('line')
.attr('x1', yearX.step() / 2)
.attr('x2', yearX.step() / 2)
// remove the last tick mark
.filter(d => d === years[years.length - 1])
.remove()
);
// y axis
g.append('g')
.call(d3.axisLeft(y));
// color legend
const size = '10px';
// create div for the legend to go in
const legend = d3.select('#legend')
.append('div')
.style('display', 'flex')
.style('font-family', 'sans-serif')
.style('font-size', size);
// create one div for each entry in the color scale
const cell = legend.selectAll('div')
.data(color.domain())
.join('div')
.style('margin-right', '1em')
.style('display', 'flex')
.style('align-items', 'center');
// add the colored square for each entry
cell.append('div')
.style('background', d => color(d))
.style('min-width', size)
.style('min-height', size)
.style('margin-right', '0.5em');
// add the text label for each entry
cell.append('div')
.text(d => d);
</script>
</body>
</html>
Source: stackoverflow.com
Related Query
- D3.js custom ticks on x axis of scatterplot
- Custom quantized ticks on axis
- Custom x axis ticks in D3 Line graph
- D3.JS change text in axis ticks to custom strings
- Howto set custom ticks and labels on Y axis in nvd3
- Custom number of axis ticks D3
- d3.js: Align text labels between ticks on the axis
- How can I get the D3.js axis ticks and positions as an array?
- D3 - using strings for axis ticks
- How do I make a custom axis formatter for hours & minutes in d3.js?
- Custom tick size on axis (d3js)
- Multiline svg text axis ticks in d3
- How to force a specific amount of y axis ticks in d3 charts?
- D3v4 time scale axis error in ticks display
- NVD3 Line Chart X Axis Ticks Are Missing
- d3.js make axis ticks clickable
- Ticks only at the start and end of an axis
- How to set fixed no. of ticks on axis in d3.js?
- Data points and ticks in the scaleBand axis are not aligned
- d3 bar chart y axis ticks and grid lines above max value
- D3 Axis ticks hard count
- Shorten months ticks on x axis
- D3 time scale axis ticks are irregular on year boundaries
- Display only values from the data set into X axis ticks
- How to render end ticks always using d3 svg axis
- NVD3 - configuring ticks on axis
- Add (second) x axis to scatterplot to display group information
- Need help lining up x axis ticks with bars in D3.js bar chart
- DC.js scatterPlot ordinal axis tick position
- custom x axis tooltip for nvd3 scatter chart
More Query from same tag
- d3 events issue with angular 2 typescript
- Javascript event listener for SVG transform
- in d3 how to put multiple paragraphs of text including hyperlink into a rect div?
- Angular 6 compile template runtime
- D3 ticks change position when changing focus
- tool tip for half donut or half pie chart
- How to group g elements in D3?
- Mouseover Event not working well with overlaid objects
- Unable to plot D3 curve with negative values
- Angular Treemap passing data from service
- D3 trouble while appending text on mouseover
- Why am I getting different results with same code in D3js?
- D3: order of selections: style and text
- geoChoroplethChart map with different fill opacity for the selected item
- D3: Multi-line graph is slightly off compared to axises when zooming in
- Graphs update only when I write enter() at the end
- Unexpected d3 v4 tree behaviour
- Clicking on node in dagre-d3
- Attribute cy returning expected length error in D3 V4
- Multiple force-layout graphs with d3 in seperate svg/div's
- how to improve the drilling performance of d3 sunburst chart when there are too many paths? like dynamically show partial levels?
- Returning array from d3.json()
- How to manipulate specific SVG Paths in D3.js
- React and D3: Cannot read property 'X' of undefined
- Introducing Arrow(directed), in Force Directed Graph d3
- d3 fitSize gives NaN values
- Removing the raised corners from d3.js axes to create flat axis
- Multiple NVD3 charts on page. How to simplify the javascript code and wrap function?
- Adjusting node position in force-directed graph
- DC.js computing mean according to dimension