D3 js如何在X轴的标签中获取纯文本并制作值scaleLog
D3 js How to get plain text in label at X axis and make value scaleLog
嗨,我想出了如何在 Y 轴上添加日志标签,但无法制作图表线比例日志,而且我真的不知道如何制作 X 轴标签纯文本而不是将其转换为日期
如果我只是将 scaleLinear 更改为 scaleLog 图表线消失
同时删除 const parseTime = d3.timeParse('%Y/%m/%d');并将 parseTime(val.date) 更改为 val.date,将 scaleTime 更改为 scaleOrdinal 并不像我想的那样工作
我也不明白为什么Y轴标签这么模糊
抱歉提问,但无法了解此插件的工作原理。
使用 D3 7.2.1 和 JQ 3.5.1
(function (d3){
const lineChartData = [
{
currency: "data",
values: [
{
date: "2018/01/01",
close: 0
},
{
date: "2018/02/01",
close: 5
},
{
date: "2018/03/01",
close: 10
},
{
date: "2018/04/01",
close: 50
},
{
date: "2018/05/01",
close: 100
},
{
date: "2018/06/01",
close: 500
},
{
date: "2018/07/01",
close: 1000
},
{
date: "2018/08/01",
close: 5000
},
{
date: "2018/09/01",
close: 10000
},
]
}
];
const margin = {
top: 20,
bottom: 20,
left: 50,
right: 20
};
const width = 400 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const createGradient = select => {
const gradient = select
.select('defs')
.append('linearGradient')
.attr('id', 'gradient')
.attr('x1', '0%')
.attr('y1', '100%')
.attr('x2', '0%')
.attr('y2', '0%');
gradient
.append('stop')
.attr('offset', '0%')
.attr('style', 'stop-color:#FF6500; stop-opacity:0');
gradient
.append('stop')
.attr('offset', '100%')
.attr('style', 'stop-color:#FF6500; stop-opacity: 1');
}
const createGlowFilter = select => {
const filter = select
.select('defs')
.append('filter')
.attr('id', 'glow')
//stdDeviation is px count for make blur around main chart line
filter
.append('feGaussianBlur')
.attr('stdDeviation', '0')
.attr('result', 'coloredBlur');
const femerge = filter
.append('feMerge');
femerge
.append('feMergeNode')
.attr('in', 'coloredBlur');
femerge
.append('feMergeNode')
.attr('in', 'SourceGraphic');
}
const svg = d3.select('#line-chart')
.append('svg')
.attr('width', 700 + margin.left + margin.right)
.attr('height', 300 + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
svg.append('defs');
svg.call(createGradient);
svg.call(createGlowFilter);
const parseTime = d3.timeParse('%Y/%m/%d');
const parsedData = lineChartData.map(company => ({
ticker: company.ticker,
values: company.values.map(val => ({
close: val.close,
date: parseTime(val.date)
}))
}));
const xScale = d3.scaleTime()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.date)),
d3.max(parsedData, d => d3.max(d.values, v => v.date))
])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.close)),
d3.max(parsedData, d => d3.max(d.values, v => v.close))
])
.range([height, 0]);
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.close))
.curve(d3.curveCatmullRom.alpha(0.5));
svg.selectAll('.line')
.data(parsedData)
.enter()
.append('path')
.attr('d', d => {
const lineValues = line(d.values).slice(1);
const splitedValues = lineValues.split(',');
return `M0,${height},${lineValues},l0,${height - splitedValues[splitedValues.length - 1]}`
})
.style('fill', 'url(#gradient)')
svg.selectAll('.line')
.data(parsedData)
.enter()
.append('path')
.attr('d', d => line(d.values))
.attr('stroke-width', '2')
.style('fill', 'none')
.style('filter', 'url(#glow)')
.attr('stroke', '#FF6500');
const tick = svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale).ticks(9))
.selectAll('.tick')
.style('transition', '.2s');
//Y dashes
//stroke color of line in background
//stroke-dasharray
//first paramter is length
//second parameter is space between
tick
.selectAll('line')
.attr('stroke-dasharray', `4, 7`)
.attr('stroke', '#5E779B')
.attr('y2', `-${height}px`)
tick
.append('rect')
.attr('width', `${(width / 12) + 10}px`)
.attr('x', `-${width / 24 + 5}px`)
.attr('y', `-${height}px`)
.attr('height', `${height + 30}px`)
.style('fill', 'transparent');
svg.selectAll('.tick')
.append('circle')
.attr('r', '5px')
.style('fill', '#ffffff')
.style('stroke', '#FF6500')
.attr('cy', (x, i) => - height + yScale(parsedData[0].values[i].close));
svg.select('.domain')
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', `4, 7`)
var yscale = d3.scaleLog()
.domain([1, 100000])
.nice()
.range([height - 10, -10]);
var y_axis = d3.axisLeft(yscale);
y_axis.ticks(5);
svg.append("g")
.call(d3.axisLeft(xScale).ticks(5))
.attr("transform", "translate(0, 10)")
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', `4, 7`)
.call(y_axis)
})
(d3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<body>
<div id="line-chart"></div>
</body>
我将从对数刻度问题开始。您有两个 y 尺度,yScale
和 yscale
。第一个是线性标尺,用于线生成器并定位圆圈。第二个是仅用于轴的对数刻度。您应该只有一个 y 尺度并将其用于定位元素和轴。
此外,log scales 无法处理值 0:
As log(0) = -∞, a log scale domain must be strictly-positive or strictly-negative; the domain must not include or cross zero.
收盘值为 0 的数据点无法在对数刻度上显示。
接下来,由于您在 y 轴组上设置的“stroke”和“stroke-dasharray”属性,y 轴标签看起来不对。
最后,对于 x 尺度,将字符串转换为 Date 对象并使用 d3.scaleTime
是正确的。对于 x 轴,您可以执行以下操作:
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1), '%b');
ticks says that you want to have one tick for each month. The second argument is a date format specifier 的第一个参数定义了刻度标签的格式。 %b
在每个刻度线处放置一个缩写的月份名称。如果您希望刻度线与原始字符串具有相同的格式,那么您可以使用 %Y/%m/%d
,但您可能会发现标签太长并且相互重叠。
这是一个解决上述问题的示例。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="line-chart"></div>
<script>
// data
const lineChartData = [
{
ticker: "ABC",
values: [
{
date: "2018/01/01",
close: 1
},
{
date: "2018/02/01",
close: 5
},
{
date: "2018/03/01",
close: 10
},
{
date: "2018/04/01",
close: 50
},
{
date: "2018/05/01",
close: 100
},
{
date: "2018/06/01",
close: 500
},
{
date: "2018/07/01",
close: 1000
},
{
date: "2018/08/01",
close: 5000
},
{
date: "2018/09/01",
close: 10000
},
]
}
];
const parseTime = d3.timeParse('%Y/%m/%d');
const parsedData = lineChartData.map(company => ({
ticker: company.ticker,
values: company.values.map(val => ({
close: val.close,
date: parseTime(val.date)
}))
}));
// gradient
const createGradient = defs => {
const gradient = defs.append('linearGradient')
.attr('id', 'gradient')
.attr('x1', '0%')
.attr('y1', '100%')
.attr('x2', '0%')
.attr('y2', '0%');
gradient.append('stop')
.attr('offset', '0%')
.attr('style', 'stop-color:#FF6500; stop-opacity:0');
gradient.append('stop')
.attr('offset', '100%')
.attr('style', 'stop-color:#FF6500; stop-opacity: 1');
}
// filter
const createGlowFilter = defs => {
const filter = defs.append('filter')
.attr('id', 'glow')
//stdDeviation is px count for make blur around main chart line
filter.append('feGaussianBlur')
.attr('stdDeviation', '2')
.attr('result', 'coloredBlur');
const femerge = filter.append('feMerge');
femerge.append('feMergeNode')
.attr('in', 'coloredBlur');
femerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
}
// set up
const margin = { top: 20, right: 20, bottom: 20, left: 50 };
const width = 400 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select('#line-chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.call(svg => svg.append('defs')
.call(createGradient)
.call(createGlowFilter))
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// scales
const xScale = d3.scaleTime()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.date)),
d3.max(parsedData, d => d3.max(d.values, v => v.date))
])
.range([0, width]);
const yScale = d3.scaleLog()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.close)),
d3.max(parsedData, d => d3.max(d.values, v => v.close))
])
.range([height, 0]);
// line and area generators
const area = d3.area()
.x(d => xScale(d.date))
.y1(d => yScale(d.close))
.y0(yScale(yScale.domain()[0]))
.curve(d3.curveCatmullRom.alpha(0.5));
const line = area.lineY1();
// axes
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1), '%b');
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(xAxis)
// add vertical grid lines
.call(g =>
g.selectAll('.tick>line')
.clone()
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', '4, 7')
.attr('y0', 0)
.attr('y1', -height)
)
const yAxis = d3.axisLeft(yScale)
.ticks(5);
svg.append('g')
.call(yAxis);
// draw area, line, and circles
// create one group for each company
const companies = svg.append('g')
.selectAll('g')
.data(parsedData)
.join('g');
// add the area
companies.append('path')
.attr('d', d => area(d.values))
.attr('fill', 'url(#gradient)');
// add the line
companies.append('path')
.attr('d', d => line(d.values))
.attr('stroke-width', 2)
.attr('fill', 'none')
.attr('stroke', '#FF6500')
.attr('filter', 'url(#glow)');
// add the circles
companies.selectAll('circle')
.data(d => d.values)
.join('circle')
.attr('fill', 'white')
.attr('stroke', '#FF6500')
.attr('r', '3')
.attr('cx', d => xScale(d.date))
.attr('cy', d => yScale(d.close));
</script>
</body>
</html>
嗨,我想出了如何在 Y 轴上添加日志标签,但无法制作图表线比例日志,而且我真的不知道如何制作 X 轴标签纯文本而不是将其转换为日期
如果我只是将 scaleLinear 更改为 scaleLog 图表线消失
同时删除 const parseTime = d3.timeParse('%Y/%m/%d');并将 parseTime(val.date) 更改为 val.date,将 scaleTime 更改为 scaleOrdinal 并不像我想的那样工作
我也不明白为什么Y轴标签这么模糊
抱歉提问,但无法了解此插件的工作原理。 使用 D3 7.2.1 和 JQ 3.5.1
(function (d3){
const lineChartData = [
{
currency: "data",
values: [
{
date: "2018/01/01",
close: 0
},
{
date: "2018/02/01",
close: 5
},
{
date: "2018/03/01",
close: 10
},
{
date: "2018/04/01",
close: 50
},
{
date: "2018/05/01",
close: 100
},
{
date: "2018/06/01",
close: 500
},
{
date: "2018/07/01",
close: 1000
},
{
date: "2018/08/01",
close: 5000
},
{
date: "2018/09/01",
close: 10000
},
]
}
];
const margin = {
top: 20,
bottom: 20,
left: 50,
right: 20
};
const width = 400 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const createGradient = select => {
const gradient = select
.select('defs')
.append('linearGradient')
.attr('id', 'gradient')
.attr('x1', '0%')
.attr('y1', '100%')
.attr('x2', '0%')
.attr('y2', '0%');
gradient
.append('stop')
.attr('offset', '0%')
.attr('style', 'stop-color:#FF6500; stop-opacity:0');
gradient
.append('stop')
.attr('offset', '100%')
.attr('style', 'stop-color:#FF6500; stop-opacity: 1');
}
const createGlowFilter = select => {
const filter = select
.select('defs')
.append('filter')
.attr('id', 'glow')
//stdDeviation is px count for make blur around main chart line
filter
.append('feGaussianBlur')
.attr('stdDeviation', '0')
.attr('result', 'coloredBlur');
const femerge = filter
.append('feMerge');
femerge
.append('feMergeNode')
.attr('in', 'coloredBlur');
femerge
.append('feMergeNode')
.attr('in', 'SourceGraphic');
}
const svg = d3.select('#line-chart')
.append('svg')
.attr('width', 700 + margin.left + margin.right)
.attr('height', 300 + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
svg.append('defs');
svg.call(createGradient);
svg.call(createGlowFilter);
const parseTime = d3.timeParse('%Y/%m/%d');
const parsedData = lineChartData.map(company => ({
ticker: company.ticker,
values: company.values.map(val => ({
close: val.close,
date: parseTime(val.date)
}))
}));
const xScale = d3.scaleTime()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.date)),
d3.max(parsedData, d => d3.max(d.values, v => v.date))
])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.close)),
d3.max(parsedData, d => d3.max(d.values, v => v.close))
])
.range([height, 0]);
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.close))
.curve(d3.curveCatmullRom.alpha(0.5));
svg.selectAll('.line')
.data(parsedData)
.enter()
.append('path')
.attr('d', d => {
const lineValues = line(d.values).slice(1);
const splitedValues = lineValues.split(',');
return `M0,${height},${lineValues},l0,${height - splitedValues[splitedValues.length - 1]}`
})
.style('fill', 'url(#gradient)')
svg.selectAll('.line')
.data(parsedData)
.enter()
.append('path')
.attr('d', d => line(d.values))
.attr('stroke-width', '2')
.style('fill', 'none')
.style('filter', 'url(#glow)')
.attr('stroke', '#FF6500');
const tick = svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale).ticks(9))
.selectAll('.tick')
.style('transition', '.2s');
//Y dashes
//stroke color of line in background
//stroke-dasharray
//first paramter is length
//second parameter is space between
tick
.selectAll('line')
.attr('stroke-dasharray', `4, 7`)
.attr('stroke', '#5E779B')
.attr('y2', `-${height}px`)
tick
.append('rect')
.attr('width', `${(width / 12) + 10}px`)
.attr('x', `-${width / 24 + 5}px`)
.attr('y', `-${height}px`)
.attr('height', `${height + 30}px`)
.style('fill', 'transparent');
svg.selectAll('.tick')
.append('circle')
.attr('r', '5px')
.style('fill', '#ffffff')
.style('stroke', '#FF6500')
.attr('cy', (x, i) => - height + yScale(parsedData[0].values[i].close));
svg.select('.domain')
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', `4, 7`)
var yscale = d3.scaleLog()
.domain([1, 100000])
.nice()
.range([height - 10, -10]);
var y_axis = d3.axisLeft(yscale);
y_axis.ticks(5);
svg.append("g")
.call(d3.axisLeft(xScale).ticks(5))
.attr("transform", "translate(0, 10)")
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', `4, 7`)
.call(y_axis)
})
(d3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<body>
<div id="line-chart"></div>
</body>
我将从对数刻度问题开始。您有两个 y 尺度,yScale
和 yscale
。第一个是线性标尺,用于线生成器并定位圆圈。第二个是仅用于轴的对数刻度。您应该只有一个 y 尺度并将其用于定位元素和轴。
此外,log scales 无法处理值 0:
As log(0) = -∞, a log scale domain must be strictly-positive or strictly-negative; the domain must not include or cross zero.
收盘值为 0 的数据点无法在对数刻度上显示。
接下来,由于您在 y 轴组上设置的“stroke”和“stroke-dasharray”属性,y 轴标签看起来不对。
最后,对于 x 尺度,将字符串转换为 Date 对象并使用 d3.scaleTime
是正确的。对于 x 轴,您可以执行以下操作:
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1), '%b');
ticks says that you want to have one tick for each month. The second argument is a date format specifier 的第一个参数定义了刻度标签的格式。 %b
在每个刻度线处放置一个缩写的月份名称。如果您希望刻度线与原始字符串具有相同的格式,那么您可以使用 %Y/%m/%d
,但您可能会发现标签太长并且相互重叠。
这是一个解决上述问题的示例。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="line-chart"></div>
<script>
// data
const lineChartData = [
{
ticker: "ABC",
values: [
{
date: "2018/01/01",
close: 1
},
{
date: "2018/02/01",
close: 5
},
{
date: "2018/03/01",
close: 10
},
{
date: "2018/04/01",
close: 50
},
{
date: "2018/05/01",
close: 100
},
{
date: "2018/06/01",
close: 500
},
{
date: "2018/07/01",
close: 1000
},
{
date: "2018/08/01",
close: 5000
},
{
date: "2018/09/01",
close: 10000
},
]
}
];
const parseTime = d3.timeParse('%Y/%m/%d');
const parsedData = lineChartData.map(company => ({
ticker: company.ticker,
values: company.values.map(val => ({
close: val.close,
date: parseTime(val.date)
}))
}));
// gradient
const createGradient = defs => {
const gradient = defs.append('linearGradient')
.attr('id', 'gradient')
.attr('x1', '0%')
.attr('y1', '100%')
.attr('x2', '0%')
.attr('y2', '0%');
gradient.append('stop')
.attr('offset', '0%')
.attr('style', 'stop-color:#FF6500; stop-opacity:0');
gradient.append('stop')
.attr('offset', '100%')
.attr('style', 'stop-color:#FF6500; stop-opacity: 1');
}
// filter
const createGlowFilter = defs => {
const filter = defs.append('filter')
.attr('id', 'glow')
//stdDeviation is px count for make blur around main chart line
filter.append('feGaussianBlur')
.attr('stdDeviation', '2')
.attr('result', 'coloredBlur');
const femerge = filter.append('feMerge');
femerge.append('feMergeNode')
.attr('in', 'coloredBlur');
femerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
}
// set up
const margin = { top: 20, right: 20, bottom: 20, left: 50 };
const width = 400 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select('#line-chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.call(svg => svg.append('defs')
.call(createGradient)
.call(createGlowFilter))
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// scales
const xScale = d3.scaleTime()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.date)),
d3.max(parsedData, d => d3.max(d.values, v => v.date))
])
.range([0, width]);
const yScale = d3.scaleLog()
.domain([
d3.min(parsedData, d => d3.min(d.values, v => v.close)),
d3.max(parsedData, d => d3.max(d.values, v => v.close))
])
.range([height, 0]);
// line and area generators
const area = d3.area()
.x(d => xScale(d.date))
.y1(d => yScale(d.close))
.y0(yScale(yScale.domain()[0]))
.curve(d3.curveCatmullRom.alpha(0.5));
const line = area.lineY1();
// axes
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1), '%b');
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(xAxis)
// add vertical grid lines
.call(g =>
g.selectAll('.tick>line')
.clone()
.attr('stroke', '#5E779B')
.attr('stroke-dasharray', '4, 7')
.attr('y0', 0)
.attr('y1', -height)
)
const yAxis = d3.axisLeft(yScale)
.ticks(5);
svg.append('g')
.call(yAxis);
// draw area, line, and circles
// create one group for each company
const companies = svg.append('g')
.selectAll('g')
.data(parsedData)
.join('g');
// add the area
companies.append('path')
.attr('d', d => area(d.values))
.attr('fill', 'url(#gradient)');
// add the line
companies.append('path')
.attr('d', d => line(d.values))
.attr('stroke-width', 2)
.attr('fill', 'none')
.attr('stroke', '#FF6500')
.attr('filter', 'url(#glow)');
// add the circles
companies.selectAll('circle')
.data(d => d.values)
.join('circle')
.attr('fill', 'white')
.attr('stroke', '#FF6500')
.attr('r', '3')
.attr('cx', d => xScale(d.date))
.attr('cy', d => yScale(d.close));
</script>
</body>
</html>