Reputation: 21
I am a working on multiline chart using d3 version7 , I have data like
data1: sample1[] = [
{
symbol: 'a',
x1: '2022-01-01',
y1: 150
},
{
symbol: 'c',
x1: '2022-01-01',
y1: 300
},
{
symbol: 'a',
x1: '2022-01-02',
y1: 200
},
{
symbol: 'c',
x1: '2022-01-02',
y1: 700
},
{
symbol: 'a',
x1: '2022-01-03',
y1: 750
},
{
symbol: 'c',
x1: '2022-01-03',
y1: 100
},
];
In X-axis I want to display x1 as string and group with a symbol. I am able to plot x-axis and y-axis from below code
const x = d3.scaleBand()
.domain(this.data1.map((d: any) => { return d.x1 }))
.range([0, this.width]);
this.svg.append("g")
.attr("transform", `translate(0, ${this.height})`)
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
const y = d3.scaleLinear()
.domain([0, d3.max(this.data1, ((d: any) => {
return d.y1;[![enter image description here][1]][1]
}))])
.range([this.height, 0]);
this.svg.append("g")
.call(
d3.axisLeft(y)
);
And It looks like this screenshot of axis
Now When draw a multiple line using this code
const dataNest = Array.from(
d3.group(this.data1, d => d.symbol), ([key, value]) => ({ key, value })
);
var legendSpace = this.width / dataNest.length; // spacing for the legend
dataNest.forEach((d1: any, i: any) => {
const xScale = d3
.scaleBand()
.domain(d1.value.map((d: any) => { return d.x1 }))
// .domain([0, d3.max(this.data1.map((d: any) => d.x1))])
.range([0, this.width])
// y-scale
const yScale = d3
.scaleLinear()
.domain([0, d3.max(this.data1.map((d: any) => d.y1))])
.range([this.height, 0]);
// Data line
const line = d3
.line()
.x((d: any) => d.x1)
.y((d: any) => yScale(d.y1));
this.svg.append("path")
.data(dataNest)
.join("path")
.attr("fill", "none")
.attr("class", "line")
.style("stroke", this.colors[i])
.attr("d", line(d1.value));
// Add the Legend
this.svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // space legend
.attr("y", this.height + (this.margin.bottom / 2) + 15)
.attr("class", "legend") // style the legend
.style("fill", this.colors[i])
.text(d1.key);
});
I am not able to plot any line and I am getting an error saying, Error: attribute d: Expected number, "MNaN,160LNaN,146.…". So anyone have solution for displaying the multiline chart.
Upvotes: 1
Views: 1899
Reputation: 585
In your line generator function, you don't use the xScale
to convert values to pixels.
const line = d3
.line()
.x((d: any) => d.x1)
.y((d: any) => yScale(d.y1));
should be
const line = d3
.line()
.x((d: any) => xScale(d.x1))
.y((d: any) => yScale(d.y1));
A few additional notes:
forEach
you define a new xScale
and yScale
that are identical to the existing x
and y
used for rendering the axes. Just use x
and y
.line
generator, which should be the same for all lines.forEach
on the dataNest
array? This is what the d3 data join is for as worked out in the snippet below.scaleBand
is for categorical data and something like bar charts. You have dates and should use scaleTime
. If you really don't want to use a time scale, stick to scalePoint
for line charts as the bandwidth is fixed to zero.const data1 = [
{symbol: 'a', x1: '2022-01-01', y1: 150},
{symbol: 'c', x1: '2022-01-01', y1: 300},
{symbol: 'a', x1: '2022-01-02', y1: 200},
{symbol: 'c', x1: '2022-01-02', y1: 700},
{symbol: 'a', x1: '2022-01-03', y1: 750},
{symbol: 'c', x1: '2022-01-03', y1: 100},
];
const width = 500,
height = 180;
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
const x = d3.scalePoint()
.domain(data1.map(d => d.x1))
.range([30, width-30]);
svg.append("g")
.attr("transform", `translate(0, ${height-30})`)
.call(d3.axisBottom(x).ticks(5));
const y = d3.scaleLinear()
.domain([0, d3.max(data1, d => d.y1)])
.range([height-30, 0]);
svg.append("g")
.attr("transform", `translate(30, 0)`)
.call(d3.axisLeft(y));
const dataNest = Array.from(
d3.group(data1, d => d.symbol), ([key, value]) => ({ key, value })
);
const line = d3.line()
.x(d => x(d.x1))
.y(d => y(d.y1));
svg.selectAll("path.line")
.data(dataNest)
.join("path")
.attr("class", "line")
.attr("fill", "none")
.style("stroke", d => d.key === 'a' ? 'blue' : 'red')
.attr("d", d => line(d.value));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
Upvotes: 1