当我想从同一数组(rawdata2)绘制两列(即 VALUE1 和 VALUE2)时,我可以计算负面积和正面积以及交叉点。
\nrawdata = [ { "DATE_TIME": "2018-10-31T23:20:00.000Z", "VALUE1": 102, "VALUE2": 118, "INSPECTION": 1, "STATE": 1, "minuteTime": 20 }, { "DATE_TIME": "2018-10-31T23:20:00.000Z", "VALUE1": 110, "VALUE2": 95, "INSPECTION": 1, "STATE": 0, "minuteTime": 20 }, { "DATE_TIME": "2018-10-31T23:40:00.000Z", "VALUE1": 153, "VALUE2": 117, "INSPECTION": 1, "STATE": 1, "minuteTime": 40 }, { "DATE_TIME": "2018-10-31T23:40:00.000Z", "VALUE1": 125, "VALUE2": 98, "INSPECTION": 1, "STATE": 0, "minuteTime": 40 }, { "DATE_TIME": "2018-11-01T00:00:00.000Z", "VALUE1": 112, "VALUE2": 117, "INSPECTION": 1, "STATE": 1, "minuteTime": 60 }, { "DATE_TIME": "2018-11-01T00:00:00.000Z", "VALUE1": 120, "VALUE2": 100, "INSPECTION": 1, "STATE": 0, "minuteTime": 60 }, { "DATE_TIME": "2018-11-01T03:20:00.000Z", "VALUE1": 147, "VALUE2": 108, "INSPECTION": 1, "STATE": 1, "minuteTime": 260 }, { "DATE_TIME": "2018-11-01T03:20:00.000Z", "VALUE1": 130, "VALUE2": 98, "INSPECTION": 1, "STATE": 0, "minuteTime": 260 }, { "DATE_TIME": "2018-11-01T06:30:00.000Z", "VALUE1": 152, "VALUE2": 111, "INSPECTION": 1, "STATE": 1, "minuteTime": 450 }, { "DATE_TIME": "2018-11-01T06:30:00.000Z", "VALUE1": 140, "VALUE2": 101, "INSPECTION": 1, "STATE": 0, "minuteTime": 450 }, { "DATE_TIME": "2018-11-01T08:00:00.000Z", "VALUE1": 112, "VALUE2": 101, "INSPECTION": 1, "STATE": 1, "minuteTime": 540 }, { "DATE_TIME": "2018-11-01T08:00:00.000Z", "VALUE1": 120, "VALUE2": 90, "INSPECTION": 1, "STATE": 0, "minuteTime": 540 }, { "DATE_TIME": "2018-11-01T11:00:00.000Z", "VALUE1": 122, "VALUE2": 100, "INSPECTION": 1, "STATE": 1, "minuteTime": 720 }, { "DATE_TIME": "2018-11-01T11:00:00.000Z", "VALUE1": 130, "VALUE2": 95, "INSPECTION": 1, "STATE": 0, "minuteTime": 720 }, { "DATE_TIME": "2018-11-01T15:00:00.000Z", "VALUE1": 112, "VALUE2": 101, "INSPECTION": 1, "STATE": 1, "minuteTime": 960 }, { "DATE_TIME": "2018-11-01T15:00:00.000Z", "VALUE1": 125, "VALUE2": 98, "INSPECTION": 1, "STATE": 0, "minuteTime": 960 }, { "DATE_TIME": "2018-11-01T17:20:00.000Z", "VALUE1": 147, "VALUE2": 108, "INSPECTION": 1, "STATE": 1, "minuteTime": 1100 }, { "DATE_TIME": "2018-11-01T17:20:00.000Z", "VALUE1": 129, "VALUE2": 102, "INSPECTION": 1, "STATE": 0, "minuteTime": 1100 }, { "DATE_TIME": "2018-11-01T20:59:00.000Z", "VALUE1": 151, "VALUE2": 102, "INSPECTION": 1, "STATE": 1, "minuteTime": 1319 }, { "DATE_TIME": "2018-11-01T20:59:00.000Z", "VALUE1": 132, "VALUE2": 90, "INSPECTION": 1, "STATE": 0, "minuteTime": 1319 } ]\nconst width = 954;\nconst height = width;\nconst margin = 5;\nconst innerRadius = width / 10;\nconst outerRadius = width / 4 - margin;\n\n\n\n function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {\n // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point\n var denominator, a, b, numerator1, numerator2, result = {\n x: null,\n y: null,\n onLine1: false,\n onLine2: false\n };\n denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));\n if (denominator === 0) {\n return result;\n }\n a = line1StartY - line2StartY;\n b = line1StartX - line2StartX;\n numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);\n numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);\n a = numerator1 / denominator;\n b = numerator2 / denominator;\n\n // if we cast these lines infinitely in both directions, they intersect here:\n result.x = line1StartX + (a * (line1EndX - line1StartX));\n result.y = line1StartY + (a * (line1EndY - line1StartY));\n /*\n // it is worth noting that this should be the same as:\n x = line2StartX + (b * (line2EndX - line2StartX));\n y = line2StartX + (b * (line2EndY - line2StartY));\n */\n // if line1 is a segment and line2 is infinite, they intersect if:\n if (a > 0 && a < 1) {\n result.onLine1 = true;\n }\n // if line2 is a segment and line1 is infinite, they intersect if:\n if (b > 0 && b < 1) {\n result.onLine2 = true;\n }\n // if line1 and line2 are segments, they intersect if both of the above are true\n return result;\n }\n// const width = 954;\n// const height = width;\n// const margin = 10;\n// const innerRadius = width / 5;\n// const outerRadius = width / 2 - margin;\n\n\n\n let rawdata2 = rawdata.filter(d => d.INSPECTION == 1);\n\n const minuteScale = d3.scaleLinear()\n .domain([0, 1439]) // 0 to 23:59 (24 hours * 60 minutes without -1 or else change to 1439 //- 1)\n .range([0, 2 * Math.PI]);\n\n const minuteTimeValues = rawdata2.map(d => d.minuteTime);\n\n const x = d3.scaleTime()\n .domain([0, 1439]) // Set the domain using the extent of the "minuteTime" values\n .range([0, 2 * Math.PI]); // Set the desired range\n\n //console.log(rawdata)\n\n const minValue1 = d3.min(rawdata, d => d.VALUE1);\n const minValue2 = d3.min(rawdata, d => d.VALUE2);\n const maxValue1 = d3.max(rawdata, d => d.VALUE1);\n const maxValue2 = d3.max(rawdata, d => d.VALUE2);\n\n const yMin = minValue1 < minValue2 ? minValue1 : minValue2;\n const yMax = maxValue1 > maxValue2 ? maxValue1 : maxValue2;\n\n const y = d3.scaleLinear()\n .domain([yMin, yMax])\n .range([innerRadius, outerRadius]);\n\n// Modify the xAxis generator to display only the hour and add padding\n\nconst xAxis = (g) => g\n .attr("font-family", "sans-serif")\n .attr("font-size", 7)\n .call((g) => g.selectAll("g")\n .data(d3.range(0, 24)) // create an array of hour values\n .join("g")\n .each((d, i) => d.id = `hour-${i}`)\n .call((g) => g.append("path")\n .attr("stroke", "#000")\n .attr("stroke-opacity", 0.2)\n .attr("d", (d) => `\n M${d3.pointRadial(x(d * 60), innerRadius)}\n L${d3.pointRadial(x(d * 60), outerRadius)}\n `))\n .call((g) => g.append("path")\n .attr("id", (d) => `hour-${d}`)\n .datum((d) => [d * 60, (d + 1) * 60])\n .attr("fill", "none")\n .attr("d", ([a, b]) => `\n M${d3.pointRadial(x(a), innerRadius-10)}\n A${innerRadius},${innerRadius} 0,0,1 ${d3.pointRadial(x(b), innerRadius)}\n `))\n .call((g) => g.append("text")\n .append("textPath")\n .attr("startOffset", 6)\n .attr("href", (d) => `#hour-${d}`)\n .text((d) => d.toString().padStart(2, "0"))\n .attr("text-anchor", "middle")\n .attr("alignment-baseline", "middle")\n .attr("transform", (d) => {\n const angle = x(d * 60);\n const radius = innerRadius - 20; // reduce the radius to move the labels closer to the center\n return `translate(${d3.pointRadial(x(d * 60), radius)})`;\n })\n )\n );\n\n\n\n const yAxis = g => g\n .attr("text-anchor", "middle")\n .attr("font-family", "sans-serif")\n .attr("font-size", 7)\n .call(g => g.selectAll("g")\n .data(y.ticks().reverse())\n .join("g")\n .attr("fill", "none")\n .call(g => g.append("circle")\n .attr("stroke", "#000")\n .attr("stroke-opacity", 0.1)\n .attr("r", y))\n .call(g => g.append("text")\n .attr("y", d => -y(d))\n .attr("dy", "0.35em")\n .attr("stroke", "#fff")\n .attr("stroke-width", 5)\n .attr("stroke-opacity", 0.6)\n .text((x, i) => `${x.toFixed(0)}${i ? "" : ""}`)\n .clone(true)\n .attr("y", d => y(d))\n .selectAll(function() { return [this, this.previousSibling]; })\n .clone(true)\n .attr("fill", "currentColor")\n .attr("stroke", "none")));\n\n const line = d3.lineRadial()\n .curve(d3.curveLinear)\n .angle(d => x(d.minuteTime));\n\n const area = d3.areaRadial()\n .curve(d3.curveLinear)\n .angle(d => x(d.minuteTime))\n\n const svg = d3.create("svg")\n .attr("viewBox", [-width / 2, -height / 2, width, height])\n .attr("stroke-linejoin", "round")\n .attr("stroke-linecap", "round");\n\n svg.append("path")\n .attr("fill", "none")\n .attr("stroke", "red")\n .attr("stroke-width", 3)\n .attr("d", line\n .radius(d => y(d.VALUE1))\n (rawdata2));\n\n svg.append("path")\n .attr("fill", "none")\n .attr("stroke", "green")\n .attr("stroke-width", 3)\n .attr("d", line\n .radius(d => y(d.VALUE2))\n (rawdata2));\n// +++++++++++++++++++++++++++++++++++ calc crossing points ++++++++++++++++++++++++++++++++\nfunction findCrossingPoints(data) {\n const crossingPoints = [];\n\n for (let i = 1; i < data.length; i++) {\n const d1 = data[i - 1];\n const d2 = data[i];\n\n if ((d1.VALUE1 - d1.VALUE2) * (d2.VALUE1 - d2.VALUE2) < 0) {\n // There\'s a crossing point between the two data points\n const t = (0 - (d1.VALUE1 - d1.VALUE2)) / ((d2.VALUE1 - d2.VALUE2) - (d1.VALUE1 - d1.VALUE2));\n const crossingValue1 = d1.VALUE1 + t * (d2.VALUE1 - d1.VALUE1);\n const crossingValue2 = d1.VALUE2 + t * (d2.VALUE2 - d1.VALUE2);\n crossingPoints.push({ VALUE1: crossingValue1, VALUE2: crossingValue2 });\n }\n }\n\n return crossingPoints;\n}\n\nconst crossingPoints = findCrossingPoints(rawdata2);\n function getX(d) {\n return Number(line.radius(d => y(d.VALUE1))([d]).match(\'M(-?[0-9.]*),(-?[0-9.]*)\')[1]);\n }\n function getY(d) {\n return Number(line.radius(d => y(d.VALUE1))([d]).match(\'M(-?[0-9.]*),(-?[0-9.]*)\')[2]);\n }\n function getX2(d) {\n return Number(line.radius(d => y(d.VALUE2))([d]).match(\'M(-?[0-9.]*),(-?[0-9.]*)\')[1]);\n }\n function getY2(d) {\n return Number(line.radius(d => y(d.VALUE2))([d]).match(\'M(-?[0-9.]*),(-?[0-9.]*)\')[2]);\n }\n\n\n\n\n\n\n// +++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++ ++++++++++++++++++++++++++++++++\n\n\n\n// separate data in positive and negative value\nlet separated = rawdata2.reduce((a, b,index) => {\n let sub = b.VALUE1 - b.VALUE2;\n if (sub > 0) {\n return {positive: [...a.positive, {...b,index}], negative: [...a.negative]};\n }\n return {positive: [...a.positive], negative: [...a.negative, {...b,index}]};\n\n}, {positive: [], negative: []})\n\nlet positiveSets = [];\n\nlet previous = undefined\n\nseparated.positive.forEach((e)=>{\n //if not contiguous\n if (!previous || previous.index + 1 !== e.index) {\n // create a new set\n positiveSets.push([e]);\n } else {\n // append value to previous set\n positiveSets[positiveSets.length - 1] = [...positiveSets[positiveSets.length - 1], e]\n }\n previous = e;\n})\n//same for negatives\nlet negativeSets = [];\nprevious = undefined\nseparated.negative.forEach((e)=>{\n if (!previous || previous.index + 1 !== e.index) {\n negativeSets.push([e]);\n } else {\n negativeSets[negativeSets.length - 1] = [...negativeSets[negativeSets.length - 1], e]\n }\n previous = e;\n})\n\n negativeSets =negativeSets.map(s=>{\n\n let first = s[0];\n let last = s[s.length-1];\n let next;\n let prev;\n if (first.index > 0){\n prev = rawdata2[first.index-1]\n }else{\n\n prev = rawdata2[rawdata2.length-1]\n }\n if(last.index === rawdata2.length-1){\n next = rawdata2[0];\n }else{\n next = rawdata2[last.index +1];\n }\n\n\n let w = checkLineIntersection(getX(prev),getY(prev),getX(first),getY(first),getX2(prev),getY2(prev),getX2(first),getY2(first))\n let n = checkLineIntersection(getX(last),getY(last),getX(next),getY(next),getX2(last),getY2(last),getX2(next),getY2(next))\n\n return [{x1:w.x,y1:w.y,x2:w.x,y2:w.y},...s.map(w=>({x1:getX(w),y1:getY(w),x2:getX2(w),y2:getY2(w)})),{x1:n.x,y1:n.y,x2:n.x,y2:n.y}]\n\n })\n\n positiveSets =positiveSets.map(s=>{\n\n let first = s[0];\n let last = s[s.length-1];\n let next;\n let prev;\n if (first.index > 0){\n prev = rawdata2[first.index-1]\n }else{\n\n prev = rawdata2[rawdata2.length-1]\n }\n if(last.index === rawdata2.length-1){\n next = rawdata2[0];\n }else{\n next = rawdata2[last.index +1];\n }\n\n\n let w = checkLineIntersection(getX(prev),getY(prev),getX(first),getY(first),getX2(prev),getY2(prev),getX2(first),getY2(first))\n let n = checkLineIntersection(getX(last),getY(last),getX(next),getY(next),getX2(last),getY2(last),getX2(next),getY2(next))\n\n return [{x1:w.x,y1:w.y,x2:w.x,y2:w.y},...s.map(w=>({x1:getX(w),y1:getY(w),x2:getX2(w),y2:getY2(w)})),{x1:n.x,y1:n.y,x2:n.x,y2:n.y}]\n\n })\n const negG = svg.selectAll(\'.negative\').data(negativeSets)\n negG.enter().append(\'path\')\n .attr(\'class\',\'negative\') // "negative" color\n .attr(\'fill\',\'lightblue\')\n .attr("d", (d)=>d3.area().x0(d => d.x1).x1(d => d.x2).y0(d => d.y1).y1(d => d.y2)(d));\n\n\n const posG = svg.selectAll(\'.positive\').data(positiveSets)\n posG.enter().append(\'path\')\n .attr(\'class\',\'positive\') // "negative" color\n .attr(\'fill\',\'darkblue\')\n .attr("d", (d)=>d3.area().x0(d => d.x1).x1(d => d.x2).y0(d => d.y1).y1(d => d.y2)(d));\n\n\n svg.append("g")\n .call(xAxis);\n\n svg.append("g")\n .call(yAxis);\n\n\n\n // console.log(negG)\n\nconst container = d3.select("body").append("div");\ncontainer.node().appendChild(svg.node());Run Code Online (Sandbox Code Playgroud)\r\n#csvdata {\n display: none;\n}Run Code Online (Sandbox Code Playgroud)\r\n<script src="https://d3js.org/d3.v7.min.js"></script>\n <h1>D3 CSV Example</h1>Run Code Online (Sandbox Code Playgroud)\r\n我想用 STATE 列(二进制值)可视化另一个数据框。我想要可视化具有 0 个 STATE 列值的 VALUE1 列和具有 1 个 STATE 列值的 VALUE1 列。我可以为每个创建两个数组。
\nlet rawdata3 = rawdata.filter(d => d.STATE== 0);\nlet rawdata4 = rawdata.filter(d => d.STATE== 1);\n\nRun Code Online (Sandbox Code Playgroud)\n我遇到的问题是,如果我想使用两个不同的数组制作径向图来根据 STATE 列的二进制值可视化 VALUE1 列,我不知道如何更新交叉点部分和负/正区域部分。rawdata3 数组的 VALUE1 列的线条颜色应为红色,rawdata3 数组的 VALUE2 列的线条颜色应为蓝色。rawdata4 数组的 VALUE1 列的线条颜色应为绿色,rawdata4 数组的 VALUE2 列的线条颜色应为绿色。\n如何我可以更新我的代码,以便可以通过更新交叉点部分和负/正面积部分来制作径向图吗?
\n如果绿线比红线大,则使用较浅的红色,否则使用较深的红色。如果绿线比蓝线大,则使用较浅的蓝色,否则使用较深的蓝色。
\n我所做的:我创建了两个空集,即负集和正集。我计算了负集和正集的每个末端的交叉点,创建了一组新的面积点并绘制了这些面积。对于同一数据帧(数组)中的不同列,这可以顺利工作。\nTLDR:我创建了多个区域。将我的数据分隔在多组连续的 \xe2\x80\x9cpositive\xe2\x80\x9d 或 \xe2\x80\x9cnegative\xe2\x80\x9d 区域中。然后,我为每个数据集绘制了一个区域。
\n我的期望:
\n我想画4条线。我想使用 rawdata.filter(d => d.STATE== 0); 绘制 2 条线(VALUE1 列值 -> 红色线,VALUE2 列值 -> 蓝色线);和 2 行(VALUE1 列值 -> 绿色线,VALUE2 列值 -> 绿色线)使用 rawdata.filter(d => d.STATE== 1);。
\n我可以画线。但是,我无法有条件地填写这些行。
\n编辑:我接受此问题的任何其他替代解决方案。我发布了我的尝试以将其显示为帮助/参考。
\n\n我刚刚发现了一些类似的问题也许它们会对你有所帮助
| 归档时间: |
|
| 查看次数: |
231 次 |
| 最近记录: |