[英]How to create line chart with JSON data using D3
我正在使用 D3.js 和 react-hooks 來創建圖表,所以我嘗試通過四處搜索並得到一個Line
圖來創建一個Line
圖。
resize-observer-polyfill
這個庫使圖表具有響應性。我做了什么
const svgRef = useRef();
const wrapperRef = useRef();
const dimensions = useResizeObserver(wrapperRef); // for responsive
// will be called initially and on every data change
useEffect(() => {
const svg = select(svgRef.current);
const { width, height } =
dimensions || wrapperRef.current.getBoundingClientRect();
// scales + line generator
const xScale = scaleLinear()
.domain([0, data.length - 1]) // here I need to pass the data
.range([0, width]);
const yScale = scaleLinear()
.domain([0, max(data)])
.range([height, 0]);
const lineGenerator = line()
.x((d, index) => xScale(index))
.y((d) => yScale(d))
.curve(curveCardinal);
// render the line
svg
.selectAll('.myLine')
.data([data])
.join('path')
.attr('class', 'myLine')
.attr('stroke', 'black')
.attr('fill', 'none')
.attr('d', lineGenerator);
svg
.selectAll('.myDot')
.data(data)
.join('circle')
.attr('class', 'myDot')
.attr('stroke', 'black')
.attr('r', (value, index) => 4)
.attr('fill', (value, index) => 'red')
.attr('cx', (value, index) => xScale(index))
.attr('cy', yScale);
// axes
const xAxis = axisBottom(xScale);
svg
.select('.x-axis')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
const yAxis = axisLeft(yScale);
svg.select('.y-axis').call(yAxis);
}, [data, dimensions]);
<React.Fragment>
<div ref={wrapperRef} style={{ marginBottom: '2rem' }}>
<svg ref={svgRef}>
<g className="x-axis" />
<g className="y-axis" />
</svg>
</div>
</React.Fragment>
這里我無法傳遞數據,我的數據在下面
[
{ anno: 2014, consumo: 300, color: "#ff99e6" },
{ anno: 2015, consumo: 290, color: "blue" },
{ anno: 2016, consumo: 295, color: "green" },
{ anno: 2017, consumo: 287, color: "yellow" },
{ anno: 2018, consumo: 282, color: "red" },
{ anno: 2019, consumo: 195, color: "white" }
]
在我的數據中,每個數據都有顏色,我想在生成的每個點中顯示顏色。
同樣,我嘗試制作條形圖,效果很好
我對標簽做了一些動態渲染,當我們調整窗口大小時,標簽會自動調整。
我還評論了我正在做什么的行。
編輯/更新
我一直在關注@MGO 的回答,它對我幫助很大,但我仍然面臨對齊標簽和將顏色填充為點的問題。
實際上很明顯它會因為文本大小而重疊,但只是為了克服在條形圖中我使用了下面的代碼
const tickWidth = 40;
const width = xScaleLabels.range()[1];
const tickN = Math.floor(width / tickWidth);
const keepEveryNth = Math.ceil(xScaleLabels.domain().length / tickN);
const xScaleLabelDomain = xScaleLabels
.domain()
.filter((_, i) => i % keepEveryNth === 0);
xScaleLabels.domain(xScaleLabelDomain);
它所做的是當設備尺寸較小時,它會過濾一些標簽並且不會顯示標簽。
而且我正在使用下面的代碼來提供顏色
.attr("fill", ({ color }) => color)
但是它不采用任何顏色,而是默認采用黑色。
我有數據作為標簽顯示為July.9.2021 11:18:28
但我只想顯示時間所以我在條形圖代碼中所做的事情如下
const xScaleLabels = scaleBand()
.domain(
data.map(
({ sensorValueAddedTime }) => sensorValueAddedTime.split(' ')[2] // this I am using to show only time
)
)
.range([0, dimensions.width])
.padding(padding);
同樣,我正在嘗試使用折線圖,以簡單的方式,我不想將其更改為任何時候。
這是第二個數據,所以基本上我得到的答案只適用於第一個數據而不適用於第二個數據,如果數據以我想顯示的任何這些格式出現,我希望它是動態的。
sensorValueAddedTime
我想在 x 軸上顯示sensorValue
On y 軸
我已經添加了我的條形圖完整的工作代碼,我想用折線圖做同樣的事情。
[
{
sensorValue: 32,
sensorValueAddedTime: "July.9.2021 10:56:22",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 32,
sensorValueAddedTime: "July.9.2021 10:56:23",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 35,
sensorValueAddedTime: "July.9.2021 11:17:51",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 35,
sensorValueAddedTime: "July.9.2021 11:17:52",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 36,
sensorValueAddedTime: "July.9.2021 11:18:08",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 36,
sensorValueAddedTime: "July.9.2021 11:18:09",
color_code: null,
condition: null,
__typename: "sensorData"
},
{
sensorValue: 38,
sensorValueAddedTime: "July.9.2021 11:18:27",
condition: null,
color_code: null,
__typename: "sensorData"
},
{
sensorValue: 38,
sensorValueAddedTime: "July.9.2021 11:18:28",
condition: null,
color_code: null,
__typename: "sensorData"
}
]
在帶有 React Hooks 視頻系列data
Muri 的D3的原始示例中,是一個扁平的一維數組: [10, 25, 30, 40, 25, 60]
。
在您提供的新數組 ( data1
) 中,我們要求 D3 基於對象數組呈現圖表。 所以當我們將data1
作為 prop 傳遞給我們的圖表函數時,我們需要做更多的工作來訪問我們對象數組中的值。
我們需要將數據值縮放到適當的范圍內。 原始示例按數組中的索引查找值。 我們將需要根據這些對象的鍵訪問包含在data1
數組中的對象中的值。 像這樣,創建我們的秤:
const yScale = scaleLinear()
.domain([0, max(data, (d) => d.sensorValue)])
.range([height, 0]);
// scale the values of sensorValueAddedTime into the range for our x axis values
// since it's a date you'll need to convert the strings to Date objects:
const xScale = scaleTime()
.domain([
new Date(min(data, (d) => d.sensorValueAddedTime)),
new Date(max(data, (d) => d.sensorValueAddedTime))
])
.range([0, width]);
就像這樣,在我們的lineGenerator
函數中:
const lineGenerator = line()
.x((d) => xScale(new Date(d.sensorValueAddedTime))
.y((d) => yScale(d.sensorValue));
您會在上面注意到,如果我們要使用scaleTime()
,我們必須將sensorValueAddedTime
轉換為 D3 可以理解的值 - 現在您的對象中的字符串中有一個額外的空間,但是如果你d3.scaleTime()
可以轉換為 Date 對象並使用d3.scaleTime()
。
有一個更新的、有效的 Sandbox,可將這些數據呈現為帶有附加注釋的折線圖。
更新:OP 要求繪制一組不同的數據,並要求這些點“按該順序出現”。
這里有一個不同的工作沙箱,可以將這組不同的數據正確呈現為折線圖。
這似乎是您所要求的 - “條形圖的折線圖版本”可能不會產生您想要的東西。 簡單說一下:
如果您使用 xScale 上每個對象的索引位置,如下所示:
const xScale = scaleLinear()
.domain([0, data.length - 1])
.range([0, width]);
這將產生一個非常無聊的圖表——因為你說“在每個代表數組索引的整數上放一個點”。 換句話說,在 1、2、3 處的一個點,依此類推。
對於這個圖表,我們應該在我們的數據中選擇有意義的值——比如data1.anno
並使用這些值進行通信。
例如:
根據數組的索引位置將值縮放到范圍內,並根據它們的索引位置繪制它們,如下所示:
const xScale = scaleLinear()
.domain([0, data.length - 1])
.range([0, width]);
....chart code....
.attr("cx", (value, index) => xScale(index))
產生這個情節:
解決方案
將數據的值縮放到范圍內並根據它們的值繪制點。 我們可以使用d3 的extent
方法,如下所示:
const xScale = scaleLinear()
.domain(extent(data, (d) => d.anno))
.range([0, width]);
const yScale = scaleLinear()
.domain([0, max(data, (d) => d.consumo)])
.range([height, 0]);
....chart code...
.attr("cx", (value) => xScale(value.anno))
.attr("cy", (value) => yScale(value.consumo));
產生這個情節:
data1.anno
顯然是一年。 將年份解析為日期對象的選項是一個不錯的選擇。 var parse = d3.timeParse("%Y")
,然后使用它來設置您的時間刻度的域: .domain([parse(minDate), parse(maxDate)])
, 如下所示。
否則,如果您想在 x 軸上保留該值而不將其視為 Date 對象,則可以使用 Javascript 的toFixed()
方法刪除小數位,使用d3.format
刪除逗號分隔符並更改隨axis.ticks()
出現的刻度數。
這產生了這個圖表:
要使用這些點生成一條線,請使用數據中的值,而不是索引。
const lineGenerator = line()
// .x((d, index) => xScale(index)) // old
// .y((d) => yScale(d)) // old
.x((d) => xScale(d.anno)) // new
.y((d) => yScale(d.consumo)) // new
.curve(curveCardinal);
這產生了這個圖表:
最后,如果您的目標是將每個對象中的顏色值分配給一個點,請訪問這些值並將它們的值分配給填充,如下所示:
.attr("fill", (value) => value.color)
這產生了這個圖表:
希望這可以幫助! ✌️
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.