当前位置: 首页 > news >正文

d3.js绘制组合PCA边缘分布图

用d3.js研发了个组合PCA边缘分布图;

组合PCA边缘分布图中包括pca散点图、散点图可根据数据自动分为连续型和离散型、还有散点的各种配置、边缘有箱线边缘、密度边缘、柱状边缘一个各个边缘的配置等等,大部分你能想到的配置都是自行传参调整的,想不到的也能稍作修改然后自行调整,上代码:

import * as d3 from "d3";
import { dispersedColorList, gradientColorList } from "@/utils/commonData";const pcaCombinationEdgeChart = (options = {}) => {let originContainer = document.querySelector("#chart-container");let originHeight = originContainer.offsetHeight;let originWidth = originContainer.offsetWidth;let height = originHeight * (options.height / 100);let width = originWidth * (options.width / 100);// 获取标签样式function getSvgTextStyle({text = "",fontSize = 14,fontFamily = "Arial",fontWeight = "normal"} = {}) {const svg = d3.select("body").append("svg").attr("class", "get-svg-text-style");const textStyle = svg.append("text").text(text).attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).node().getBBox();svg.remove();return {width: textStyle.width,height: textStyle.height};}// 获取线性坐标轴宽高function getSvgLinearAxisStyle({fontSize = 20,orient = "bottom",fontFamily = "Arial",fontWeight = "normal",rotate = 0,domain = [0, 9],range = [0, 200]} = {}) {let axis;let svg = d3.select("body").append("svg").attr("width", 200).attr("height", 100).attr("transform", "translate(300, 200)").attr("class", "get-svg-axis-style");let scale = d3.scaleLinear().domain(domain).range(range);if (orient === "bottom" || orient === "top") {axis = d3.axisBottom(scale);} else {axis = d3.axisLeft(scale);}let axisStyle = svg.append("g").call(axis).call((g) => {g.selectAll("text").attr("fill", "#555").attr("font-size", fontSize).attr("font-family", fontFamily).attr("font-weight", fontWeight).attr("tmpY",g.select("text").attr("tmpY") || g.select("text").attr("dy")).attr("dy",rotate > 70 && rotate <= 90? "0.35em": rotate >= -90 && rotate < -70? "0.4em": g.select("text").attr("tmpY")).attr("text-anchor",orient === "left"? "end": rotate? rotate > 0? "start": "end": "middle").attr("transform",`translate(0, 0) ${rotate ? `rotate(${rotate} 0 ${g.select("text").attr("y")})` : ""}`);}).node().getBBox();svg.remove();return {width: axisStyle.width,height: axisStyle.height};}const symbolZoomTimes = 10;const symbolZoomTimesHover = 15;// 散点类型const symbolTypes = {circle: d3.symbolCircle,cross: d3.symbolCross,diamond: d3.symbolDiamond,square: d3.symbolSquare,star: d3.symbolStar,triangle: d3.symbolTriangle,wye: d3.symbolWye};// 线条样式const lineShapes = {point: "2 2",straigh_line: "0",dash_dot: "10 10 2 2",long_dash: "20 10 2 2",short_straight_line: "10 10"};// 选择器类名const classList = [".svg-groups-hull-group",".svg-groups-ellipse-group",".svg-groups-line-group",".svg-groups-scatter-group",".svg-groups-label-group"];// pca名称列表const pcaNameList = ["PC1", "PC2", "PC3"];const combinationBoxplot = {PC1: "pc1Boxplot",PC2: "pc2Boxplot",PC3: "pc3Boxplot"};const combinationAreas = {PC1: "pc1Areas",PC2: "pc2Areas",PC3: "pc3Areas"};const combinationBarplot = {PC1: "pc1BarData",PC2: "pc2BarData",PC3: "pc3BarData"};// 图例鼠标移入移出事件function legendMouse(type, index, data = []) {data.forEach((item, i) => {let opacity = type === "mouseout" ? 1 : index == i ? 1 : 0.3;let fontWeight =type === "mouseout" ? "normal" : index == i ? "bold" : "normal";d3.select(`.svg-legend-label-item${i}`).attr("font-weight", fontWeight);classList.forEach((subItem) => {d3.selectAll(subItem + i).attr("opacity", opacity);});});}// 图例图表点击事件function legendClick(index, color = "") {let el = d3.selectAll(`.svg-groups-scatter-group${index}`);let visibility = el.attr("visibility");let isVisibility = visibility? visibility === "visible"? true: false: true;d3.select(`.svg-legend-label-item${index}`).attr("fill",isVisibility ? "#ccc" : "#000");d3.select(`.svg-legend-path-item${index}`).attr("fill",isVisibility ? "#ccc" : color);classList.forEach((item) => {if (isVisibility) {el.attr("visibility", "hidden");} else {el.attr("visibility", "visible");}});}// tooltipfunction tooltip({group,sample,xValue,yValue,mate,left,top,parentElement}) {if (d3.select(".scatter-tooltip").empty()) {d3.select("body").append("div").attr("class", "scatter-tooltip").html(`<div class="scatter-tooltip-group">group: ${group}</div><div class="scatter-tooltip-sample">sample: ${sample}</div><div class="scatter-tooltip-x">X轴:${xValue}</div><div class="scatter-tooltip-y">Y轴:${yValue}</div>`).style("position", "fixed").style("left", `${left}px`).style("top", `${top}px`).style("padding", "8px 5px").style("border-radius", "4px").style("font-size", "12px").style("color", "#555").style("background", "rgba(255, 255,  255, .8)").style("border", `1px solid ${d3.select(parentElement).attr("fill")}`);} else {d3.select(".scatter-tooltip").style("display", "block").style("left", `${left}px`).style("top", `${top}px`).style("border", `1px solid ${d3.select(parentElement).attr("fill")}`);d3.select(".scatter-tooltip-group").html(`group: ${group}`);d3.select(".scatter-tooltip-sample").html(`sample: ${sample}`);d3.select(".scatter-tooltip-x").html(` X轴: ${xValue}`);d3.select(".scatter-tooltip-y").html(` Y轴: ${yValue}`);}}let {container = "#pca-container",left = 20,right = 350,bottom = 20,groupList = [],plot_type = "scatter", // 散点:scatter,按组连线:polygon,置信椭圆:ellipsedot_types = {SCL: "circle",CL: "circle",TL: "circle",ML: "circle"},mateDot_types = {Mate1: "circle",Mate2: "circle"},dot_size = 10,opacity = 0.4,grid_enabled = false,border_enabled = true,center_line_enabled = false,auxiliary_line_enabled = false,line_type = "straigh_line",x_axis_value = "PC1",y_axis_value = "PC2",showGroupObj = {SCL: 0,CL: 0,TL: 0,ML: 0},label_color = "#000",label_font = "Arial",label_size = 10,isLabel = true,dotColorType = "group",dotShapeType = "group",ischeckLabel = false,checkLabelPosition = "leftTop",main_title = "main_title",main_title_color = "#000",main_title_font = "Arial",main_title_size = 14,x_title = "x_title",x_title_color = "#000",x_title_font = "Arial",x_title_size = 14,x_text_color = "#000",x_text_font = "Arial",x_text_size = 12,x_text_rotate = 0,y_title = "y_title",y_title_color = "#000",y_title_font = "Arial",y_title_size = 14,y_text_color = "#000",y_text_font = "Arial",y_text_size = 12,legend_title = "group",legend_title_color = "#000",legend_title_size = 14,legend_title_font = "Arial",legend_text_color = "#000",legend_text_font = "Arial",legend_text_size = 14,legend_title2 = "shape",legend_title_color2 = "#000",legend_title_size2 = 14,legend_title_font2 = "Arial",legend_text_color2 = "#000",legend_text_font2 = "Arial",legend_text_size2 = 14,legend_size = 7,stress = false,stressValue = "",// 新增可调参数plotType = "barPlot",cloud_opacity = 0.5,isGroup = true,isEdge = false,edgeColor = "#FDC0C0"} = options;const checkLabel = options.data.checkLabel;// 箱线图相关定义和计算const boxPlotMarginTop = 20;const data = options.data;const boxPlotHeight =isEdge && !isGroup ? 70 : data.list?.length * (10 + 5) + boxPlotMarginTop;const allBoxplotHight = 30;const mTop = isEdge ? boxPlotHeight : 15;// 针对多边形和椭圆let forwardValue = `${x_axis_value}_${y_axis_value}`;let reverseValue = `${y_axis_value}_${x_axis_value}`;let colors = dispersedColorList[options.color];let colorListMates = dispersedColorList[options.colorMate];let colorListMateLiners = gradientColorList[options.colorMateLiner];// options.colorList.forEach((item) => {//   colors.push(item.color);// });// options.colorListMate.forEach((item) => {//   colorListMates.push(item.color);// });// options.colorListMateLiner.forEach((item) => {//   colorListMateLiners.push(item.color);// });let newGroupcheck = [];newGroupcheck = data.groups?.filter((item, index) => {if (groupList.indexOf(item.group) !== -1) {return item;}});let medianNum = (data.mateRange[0] + data.mateRange[1]) / 2;let data_tip = [data.mateRange[0], medianNum, data.mateRange[1]];let data_tip_backup = [];data_tip.map((item) => {data_tip_backup.push(Number(item).toFixed(2));});const colorScale = d3.scaleLinear().domain(data_tip_backup).range(colorListMateLiners);const box = document.querySelector(container);if (!box || !data.list || !data.list.length) return;if (data.type === "two") {data.list.forEach((item) => {let lineData = item.line[forwardValue] || item.line[reverseValue];lineData.ellipse = [];});}if (data.type === "one") {data.list.forEach((item) => {let lineData = item.line[forwardValue] || item.line[reverseValue];lineData.hull = [];});}let xIndex = pcaNameList.indexOf(x_axis_value);let yIndex = pcaNameList.indexOf(y_axis_value);let xCoordSet = [];let yCoordSet = [];data.list.forEach((item, index) => {xCoordSet.push(item.center[0][xIndex]);yCoordSet.push(item.center[0][yIndex]);item.data.forEach((subItem, j) => {xCoordSet.push(Number(subItem[xIndex + 1]));yCoordSet.push(Number(subItem[yIndex + 1]));});let isAxisReverse = !item.line[forwardValue];let pcaXIndex = isAxisReverse ? 1 : 0;let pcaYIndex = isAxisReverse ? 0 : 1;let pcaData = item.line[forwardValue] || item.line[reverseValue];pcaData?.hull?.forEach((subItem, j) => {xCoordSet.push(subItem[pcaXIndex]);yCoordSet.push(subItem[pcaYIndex]);});pcaData?.ellipse?.forEach((subItem, j) => {xCoordSet.push(subItem[pcaXIndex]);yCoordSet.push(subItem[pcaYIndex]);});});const xOldMin = Math.min(...xCoordSet);const xOldMax = Math.max(...xCoordSet);const yOldMin = Math.min(...yCoordSet);const yOldMax = Math.max(...yCoordSet);// x、y作用域const xTmpDomain = [xOldMin, xOldMax];const yTmpDomain = [yOldMin, yOldMax];const xTmpMin = xTmpDomain[0];const xTmpMax = xTmpDomain[1];const xStep = Math.abs(xTmpMax * 0.1);const xMin = xTmpMin - xStep >= 0 ? -xStep : xTmpMin - xStep;const xMax = xTmpMax + xStep > 0 ? xTmpMax + xStep : xStep;const yTmpMin = yTmpDomain[0];const yTmpMax = yTmpDomain[1];const yStep = Math.abs(yTmpMax * 0.1);const yMin = yTmpMin - yStep >= 0 ? -yStep : yTmpMin - yStep;const yMax = yTmpMax + yStep > 0 ? yTmpMax + yStep : yStep;const xDomain = [xMin, xMax];const yDomain = [yMin, yMax];// 主标题高度const titleSpace = 10;const titleH = getSvgTextStyle({text: main_title,fontSize: main_title_size,fontFamily: main_title_font}).height;const titleW = getSvgTextStyle({text: main_title,fontSize: main_title_size,fontFamily: main_title_font}).width;const titleTotalH = main_title ? titleH + titleSpace : 0;// X轴及标题高度const xTitleSpace = 10;const xTitleH = getSvgTextStyle({text: data.var ? x_title + `[${data.var[xIndex]}%]` : x_title,fontSize: x_title_size,fontFamily: x_title_font}).height;const xTitleTotalH = xTitleH + xTitleSpace;const xAxisH = getSvgLinearAxisStyle({fontSize: x_text_size,fontFamily: x_text_font,rotate: x_text_rotate,domain: xDomain}).height;// Y轴及标题高度const yTitleSpace = 10;const yTitleW = getSvgTextStyle({text: data.var ? y_title + `[${data.var[yIndex]}%]` : y_title,fontSize: y_title_size,fontFamily: y_title_font}).height;const yTitleTotalW = yTitleW + yTitleSpace;const yAxisW = getSvgLinearAxisStyle({fontSize: y_text_size,fontFamily: y_text_font,domain: yDomain,orient: "left"}).width;// 图例宽、高、间距等const legendList = [];data.list.map((item) => {legendList.push({label: item.group,...getSvgTextStyle({text: item.group,fontSize: legend_text_size,fontFamily: legend_text_font})});});const legendShapeList = [];if (data.mateRange) {data.mateRange?.map((item) => {legendShapeList.push({label: item,...getSvgTextStyle({text: item,fontSize: legend_text_size2,fontFamily: legend_text_font2})});});}const legendLabelSpace = 5;const legendBottomSpace = 8;const legendRightSpace = 10;const legendLeftSpace = 15;const legendLabelH = legendList.length? Math.max(legendList[0].height, legend_size * 2): 0;const legendLabelW = d3.max(legendList, (d) => d.width + 20) || 0;const legendEachH = legendLabelH + legendBottomSpace;const legendEachW =legendLabelW + legend_size * 2 + legendLabelSpace + legendRightSpace;const chartHeight =height - mTop - bottom - titleTotalH - xTitleTotalH - xAxisH;const legendStep =options.dotColorType === options.dotShapeType? Math.floor(chartHeight / legendEachH): Math.floor((chartHeight - 50) / 2 / legendEachH);const legendColumn = Math.ceil(legendList.length / legendStep);const legendTotalw = legendList.length ? legendColumn * legendEachW : 0;const chartWidth =width -left -right -yTitleTotalW -yAxisW -legendLeftSpace -legendTotalw;// x轴值映射const xScale = d3.scaleLinear().domain(xDomain).range([0, chartWidth]);// y轴值映射const yScale = d3.scaleLinear().domain(yDomain).range([chartHeight, 0]);// X轴const xAxis = d3.axisBottom(xScale).tickSizeOuter(border_enabled ? -chartHeight : 0);// Y轴const yAxis = d3.axisLeft(yScale).tickSizeOuter(border_enabled ? -chartWidth : 0);!d3.select(container).select("svg").empty() &&d3.select(container).select("svg").remove();// 创建svgconst svg = d3.select(container).append("svg").attr("width", width).attr("height", height).attr("id", "pca-svg-container");// 创建X轴svg.append("g").attr("class", "svg-x-axis").attr("transform",`translate(${left + yTitleTotalW + yAxisW}, ${height - bottom - xTitleTotalH - xAxisH})`).call(xAxis).call((g) => {g.selectAll("text").attr("fill", x_text_color).attr("font-size", x_text_size).attr("font-family", x_text_font).attr("tmpY",g.select("text").attr("tmpY") || g.select("text").attr("dy")).attr("dy",x_text_rotate > 70 && x_text_rotate <= 90? "0.35em": x_text_rotate >= -90 && x_text_rotate < -70? "0.4em": g.select("text").attr("tmpY")).attr("text-anchor",x_text_rotate ? (x_text_rotate > 0 ? "start" : "end") : "middle").attr("transform",`translate(0, 0) ${x_text_rotate? `rotate(${x_text_rotate} 0 ${g.select("text").attr("y")})`: ""}`);});// 创建Y轴svg.append("g").attr("class", "svg-y-axis").attr("transform",`translate(${left + yTitleTotalW + yAxisW}, ${mTop + titleTotalH})`).call(yAxis).call((g) => {g.selectAll("text").attr("fill", y_text_color).attr("font-size", y_text_size).attr("font-family", y_text_font);});// 主标题;svg.append("g").attr("class", "svg-main-title").append("text").text(main_title).attr("fill", main_title_color).attr("font-family", main_title_font).attr("font-size", main_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform",`translate(${left + yTitleTotalW + yAxisW + chartWidth / 2}, ${main_title ? titleH : 0})`);//NMDS 主标题下面标识svg.append("g").attr("class", "theme-pca").append("text").text(stressValue).attr("x", left + yTitleTotalW + yAxisW + chartWidth / 2).attr("y", main_title ? titleH + 10 : 10).attr("text-anchor", "middle").attr("fill", "rgba(0, 0, 0, 0.6)").attr("font-size", 12).attr("visibility", stress ? "visible" : "hidden");// X轴标题svg.append("g").attr("class", "svg-x-title").append("text").text(data.var?.length > 0 ? x_title + ` [${data.var[xIndex]}%]` : x_title).attr("fill", x_title_color).attr("font-family", x_title_font).attr("font-size", x_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "hanging").attr("transform",`translate(${left + yTitleTotalW + yAxisW + chartWidth / 2}, ${height - bottom - xTitleH})`);// Y轴标题svg.append("g").attr("class", "svg-y-title").append("text").text(data.var?.length > 0 ? y_title + ` [${data.var[yIndex]}%]` : y_title).attr("fill", y_title_color).attr("font-family", y_title_font).attr("font-size", y_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "hanging").attr("transform",`translate(${left}, ${mTop + titleH + chartHeight / 2}) rotate(-90)`);// 图例const legendEl = svg.append("g").attr("cursor", "pointer").attr("class", "svg-legend").attr("transform",`translate(${isEdge? width - right - legendTotalw + boxPlotHeight + 15: width - right - legendTotalw + 10}, ${mTop + titleTotalH})`);if (!data.isMate) {legendEl.append("g").attr("class", "svg-legend-title").append("text").text(legend_title).attr("fill", legend_title_color).attr("font-family", legend_title_font).attr("font-size", legend_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);// 图例iconlegendEl.append("g").attr("class", "svg-legend-path").selectAll("path").data(legendList).enter().append("path").attr("index", (d, i) => i).attr("class", (d, i) => `svg-legend-path-item svg-legend-path-item${i}`).attr("fill", (d, i) => {if (dotColorType == "mate") {return colorListMates[i % colorListMates.length];} else {return colors[i % colors.length];}}).attr("d", (d, i) => {if (dotShapeType == "mate") {const symbolType = mateDot_types[d.label];const symbolGenerator = d3.symbol().type(symbolTypes[symbolType]).size(dot_size * symbolZoomTimes);return symbolGenerator();} else {let group = data.list[i].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(legend_size * symbolZoomTimes);return symbol();}}).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;}).on("mouseover", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseover", index, data.list);}}).on("mouseout", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseout", index, data.list);}}).on("click", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendClick(index, colors[index]);}});// 图例标签legendEl.append("g").attr("class", "svg-legend-label").selectAll("text").data(legendList).enter().append("text").text((d) => d.label).attr("index", (d, i) => i).attr("fill", legend_text_color).attr("font-size", legend_text_size).attr("font-family", legend_text_font).attr("dominant-baseline", "central").attr("class",(d, i) => `svg-legend-label-item svg-legend-label-item${i}`).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size * 2 + legendLabelSpace + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +11})`;});} else {if (data.dataType == "discrete" && dotColorType == dotShapeType) {legendEl.append("g").attr("class", "svg-legend-title").append("text").text(legend_title).attr("fill", legend_title_color).attr("font-family", legend_title_font).attr("font-size", legend_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);// 图例iconlegendEl.append("g").attr("class", "svg-legend-path").selectAll("path").data(dotColorType == "mate" ? legendShapeList : legendList).enter().append("path").attr("index", (d, i) => i).attr("class",(d, i) => `svg-legend-path-item svg-legend-path-item${i}`).attr("fill", (d, i) => {if (dotColorType == "mate") {return colorListMates[i % colorListMates.length];} else {return colors[i % colors.length];}}).attr("d", (d, i) => {if (dotShapeType == "mate") {const symbolType = mateDot_types[d.label];const symbolGenerator = d3.symbol().type(symbolTypes[symbolType]).size(dot_size * symbolZoomTimes);return symbolGenerator();} else {let group = data.list[i].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(legend_size * symbolZoomTimes);return symbol();}}).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;}).on("mouseover", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseover", index, data.list);}}).on("mouseout", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseout", index, data.list);}}).on("click", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendClick(index, colors[index]);}});// 图例标签legendEl.append("g").attr("class", "svg-legend-label").selectAll("text").data(dotColorType == "mate" ? legendShapeList : legendList).enter().append("text").text((d) => d.label).attr("index", (d, i) => i).attr("fill", legend_text_color).attr("font-size", legend_text_size).attr("font-family", legend_text_font).attr("dominant-baseline", "central").attr("class",(d, i) => `svg-legend-label-item svg-legend-label-item${i}`).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size * 2 + legendLabelSpace + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +11})`;});} else if (data.dataType == "liner") {legendEl.append("g").attr("class", "svg-legend-title").append("text").text(legend_title).attr("fill", legend_title_color).attr("font-family", legend_title_font).attr("font-size", legend_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);// 图例iconlegendEl.append("g").attr("class", "svg-legend-path").selectAll("path").data(legendList).enter().append("path").attr("index", (d, i) => i).attr("class",(d, i) => `svg-legend-path-item svg-legend-path-item${i}`).attr("fill", (d, i) => {return colors[i % colors.length];}).attr("d", (d, i) => {let group = data.list[i].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(legend_size * symbolZoomTimes);return symbol();}).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;});// 图例标签legendEl.append("g").attr("class", "svg-legend-label").selectAll("text").data(legendList).enter().append("text").text((d) => d.label).attr("index", (d, i) => i).attr("fill", legend_text_color).attr("font-size", legend_text_size).attr("font-family", legend_text_font).attr("dominant-baseline", "central").attr("class",(d, i) => `svg-legend-label-item svg-legend-label-item${i}`).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size * 2 + legendLabelSpace + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +11})`;});if (dotColorType == "mate") {let legendElHidth = d3.select(".svg-legend").node().getBoundingClientRect().height;const legendLiner = svg.append("g").attr("class", "svg-legend-liner").attr("transform",`translate(${isEdge? width - right - legendTotalw + boxPlotHeight + 15: width - right - legendTotalw + 10}, ${mTop + titleTotalH + legendElHidth + 45})`);legendLiner.append("g").attr("class", "svg-legend-title-liner").append("text").text(legend_title2).attr("fill", legend_title_color2).attr("font-family", legend_title_font2).attr("font-size", legend_title_size2).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);const defs = legendLiner.append("defs");const linearGradient = defs.append("linearGradient").attr("id", "linearColor").attr("height", 80).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%");linearGradient.append("stop").attr("offset", "0%").style("stop-color", colorListMateLiners[2]);linearGradient.append("stop").attr("offset", "50%").style("stop-color", colorListMateLiners[1]);linearGradient.append("stop").attr("offset", "100%").style("stop-color", colorListMateLiners[0]);legendLiner.append("rect").attr("x", 0).attr("y", 10).attr("width", 10).attr("height", 70).style("fill", "url(#" + linearGradient.attr("id") + ")");legendLiner.append("g").selectAll(".ledend_text").data(data_tip_backup).enter().append("text").attr("class", "legend-text").text((d) => {return d;}).attr("x", 15).attr("y", (_, i) => {if (i === 0) {return 80;} else if (i === 1) {return 50;} else {return 20;}}).attr("text-anchor", "start").attr("fill", "rgb(0, 0, 0)").attr("font-size", 12).attr("font-family", "Arial");}} else {legendEl.append("g").attr("class", "svg-legend-title").append("text").text(legend_title).attr("fill", legend_title_color).attr("font-family", legend_title_font).attr("font-size", legend_title_size).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);// 图例iconlegendEl.append("g").attr("class", "svg-legend-path").selectAll("path").data(dotColorType == "mate" ? legendShapeList : legendList).enter().append("path").attr("index", (d, i) => i).attr("class",(d, i) => `svg-legend-path-item svg-legend-path-item${i}`).attr("fill", (d, i) => {if (dotColorType == "mate") {return colorListMates[i % colorListMates.length];} else {return colors[i % colors.length];}}).attr("d", (d, i) => {let type = "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(legend_size * symbolZoomTimes);return symbol();}).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;}).on("mouseover", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseover", index, data.list);}}).on("mouseout", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendMouse("mouseout", index, data.list);}}).on("click", function (e, d) {if (dotColorType == "group") {let index = d3.select(this).attr("index");legendClick(index, colors[index]);}});// 图例标签legendEl.append("g").attr("class", "svg-legend-label").selectAll("text").data(dotColorType == "mate" ? legendShapeList : legendList).enter().append("text").text((d) => d.label).attr("index", (d, i) => i).attr("fill", legend_text_color).attr("font-size", legend_text_size).attr("font-family", legend_text_font).attr("dominant-baseline", "central").attr("class",(d, i) => `svg-legend-label-item svg-legend-label-item${i}`).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size * 2 + legendLabelSpace + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +11})`;});let legendElHidth = d3.select(".svg-legend").node().getBoundingClientRect().height;const legendShape = svg.append("g").attr("class", "svg-legend-shape").attr("transform",`translate(${isEdge? width - right - legendTotalw + boxPlotHeight + 15: width - right - legendTotalw + 10}, ${mTop + titleTotalH + legendElHidth + 45})`);legendShape.append("g").attr("class", "svg-legend-shape-title").append("text").text(legend_title2).attr("fill", legend_title_color2).attr("font-family", legend_title_font2).attr("font-size", legend_title_size2).attr("text-anchor", "middle").attr("dominant-baseline", "ideographic").attr("transform", `translate(${15}, ${0})`);legendShape.append("g").attr("class", "svg-legend-shape-label").selectAll("text").data(dotShapeType == "mate" ? legendShapeList : legendList).enter().append("text").text((d) => d.label).attr("index", (d, i) => i).attr("fill", legend_text_color2).attr("font-size", legend_text_size2).attr("font-family", legend_text_font2).attr("dominant-baseline", "central").attr("class",(d, i) => `svg-legend-label-item svg-legend-label-item${i}`).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size * 2 + legendLabelSpace + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;});legendShape.append("g").attr("class", "svg-legend-shape-path").selectAll("path").data(dotShapeType == "mate" ? legendShapeList : legendList).enter().append("path").attr("index", (d, i) => i).attr("class",(d, i) => `svg-legend-path-item svg-legend-path-item${i}`).attr("fill", "rgb(0, 0, 0)").attr("d", (d, i) => {if (dotShapeType == "mate") {const shapeType = d.label.trim();const symbolType = mateDot_types[shapeType];const symbolGenerator = d3.symbol().type(symbolTypes[symbolType]).size(dot_size * symbolZoomTimes);return symbolGenerator();} else {let group = data.list[i].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(legend_size * symbolZoomTimes);return symbol();}}).attr("transform", (d, i) => {let times = Math.floor(i / legendStep);return `translate(${legend_size + legendEachW * times}, ${legendLabelH / 2 +(legendLabelH + legendBottomSpace) * (i % legendStep) +10})`;});}}if (grid_enabled) {// 网格线Y轴d3.selectAll(".svg-x-axis .tick").append("line").attr("x1", 0).attr("y1", 0).attr("x2", 0).attr("y2", -chartHeight).attr("stroke-width", 1).attr("stroke", "rgba(0, 0, 0, 0.2)").style("stroke-dasharray", "5 5");d3.selectAll(".svg-y-axis .tick").append("line").attr("x1", 0).attr("y1", 0).attr("x2", chartWidth).attr("y2", 0).attr("stroke-width", 1).attr("stroke", "rgba(0, 0, 0, 0.2)").style("stroke-dasharray", "5 5");}// 原点辅助线if (auxiliary_line_enabled) {const auxiliaryEl = svg.append("g").attr("class", "svg-zero-line").attr("transform",`translate(${left + yTitleTotalW + yAxisW}, ${mTop + titleTotalH})`);auxiliaryEl.append("line").attr("class", "svg-zero-line-x").attr("x1", xScale(0) + 0.5).attr("y1", chartHeight).attr("x2", xScale(0) + 0.5).attr("y2", 0).attr("stroke", "rgba(0, 0, 0, 0.4)");auxiliaryEl.append("line").attr("class", "svg-zero-line-y").attr("x1", 0).attr("y1", yScale(0) + 0.5).attr("x2", chartWidth).attr("y2", yScale(0) + 0.5).attr("stroke", "rgba(0, 0, 0, 0.4)");}// 绘图区const groups = svg.append("g").attr("class", "svg-groups").attr("transform",`translate(${left + yTitleTotalW + yAxisW}, ${mTop + titleTotalH})`);if (ischeckLabel) {if (checkLabel) {const label = groups.append("text").text(checkLabel).attr("fill", "black").attr("font-size", 10);// 检验标签位置if (checkLabelPosition === "leftTop") {label.attr("x", 5).attr("y", 20).attr("text-anchor", "start");} else {label.attr("x", chartWidth - 5).attr("y", chartHeight - 10).attr("text-anchor", "end");}}}// 多边形(按组连线)if (dotColorType == "group" && plot_type === "polygon") {groups.append("g").attr("class", "svg-groups-hull").selectAll(".svg-groups-hull-group").data(data.list).enter().append("g").attr("fill-opacity", opacity).attr("fill", (d, i) => colors[i % colors.length]).attr("class",(d, i) => `svg-groups-hull-group svg-groups-hull-group${i}`).append("polygon").attr("stroke-width", 1).attr("stroke", (d, i) => colors[i % colors.length]).attr("stroke-dasharray", lineShapes[line_type]).attr("points", (d, i) => {let points = "";let data = (d.line[forwardValue] || d.line[reverseValue]).hull;let isAxisReverse = !d.line[forwardValue];let pcaXIndex = isAxisReverse ? 1 : 0;let pcaYIndex = isAxisReverse ? 0 : 1;data.forEach((item) => {points += `${xScale(item[pcaXIndex])}, ${yScale(item[pcaYIndex])} `;});return points;});}// 椭圆if (dotColorType == "group" && plot_type === "ellipse") {groups.append("g").attr("class", "svg-groups-ellipse").selectAll(".svg-groups-ellipse-group").data(data.list).enter().append("g").attr("fill-opacity", opacity).attr("fill", (d, i) => colors[i % colors.length]).attr("class",(d, i) => `svg-groups-ellipse-group svg-groups-ellipse-group${i}`).append("polygon").attr("stroke-width", 1).attr("stroke", (d, i) => colors[i % colors.length]).attr("stroke-dasharray", lineShapes[line_type]).attr("points", (d, i) => {let points = "";let isAxisReverse = !d.line[forwardValue];let pcaXIndex = isAxisReverse ? 1 : 0;let pcaYIndex = isAxisReverse ? 0 : 1;let data = (d.line[forwardValue] || d.line[reverseValue]).ellipse;data.forEach((item) => {points += `${xScale(item[pcaXIndex])}, ${yScale(item[pcaYIndex])} `;});return points;});}// 中心点连线if (center_line_enabled) {groups.append("g").attr("class", "svg-groups-line").selectAll(".svg-groups-line-group").data(data.list).enter().append("g").attr("index", (d, i) => i).attr("stroke-opacity", 0.3).attr("stroke", (d, i) => colors[i % colors.length]).attr("class",(d, i) => `svg-groups-line-group svg-groups-line-group${i}`).selectAll("line").data((d, i) => d.data).enter().append("line").attr("x1", (d, i, elList) => {let index = d3.select(elList[i].parentElement).attr("index");let centroids = data.list[index].center[0];return xScale(centroids[xIndex]);}).attr("y1", (d, i, elList) => {let index = d3.select(elList[i].parentElement).attr("index");let centroids = data.list[index].center[0];return yScale(centroids[yIndex]);}).attr("x2", (d) => xScale(d[xIndex + 1])).attr("y2", (d) => yScale(d[yIndex + 1]));}let uniqueMates = [];data.list.forEach((d) => {d.data.forEach((item) => {const mate = item[item.length - 1].trim();uniqueMates.push(mate);});});uniqueMates = [...new Set(uniqueMates)];let mateColorMap = uniqueMates.reduce((acc, mate, index) => {if (index < colorListMates.length) {acc[mate] = colorListMates[index];}return acc;}, {});// 散点if (dotColorType == "mate") {groups.append("g").attr("class", "svg-groups-scatter").selectAll(".svg-groups-scatter-group").data(data.list).enter().append("g").attr("index", (d, i) => i).attr("class", (d, i) => {return `svg-groups-common svg-groups-scatter-group svg-groups-scatter-group${i}`;}).selectAll("path").data((d) => d.data).enter().append("path").attr("d", (d, i, elList) => {if (data.dataType == "discrete" && dotShapeType == "mate") {const shapeType = d[d.length - 1].trim();const symbolType = mateDot_types[shapeType];const symbolGenerator = d3.symbol().type(symbolTypes[symbolType]).size(dot_size * symbolZoomTimes);return symbolGenerator();} else {let index = d3.select(elList[i].parentElement).attr("index");let group = data.list[index].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(dot_size * symbolZoomTimes);return symbol();}}).attr("transform",(d, i) =>`translate(${xScale(d[xIndex + 1])}, ${yScale(d[yIndex + 1])})`).attr("fill", (d, i) => {if (data.dataType == "liner" && dotColorType == "mate") {return colorScale(d[d.length - 1]);} else if (dotColorType == "mate") {return mateColorMap[d[d.length - 1]];}}).on("mouseover", function (e, d) {let index = d3.select(this.parentElement).attr("index");if (data.isMate) {tooltip({group: data.list[index].group,sample: d[0],xValue: d[xIndex + 1],yValue: d[yIndex + 1],mate: d[xIndex + 4],left: e.pageX + 15,top: e.pageY - 27,parentElement: this.parentElement});} else {tooltip({group: data.list[index].group,sample: d[0],xValue: d[xIndex + 1],yValue: d[yIndex + 1],left: e.pageX + 15,top: e.pageY - 27,parentElement: this.parentElement});}}).on("mouseout", function (d, i, elList) {d3.select(".scatter-tooltip").style("display", "none");});} else {groups.append("g").attr("class", "svg-groups-scatter").selectAll(".svg-groups-scatter-group").data(data.list).enter().append("g").attr("index", (d, i) => i).attr("fill", (d, i) => {return colors[i % colors.length];}).attr("class",(d, i) =>`svg-groups-common svg-groups-scatter-group svg-groups-scatter-group${i}`).selectAll("path").data((d) => d.data).enter().append("path").attr("d", (d, i, elList) => {if (data.dataType == "discrete" && dotShapeType == "mate") {const shapeType = d[d.length - 1].trim();const symbolType = mateDot_types[shapeType];const symbolGenerator = d3.symbol().type(symbolTypes[symbolType]).size(dot_size * symbolZoomTimes);return symbolGenerator();} else {let index = d3.select(elList[i].parentElement).attr("index");let group = data.list[index].group;let type = dot_types[group] || "circle";let symbol = d3.symbol().type(symbolTypes[type]).size(dot_size * symbolZoomTimes);return symbol();}}).attr("transform",(d, i) =>`translate(${xScale(d[xIndex + 1])}, ${yScale(d[yIndex + 1])})`).on("mouseover", function (e, d) {let index = d3.select(this.parentElement).attr("index");if (data.isMate) {tooltip({group: data.list[index].group,sample: d[0],xValue: d[xIndex + 1],yValue: d[yIndex + 1],mate: d[xIndex + 4],left: e.pageX + 15,top: e.pageY - 27,parentElement: this.parentElement});} else {tooltip({group: data.list[index].group,sample: d[0],xValue: d[xIndex + 1],yValue: d[yIndex + 1],left: e.pageX + 15,top: e.pageY - 27,parentElement: this.parentElement});}}).on("mouseout", function (d, i, elList) {d3.select(".scatter-tooltip").style("display", "none");});}if (isLabel) {groups.append("g").attr("class", "svg-groups-label").selectAll(".svg-groups-label-group").data(data.list).enter().append("g").attr("class",(d, i) => `svg-groups-label-group svg-groups-label-group${i}`).selectAll("text").data((d) => {return d.data;}).enter().append("text").text((d) => d[0]).attr("text-anchor", "middle").attr("fill", label_color).attr("font-size", label_size).attr("font-family", label_font).attr("x", (d) => xScale(d[xIndex + 1])).attr("y", (d) => yScale(d[yIndex + 1]) - dot_size);}if (isEdge) {// 边缘箱线图// 获取svg-x-axis、svg-y-axis元素的宽度和高度let svgXaxisWidth = d3.select(".svg-x-axis").node().getBoundingClientRect().width;let svgXaxisHeigth = d3.select(".svg-x-axis").node().getBoundingClientRect().height;let svgYaxisWidth = d3.select(".svg-y-axis").node().getBoundingClientRect().width;let svgYaxisHeight = d3.select(".svg-y-axis").node().getBoundingClientRect().height;if (dotColorType !== "mate" && isGroup) {if (plotType === "boxplot") {const topBoxplots = svg.append("g").attr("class", "top-boxplot").attr("transform", `translate(${left + yTitleTotalW + yAxisW}, 0)`);topBoxplots.selectAll("rect").data(data.list).enter().append("rect").attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y", (item, index) => index * (10 + 5)) //10表示箱体宽度 5表示每个箱体之间的间距.attr("height", 10).attr("width", (item) => {const val =xScale(item[combinationBoxplot[x_axis_value]][3]) -xScale(item[combinationBoxplot[x_axis_value]][1]);return val;}).attr("x", (item) =>xScale(item[combinationBoxplot[x_axis_value]][1])).attr("stroke-width", 1).attr("stroke", (d, i) => colors[i % colors.length]).attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity);// 绘制中心线(中位数线)topBoxplots.selectAll("line.median").data(data.list).enter().append("line").classed("median", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", (item, index) => index * (10 + 5)).attr("y2", (item, index) => index * (10 + 5) + 10).attr("x1", (item) =>xScale(item[combinationBoxplot[x_axis_value]][2])).attr("x2", (item) =>xScale(item[combinationBoxplot[x_axis_value]][2])).attr("stroke", "black");// 绘制左须线topBoxplots.selectAll("line.whisker-left").data(data.list).enter().append("line").classed("whisker-left", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", (item, index) => index * (10 + 5) + 5).attr("y2", (item, index) => index * (10 + 5) + 5).attr("x1", (item) =>xScale(item[combinationBoxplot[x_axis_value]][0])) // 左须线从最小值开始.attr("x2", (item) =>xScale(item[combinationBoxplot[x_axis_value]][1])).attr("stroke", "black");// 绘制右须线topBoxplots.selectAll("line.whisker-right").data(data.list).enter().append("line").classed("whisker-right", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", (item, index) => index * (10 + 5) + 5).attr("y2", (item, index) => index * (10 + 5) + 5).attr("x1", (item) =>xScale(item[combinationBoxplot[x_axis_value]][3])) // 右须线从Q3开始,.attr("x2", (item) =>xScale(item[combinationBoxplot[x_axis_value]][4])) // 右须线到最大值.attr("stroke", "black");//右侧箱线图const rightBoxplots = svg.append("g").attr("class", "right-boxplot").attr("transform",`translate(${left + yTitleTotalW + yAxisW + svgXaxisWidth + 7}, ${titleH ? titleH + boxPlotHeight + 10 : titleH + boxPlotHeight})`);rightBoxplots.selectAll("rect").data(data.list).enter().append("rect").attr("transform", `translate(0, ${0})`).attr("x", (item, index) => index * (10 + 5)).attr("width", 10).attr("height", (item) => {const val =yScale(item[combinationBoxplot[y_axis_value]][1]) -yScale(item[combinationBoxplot[y_axis_value]][3]);return val;}).attr("y", (item) =>yScale(item[combinationBoxplot[y_axis_value]][3])).attr("stroke-width", 1).attr("stroke", (d, i) => colors[i % colors.length]).attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity);// 绘制中心线(中位数线)rightBoxplots.selectAll("line.median").data(data.list).enter().append("line").classed("median", true).attr("x1", (item, index) => index * (10 + 5)).attr("x2", (item, index) => index * (10 + 5) + 10).attr("y1", (item) =>yScale(item[combinationBoxplot[y_axis_value]][2])).attr("y2", (item) =>yScale(item[combinationBoxplot[y_axis_value]][2])).attr("stroke", "black");// 绘制上须线rightBoxplots.selectAll("line.whisker-top").data(data.list).enter().append("line").classed("whisker-top", true).attr("x1", (item, index) => index * (10 + 5) + 5).attr("x2", (item, index) => index * (10 + 5) + 5).attr("y1", (item) =>yScale(item[combinationBoxplot[y_axis_value]][4])).attr("y2", (item) =>yScale(item[combinationBoxplot[y_axis_value]][3])).attr("stroke", "black");// 绘制下须线rightBoxplots.selectAll("line.whisker-bottom").data(data.list).enter().append("line").classed("whisker-bottom", true).attr("x1", (item, index) => index * (10 + 5) + 5).attr("x2", (item, index) => index * (10 + 5) + 5).attr("y1", (item) =>yScale(item[combinationBoxplot[y_axis_value]][0])).attr("y2", (item) =>yScale(item[combinationBoxplot[y_axis_value]][1])).attr("stroke", "black");}if (plotType === "areas") {const areaPlotsGroup = svg.append("g").attr("class", "areas-boxplot");// 为每个数据项创建一个g元素const areaPlots = areaPlotsGroup.selectAll(".area-plot").data(data.list).enter().append("g").attr("class", "area-plot").attr("transform", (item) => {return `translate(${left + yTitleTotalW + yAxisW},${titleH ? boxPlotHeight + titleH + 10 : boxPlotHeight})`;});const dScale = (topAreas) => {return d3.scaleLinear().domain([d3.min(topAreas, (d) => d.offset),d3.max(topAreas, (d) => d.offset)]).range([0, -boxPlotHeight + boxPlotMarginTop]);};const area = (d, topAreas) => {return d3.area().y0((d) => {return dScale(topAreas)(d.offset);}).x((d) => {return xScale(d.value);}).y1(0);};// 绘制面积密度图areaPlots.append("path").attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity).attr("d", (item) =>area(null,item[combinationAreas[x_axis_value]])(item[combinationAreas[x_axis_value]]));// 添加垂直面积图的g元素const verticalAreaPlotsGroup = svg.append("g").attr("class", "vertical-areas-boxplot");// 为每个数据项创建一个g元素(垂直面积图)const verticalAreaPlots = verticalAreaPlotsGroup.selectAll(".vertical-area-plot").data(data.list).enter().append("g").attr("class", "vertical-area-plot").attr("transform", (item) => {return `translate(${left + yTitleTotalW + svgYaxisWidth},${titleH ? boxPlotHeight + titleH + 10 : boxPlotHeight})`;});// 定义垂直方向的缩放比例尺;const dVScale = (rightAreas) => {return d3.scaleLinear().domain([d3.min(rightAreas, (d) => d.offset),d3.max(rightAreas, (d) => d.offset)]).range([0, boxPlotHeight]);};const areaV = (d, rightAreas) => {return d3.area().x0((d) => {return dVScale(rightAreas)(d.offset);}).y((d) => {return yScale(d.value);}).x1(0);};// 绘制垂直方向的面积图verticalAreaPlots.append("path").attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity).attr("d", (item) => {return areaV(null,item[combinationAreas[y_axis_value]])(item[combinationAreas[y_axis_value]]);});}if (plotType === "barPlot") {const numColumns =data.list[0][combinationBarplot[x_axis_value]].length;const topColumnWidth = (xDomain[1] - xDomain[0]) / numColumns;const rightColumnWidth = (yDomain[1] - yDomain[0]) / numColumns;const groupDataToPlot = {};data.list.forEach((item) => {groupDataToPlot[item.group] = item[combinationBarplot[x_axis_value]];});// 生成新的数据格式 上边缘const topData = Array.from({ length: numColumns }, (_, i) => {const x = xDomain[0] + i * topColumnWidth + topColumnWidth / 2; // 计算x坐标(柱子的中心)const plotData = {};// 遍历所有组,并将值添加到plotDatafor (const group of Object.keys(groupDataToPlot)) {plotData[group] = groupDataToPlot[group][i];}return { x, ...plotData };});const groupRDataToPlot = {};data.list.forEach((item) => {groupRDataToPlot[item.group] = item[combinationBarplot[x_axis_value]];});// 右 新数据const rightData = Array.from({ length: numColumns }, (_, i) => {const y = yDomain[0] + i * rightColumnWidth + rightColumnWidth / 2;const plotData = {};for (const group of Object.keys(groupRDataToPlot)) {plotData[group] = groupRDataToPlot[group][i];}return { y, ...plotData };});const keys = Object.keys(groupDataToPlot);const stackedData = d3.stack().keys(keys)(topData);const stackedData2 = d3.stack().keys(keys)(rightData);// 设置X轴和Y轴比例尺const topBarxScale = d3.scaleBand().domain(topData.map((d) => d.x)).range([0, svgXaxisWidth - 6]).padding(0.1);const topBaryScale = d3.scaleLinear().domain([0, d3.max(stackedData, (d) => d3.max(d, (d) => d[1]))]).range([boxPlotHeight - 10, 0]);const topBarPlotSvg = svg.append("g").attr("class", "bar-plot").attr("transform", (item) => {return `translate(${left + yTitleTotalW + yAxisW},${titleH ? titleH + 20 : titleH + 10})`;});// 绘制上边缘堆叠柱状图topBarPlotSvg.selectAll(".layer").data(stackedData).enter().append("g").attr("class", "layer").attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity).selectAll("rect").data((d) => d).enter().append("rect").attr("x", (d) => topBarxScale(d.data.x)).attr("y", (d) => topBaryScale(d[1])).attr("height", (d) => topBaryScale(d[0]) - topBaryScale(d[1])).attr("width", topBarxScale.bandwidth());// 定义y轴(垂直方向),使用scaleBandconst rightBaryScale = d3.scaleBand().domain(rightData.map((d) => d.y)).range([svgYaxisHeight, 0]).padding(0.1);// 定义x轴(水平方向),使用scaleLinearconst rightBarxScale = d3.scaleLinear().domain([0, d3.max(stackedData2, (d) => d3.max(d, (e) => e[1]))]).range([0, boxPlotHeight]);const rightBarPlotSvg = svg.append("g").attr("class", "right-bar-plot").attr("transform", (item) => {return `translate(${left + yTitleTotalW + svgYaxisWidth},${titleH ? titleH + boxPlotHeight + 10 : titleH + boxPlotHeight})`;});// 绘制右边缘水平堆叠柱状图rightBarPlotSvg.selectAll(".layer").data(stackedData2).enter().append("g").attr("class", "layer").attr("fill", (d, i) => colors[i % colors.length]).attr("fill-opacity", cloud_opacity).selectAll("rect").data((d) => d).enter().append("rect").attr("y", (d) => {return rightBaryScale(d.data.y);}).attr("x", (d) => rightBarxScale(d[0])).attr("width", (d) => {return rightBarxScale(d[1]) - rightBarxScale(d[0]);}).attr("height", rightBaryScale.bandwidth());}} else {if (plotType === "boxplot") {const topBoxplotsAll = svg.append("g").attr("class", "top-boxplot-all").attr("transform", `translate(${left + yTitleTotalW + yAxisW}, -10)`);topBoxplotsAll.selectAll("rect").data(data.ALL[combinationBoxplot[x_axis_value]]).enter().append("rect").attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y", 0).attr("height", allBoxplotHight).attr("width",xScale(data.ALL[combinationBoxplot[x_axis_value]][3]) -xScale(data.ALL[combinationBoxplot[x_axis_value]][1])).attr("x", xScale(data.ALL[combinationBoxplot[x_axis_value]][1])).attr("stroke-width", 1).attr("stroke", "rgba(0,0,0,0.5)").attr("fill", edgeColor).attr("fill-opacity", cloud_opacity);// 绘制中心线(中位数线)topBoxplotsAll.selectAll("line.median").data(data.ALL[combinationBoxplot[x_axis_value]]).enter().append("line").classed("median", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", 0).attr("y2", allBoxplotHight).attr("x1", xScale(data.ALL[combinationBoxplot[x_axis_value]][2])).attr("x2", xScale(data.ALL[combinationBoxplot[x_axis_value]][2])).attr("stroke", "black");// 绘制左须线topBoxplotsAll.selectAll("line.whisker-left").data(data.ALL[combinationBoxplot[x_axis_value]]).enter().append("line").classed("whisker-left", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", allBoxplotHight / 2).attr("y2", allBoxplotHight / 2).attr("x1", xScale(data.ALL[combinationBoxplot[x_axis_value]][0])).attr("x2", xScale(data.ALL[combinationBoxplot[x_axis_value]][1])).attr("stroke", "black");// 绘制右须线topBoxplotsAll.selectAll("line.whisker-right").data(data.ALL[combinationBoxplot[x_axis_value]]).enter().append("line").classed("whisker-right", true).attr("transform", `translate(0, ${titleTotalH + boxPlotMarginTop})`).attr("y1", allBoxplotHight / 2).attr("y2", allBoxplotHight / 2).attr("x1", xScale(data.ALL[combinationBoxplot[x_axis_value]][3])).attr("x2", xScale(data.ALL[combinationBoxplot[x_axis_value]][4])).attr("stroke", "black");//右侧箱线图const rightBoxplotsAll = svg.append("g").attr("class", "right-boxplot-all").attr("transform",`translate(${left + yTitleTotalW + yAxisW + svgXaxisWidth + 20}, ${titleH ? titleH + boxPlotHeight + 10 : titleH + boxPlotHeight})`);rightBoxplotsAll.selectAll("rect").data(data.ALL[combinationBoxplot[y_axis_value]]).enter().append("rect").attr("x", 0).attr("width", allBoxplotHight).attr("height",yScale(data.ALL[combinationBoxplot[y_axis_value]][1]) -yScale(data.ALL[combinationBoxplot[y_axis_value]][3])).attr("y", yScale(data.ALL[combinationBoxplot[y_axis_value]][3])).attr("stroke-width", 1).attr("stroke", "rgba(0,0,0,0.5)").attr("fill", edgeColor).attr("fill-opacity", cloud_opacity);// 绘制中心线(中位数线)rightBoxplotsAll.selectAll("line.median").data(data.ALL[combinationBoxplot[y_axis_value]]).enter().append("line").classed("median", true).attr("x1", 0).attr("x2", allBoxplotHight).attr("y1", yScale(data.ALL[combinationBoxplot[y_axis_value]][2])).attr("y2", yScale(data.ALL[combinationBoxplot[y_axis_value]][2])).attr("stroke", "black");// 绘制上须线rightBoxplotsAll.selectAll("line.whisker-top").data(data.ALL[combinationBoxplot[y_axis_value]]).enter().append("line").classed("whisker-top", true).attr("x1", allBoxplotHight / 2).attr("x2", allBoxplotHight / 2).attr("y1", yScale(data.ALL[combinationBoxplot[y_axis_value]][4])).attr("y2", yScale(data.ALL[combinationBoxplot[y_axis_value]][3])).attr("stroke", "black");// 绘制下须线rightBoxplotsAll.selectAll("line.whisker-bottom").data(data.ALL[combinationBoxplot[y_axis_value]]).enter().append("line").classed("whisker-bottom", true).attr("x1", allBoxplotHight / 2).attr("x2", allBoxplotHight / 2).attr("y1", yScale(data.ALL[combinationBoxplot[y_axis_value]][0])).attr("y2", yScale(data.ALL[combinationBoxplot[y_axis_value]][1])).attr("stroke", "black");}if (plotType === "areas") {let topAreas = data.ALL[combinationAreas[x_axis_value]];let rightAreas = data.ALL[combinationAreas[y_axis_value]];// 创建比例尺实例const dScaleAll = d3.scaleLinear().domain([d3.min(topAreas, (d) => d.offset),d3.max(topAreas, (d) => d.offset)]).range([0, -boxPlotHeight + boxPlotMarginTop]);// 创建面积生成器const areaGenerator = d3.area().y1(0).x((d) => xScale(d.value)).y0((d) => dScaleAll(d.offset));// 创建面积图组const areaPlotsGroupAll = svg.append("g").attr("class", "areas-all");// 为每个topArea创建一个g元素和路径const areaPlotsAll = areaPlotsGroupAll.selectAll(".area-plot-all").data([topAreas]).enter().append("g").attr("class", "area-plot-all").attr("transform",`translate(${left + yTitleTotalW + yAxisW + 2},${titleH ? boxPlotHeight + titleH + 7 : boxPlotHeight + titleH})`);// 绘制路径areaPlotsAll.append("path").datum(topAreas).attr("fill", edgeColor).attr("fill-opacity", cloud_opacity).attr("d", areaGenerator);//垂直!const dScaleAllV = d3.scaleLinear().domain([d3.min(rightAreas, (d) => d.offset),d3.max(rightAreas, (d) => d.offset)]).range([0, boxPlotHeight]);const areaGeneratorV = d3.area().x0((d) => dScaleAllV(d.offset)).y((d) => yScale(d.value)).x1(0);// 创建面积图组const areaPlotsGroupAllV = svg.append("g").attr("class", "areas-all-v");const areaPlotsAllV = areaPlotsGroupAllV.selectAll(".area-plot-all-v").data([rightAreas]).enter().append("g").attr("class", "area-plot-all-v").attr("transform",`translate(${left + yTitleTotalW + yAxisW + svgXaxisWidth},${titleH ? boxPlotHeight + titleH + 8 : boxPlotHeight + titleH})`);// 绘制路径areaPlotsAllV.append("path").datum(rightAreas).attr("fill", edgeColor).attr("fill-opacity", cloud_opacity).attr("d", areaGeneratorV);}if (plotType === "barPlot") {// 设置X轴和Y轴比例尺const topBarxScaleAll = d3.scaleBand().domain(d3.range(data.ALL[combinationBarplot[x_axis_value]].length)).range([0, svgXaxisWidth]).padding(0.1);const topBaryScaleAll = d3.scaleLinear().domain([0, d3.max(data.ALL[combinationBarplot[x_axis_value]])]).range([boxPlotHeight - 10, 0]);const topBarPlotSvgAll = svg.append("g").attr("class", "bar-plot-all").attr("transform",`translate(${left + yTitleTotalW + yAxisW},${titleH ? titleH + 8 : titleH - 2})`);topBarPlotSvgAll.selectAll(".bar").data(data.ALL[combinationBarplot[x_axis_value]]).enter().append("rect").attr("class", "bar").attr("x", (d, i) => topBarxScaleAll(i)).attr("width", topBarxScaleAll.bandwidth()).attr("y", (d) => topBaryScaleAll(d)).attr("height", (d) => boxPlotHeight - topBaryScaleAll(d)).attr("fill", edgeColor).attr("fill-opacity", cloud_opacity);// 设置水平X轴和Y轴比例尺const topBaryScaleAllV = d3.scaleBand().domain(d3.range(data.ALL[combinationBarplot[y_axis_value]].length)).range([0, svgYaxisHeight]).padding(0.1);const topBarxScaleAllV = d3.scaleLinear().domain([0, d3.max(data.ALL[combinationBarplot[y_axis_value]])]).range([boxPlotHeight, 0]);const topBarPlotSvgAllV = svg.append("g").attr("class", "bar-plot-all-v").attr("transform", (item) => {return `translate(${left + yTitleTotalW + yAxisW + svgXaxisWidth},${titleH ? titleH + boxPlotHeight + 10 : titleH + boxPlotHeight})`;});// 绘制水平柱状图topBarPlotSvgAllV.selectAll(".bar").data(data.ALL[combinationBarplot[y_axis_value]]).enter().append("rect").attr("class", "bar").attr("y", (d, i) => {return topBaryScaleAllV(i);}).attr("height", topBaryScaleAllV.bandwidth()).attr("x", 2).attr("width", (d) => {return topBarxScaleAllV(0) - topBarxScaleAllV(d);}).attr("fill", edgeColor).attr("fill-opacity", cloud_opacity);}}}
};export default pcaCombinationEdgeChart;

 页面调用:

 pcaCombinationEdgeChart({ data: plots, ...chartParam });

小小的展示一下部分效果吧

 

大概是去年年底研发完成的吧 实在是太多了太绕了阿!
有什么不懂的就自己研究吧,我反正是写完就忘了

相关文章:

d3.js绘制组合PCA边缘分布图

用d3.js研发了个组合PCA边缘分布图&#xff1b; 组合PCA边缘分布图中包括pca散点图、散点图可根据数据自动分为连续型和离散型、还有散点的各种配置、边缘有箱线边缘、密度边缘、柱状边缘一个各个边缘的配置等等&#xff0c;大部分你能想到的配置都是自行传参调整的&#xff0…...

开源语音合成模型SparkTTS使用

一、环境配置 git clone https://github.com/SparkAudio/Spark-TTS.git pip install -r requirements.txt 二、模型下载 从modelscope进行下载&#xff0c;pip install modelscope 创建一个download.py import torchfrom modelscope import snapshot_downloadsnapshot_dow…...

课程9. 数据降维

课程9. 数据降维 维度灾难奇异值分解SVD 变换SVD 的几何意义 SVD分解应用示例图像压缩文本分析推荐系统中的应用* 主成分分析PCA演示使用 PCA 降低多元数据的维数PCA 说明单词的语义相似性 t-SNE 维度灾难 机器学习和数据科学中的关键问题之一是数据高维性问题。我们已经遇到过…...

24-25【动手学深度学习】AlexNet + Vgg

1. AlexNet 1.1 原理 1.2 代码 import torch from torch import nn from d2l import torch as d2lnet nn.Sequential(nn.Conv2d(1, 96, kernel_size11,stride4, padding1), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2),nn.Conv2d(96, 256, kernel_size5, padding2), nn.…...

1.Axum 与 Tokio:异步编程的完美结合

摘要 深入解析 Axum 核心架构与 Tokio 异步运行时的集成&#xff0c;掌握关键原理与实践技巧。 一、引言 在当今的软件开发领域&#xff0c;高并发和高性能是衡量一个系统优劣的重要指标。对于 Web 服务器而言&#xff0c;能够高效地处理大量并发请求是至关重要的。Rust 语言…...

快速认识:数据库、数仓(数据仓库)、数据湖与数据运河

数据技术核心概念对比表 概念核心定义核心功能数据特征典型技术/工具核心应用场景数据库结构化数据的「电子档案柜」&#xff0c;按固定 schema 存储和管理数据&#xff0c;支持高效读写和事务处理。实时事务处理&#xff08;增删改查&#xff09;&#xff0c;确保数据一致性&…...

【Linux】第十章 配置和保护SSH

1. 简单说下ssh如何实现用户的免密登录&#xff1f; &#xff08;1&#xff09;生成公钥和私钥&#xff1a;使用 ssh-keygen -t rsa 命令&#xff0c;在客户端&#xff08;即你登录的机器&#xff09;上生成一对密钥——公钥&#xff08;~/.ssh/id_rsa.pub&#xff09;和私钥&…...

量子计算:开启未来科技之门的钥匙

在当今科技飞速发展的时代&#xff0c;量子计算正逐渐从实验室走向实际应用&#xff0c;成为全球科技领域的焦点之一。它有望为众多行业带来前所未有的变革&#xff0c;从密码学、药物研发到金融风险评估等&#xff0c;量子计算的潜力不可限量。 一、量子计算的原理 量子计算基…...

基础知识 - 结构体

1、结构体类型与结构体变量 1.1 结构体的定义 结构体是一种自定义的数据类型&#xff0c;它把多个不同类型的变量封装在一起&#xff0c;形成一个新的复合数据类型。可以定义该结构体类型的变量&#xff0c;与使用 int 定义变量的方法相同 结构体是一些值的集合&#xff0c;这…...

uniapp上传图片时(可选微信头像、相册、拍照)

参考文献&#xff1a;微信小程序登录——头像_onchooseavatar-CSDN博客 <button open-type"chooseAvatar" chooseavatar"onChooseAvatar"> </button>onChooseAvatar(e) {uni.showLoading({title: 上传中...,mask: true});uni.uploadFile({url…...

2025年4月16日华为笔试第二题200分

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 智慧旅游路线规划 问题描述 LYA正在开发一款智慧旅游APP,该APP需要为游客规划城市景点之间的最佳路线。城市有 N N...

面试题之高频面试题

最近开始面试了&#xff0c;410面试了一家公司 针对自己薄弱的面试题库&#xff0c;深入了解下&#xff0c;也应付下面试。在这里先祝愿大家在现有公司好好沉淀&#xff0c;定位好自己的目标&#xff0c;在自己的领域上发光发热&#xff0c;在自己想要的领域上&#xff08;技术…...

一路磕磕绊绊解决flutter doctor 报错CocoaPods not installed

flutter doctor执行之后&#xff0c;出现以下错误: 错误消息&#xff1a; ✗ CocoaPods not installed.CocoaPods is a package manager for iOS or macOS platform code.Without CocoaPods, plugins will not work on iOS or macOS.For more info, see https://flutter.dev/t…...

探寻Gson解析遇到不存在键值时引发的Kotlin的空指针异常的原因

文章目录 一、问题背景二、问题原因三、问题探析Kotlin空指针校验Gson.fromJson(String json, Class<T> classOfT)TypeTokenGson.fromJson(JsonReader reader, TypeToken<T> typeOfT)TypeAdapter 和 TypeAdapterFactoryReflectiveTypeAdapterFactoryRecordAdapter …...

面试算法高频08-动态规划-01

动态规划 递归知识要点 递归代码模板&#xff1a;提供递归代码的标准形式public void recur(int level, int param) &#xff0c;包含终止条件&#xff08;if (level> MAX_LEVEL)&#xff09;、当前层逻辑处理&#xff08;process(level, param)&#xff09;、向下一层递归…...

【AI】以Llama模型为例学习如何进行LLM模型微调

以Llama模型为例学习如何进行LLM模型微调 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 以Llama模型为例学习如何进行LLM模型微调背景预训练微调全部微调参数高效微调低秩适配 (LoR…...

细说STM32单片机FreeRTOS任务管理API函数vTaskList()的使用方法

目录 一、函数vTaskList() 1、 函数说明 2、返回的字符串表格说明 3、函数的使用方法 二、 vTaskList()的应用示例 1、示例功能、项目设置 2、软件设计 &#xff08;1&#xff09;main.c &#xff08;2&#xff09;freertos.c &#xff08;3&#xff09;FreeRTOSConf…...

ffmpeg 添加 nvenc支持

运行以下命令检查当前 FFmpeg 是否支持 hevc_nvenc&#xff1a; ffmpeg -hide_banner -encoders | grep nvenc 若输出包含 hevc_nvenc&#xff0c;说明编码器已集成&#xff0c;问题出在驱动或参数配置若无输出&#xff0c;则需要手动编译 ffmpeg 安装显卡驱动、cuda和cudnn…...

锚定效应的应用-独立站优化价格打折显示-《认知偏差手册》

锚定效应的应用-独立站优化价格打折显示-《认知偏差手册》 先看结果&#xff1a;价格展示 https://atemplate.com/pricing 旧的打折价格展示 新的打折价格展示 锚定效应是什么&#xff1f; 人类在进行决策时&#xff0c;会过度偏重先前取得的资讯&#xff08;这称为锚点&…...

红宝书第四十九讲:XSS/CSRF攻击防御策略解析

红宝书第四十九讲&#xff1a;XSS/CSRF攻击防御策略解析 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 XSS&#xff08;跨站脚本&#xff09;&#xff1a;黑客把恶意代码塞进网页&#xff0c;当你打开页面时&am…...

Unity基于屏幕空间的鼠标拖动,拖动物体旋转

代码的核心在于&#xff0c;鼠标的屏幕偏移映射到物体的旋转角度&#xff0c;代码中是使用射线去检测的&#xff0c;检测帧间隔鼠标的位置对应物体上的旋转 未解决的问题&#xff1a;旋转都是相对的&#xff0c;怎么去处理&#xff0c;鼠标拖动物体&#xff0c;物体不动&#…...

Unity3D 测试驱动开发(TDD)框架设计

前言 针对Unity3D测试驱动开发&#xff08;TDD&#xff09;框架的设计&#xff0c;需要结合Unity引擎特性与TDD核心原则&#xff0c;构建可维护、高效且与开发流程深度集成的测试体系。以下是分层次的框架设计方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&a…...

Google Mock(GMock):C++单元测试的高效模拟框架详解

标题&#xff1a; Google Mock&#xff08;GMock&#xff09;&#xff1a;C单元测试的高效模拟框架详解 摘要&#xff1a; Google Mock&#xff08;GMock&#xff09;是C单元测试中的核心工具&#xff0c;能够高效隔离外部依赖并验证复杂交互逻辑。本文详细介绍了GMock的核心…...

智慧城市气象中台架构:多源天气API网关聚合方案

在开发与天气相关的应用时&#xff0c;获取准确的天气信息是一个关键需求。万维易源提供的“天气预报查询”API为开发者提供了一个高效、便捷的工具&#xff0c;可以通过简单的接口调用查询全国范围内的天气信息。本文将详细介绍如何使用该API&#xff0c;以及其核心功能和调用…...

vue3项目启动bug

项目场景&#xff1a; vue3 项目启动运行 问题描述 终端无法正常启动运行 C:/user/adminC:/user/admin> npm run dev > student_status_vue30.0.0 dev > vite原因分析&#xff1a; 暂无 解决方案&#xff1a; 在当前项目目录下运行&#xff1a; npx vite --host…...

逻辑回归 (Logistic Regression)

文章目录 逻辑回归 (Logistic Regression)问题的引出Sigmoid function逻辑回归的解释决策边界 (Decision boundary)逻辑回归的代价函数机器学习中代价函数的设计1. 代价函数的来源&#xff08;1&#xff09;从概率模型推导而来&#xff08;统计学习视角&#xff09;&#xff08…...

SLAM | 激光SLAM中的退化问题

在激光SLAM中,判断退化环境的核心是通过数学建模分析环境特征对位姿估计的约束能力。除了LOAM中提出的退化因子D外,还存在多种基于表达式和阈值设定的方法。以下是几种典型方法及其实现原理: 1. 协方差矩阵特征值分析 原理:通过分析点云协方差矩阵的特征值分布,判断环境中…...

【已更新】2025华中杯B题数学建模网络挑战赛思路代码文章教学:校园共享单车的调度与维护问题

完整内容请看文末最后的推广群 先展示问题一代码和结果、再给出四个问题详细的模型 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from matplotlib.font_manager import FontPropertiesfrom matplotlib import rcParams# 设…...

[特殊字符] 基于大模型的地理领域文档中英互译自动化方案

一、&#x1f4cc; 项目背景与挑战 在全球化商业环境中&#xff0c;跨国企业经常面临专业文档翻译的痛点&#xff1a; 传统方式效率低下&#xff1a;专业文档翻译需要专人耗时数小时甚至数天 专业术语准确性难保证&#xff1a;地理领域术语的特殊性 格式保持困难&#xff1a…...

破局遗留系统!AI自动化重构:从静态方法到Spring Bean注入实战

在当今快速发展的软件行业中,许多企业都面临着 Java 遗留系统的维护和升级难题。这些老旧系统往往采用了大量静态方法,随着业务的不断发展,其局限性日益凸显。而飞算 JavaAI 作为一款强大的 AI 工具,为 Java 遗留系统的重构提供了全新的解决方案,能够实现从静态方法到 Spring B…...

高度图(Heightmap)

高度图的数学组成与建模方法 高度图&#xff08;Heightmap&#xff09;是一种基于规则网格的地形表示方法&#xff0c;其数学本质是将三维地形简化为二维离散函数&#xff0c;通过高度值的存储和插值实现地形重建。以下从数学建模角度系统阐述其组成原理及关键技术。 一、基础…...

2025第十七届“华中杯”大学生数学建模挑战赛题目B 题 校园共享单车的调度与维护问题完整思路 模型 代码 结果分享

共享单车目前已成为不少大学校园内学生的重要通勤工具&#xff0c;给学生的出行带来了极大便利&#xff0c;但同时也产生了一些问题&#xff0c;如共享单车投放点位设计不合理&#xff0c;高峰期运力不足等。 某高校委托一公司在校园内投放了一批共享单车&#xff0c;经过一段时…...

ESP32-idf学习(一)搭建环境和点灯

一、前言 先说一下查到的数据&#xff08;不保证准确&#xff09;&#xff1a; 1、连续四年Wi-Fi MCU全球市场份额第一&#xff0c;产品应用于智能家居、工业自动化、医疗健康等泛IoT领域‌&#xff0c;2024 年营收突破 20 亿元&#xff08;同比 40%&#xff09;&#xff0c;…...

超详细VMware虚拟机扩容磁盘容量-无坑版

1.环境&#xff1a; 虚拟机&#xff1a;VMware Workstation 17 Pro-17.5.2 Linux系统&#xff1a;Ubuntu 22.04 LTS 2.硬盘容量 虚拟机当前硬盘容量180G -> 扩展至 300G 3.操作步骤 &#xff08;1&#xff09;在虚拟机关机的状态下&#xff0c;虚拟机硬盘扩容之前必…...

多线程(进阶续~)(内涵面试题)

目录 一、JUC 的常见类 1. Callable 接口 2. ReentrantLock ReentrantLock 的用法&#xff1a; ReentrantLock 和 synchronized 的区别&#xff1a; 何时使用何锁: 3. 原子类 4. 线程池 ExecutorService 和 Executors ThreadPoolExecutor 5. 信号量 Semaphore 6. C…...

OpenGL shader开发实战学习笔记:第十一章 立方体贴图和天空盒

1. 立方体贴图和天空盒 1.1. 什么是立方体贴图 立方体贴图&#xff08;Cube Map&#xff09;是一种纹理&#xff0c;它由六个纹理图像组成&#xff0c;每个纹理图像对应一个方向。这些方向通常是立方体的六个面&#xff0c;分别是“前面”&#xff0c;“后面”&#xff0c;“…...

双指针算法(二)

目录 一、力扣611——有效三角形的个数 二、牛客网3734——和为S的两个数字 三、力扣15——三数之和 四、力扣18——四数之和 一、力扣611——有效三角形的个数 题目如下&#xff1a; 这里我们先认识如何判断是个三角形&#xff0c;ab>c,ac>b,bc>a即为三角形 这里…...

docker Windows 存放位置

docker Windows 存放位置 镜像文件层可能是这 docker的overlay2中存的都是什么and如何清理/var/lib/docker/overlay2_docker overlay 是什么目录-CSDN博客 存的是我们的镜像文件和容器内的文件 \\wsl.localhost\docker-desktop\mnt\docker-desktop-disk\data\docker\overla…...

每日一题(小白)暴力娱乐篇31

首先分析一下题意&#xff0c;需要求出2024的因子&#xff0c;因为我们要求与2024互质的数字&#xff0c;为什么呢&#xff1f;因为我们要求互质说直白点就是我和你两个人没有中间人&#xff0c;我们是自然而然认识的&#xff0c;那我们怎么认识呢&#xff0c;就是直接见面对吧…...

FastAPI与SQLAlchemy数据库集成

title: FastAPI与SQLAlchemy数据库集成 date: 2025/04/17 15:33:34 updated: 2025/04/17 15:33:34 author: cmdragon excerpt: FastAPI与SQLAlchemy的集成通过创建虚拟环境、安装依赖、配置数据库连接、定义数据模型和实现路由来完成。核心模块包括数据库引擎、会话工厂和声…...

SQL刷题记录贴

1.题目&#xff1a;现在运营想要对用户的年龄分布开展分析&#xff0c;在分析时想要剔除没有获取到年龄的用户&#xff0c;请你取出所有年龄值不为空的用户的设备ID&#xff0c;性别&#xff0c;年龄&#xff0c;学校的信息。 错误&#xff1a;select device_id,gender,age,un…...

消息队列实际结点数与计数器不一致问题分析

问题描述 协议栈 PDCP线程任根据外部消息&#xff0c;维护一个链表式的PDCP PDU消息队列&#xff0c;以及一个变量count来记录消息队列中结点数。 当收到 从NG接口业务数据时&#xff0c;PDCP线程会向PDCP PDU消息队列中添加大量节点&#xff0c;消息队列的count值相应的增加…...

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年4月17日第55弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀6-8个和值&#xff0c;可以做到100-300注左右。 (1)定…...

C++23 新特性:std::size_t 字面量后缀 Z/z

在 C23 中&#xff0c;引入了一个非常实用的新特性&#xff1a;为 std::size_t 类型的字面量提供了新的后缀 Z 和 z。这一改进使得在代码中声明和使用 std::size_t 类型的字面量变得更加直观和便捷。 1. 背景与动机 在之前的 C 标准中&#xff0c;std::size_t 是一种非常常用…...

【裁员感想】

裁员感想 今天忽然感觉很emo 因为知道公司要裁员 年中百分之10 年末百分十10 我知道这个百分20会打到自己 所以还挺不开心的 我就想起 我的一个亲戚当了大学老师 我觉得真的挺好的 又有寒暑假 又不是很累 薪资也不低 又是编制 同时也觉得自己很失败 因为对自己互联网的工作又…...

CSS例子 > 图片瀑布流布局(vue2)

<template><div class"container"><!-- 临时容器用于计算高度 --><div v-if"!isLayoutReady" class"temp-container"><divv-for"(item, index) in list":key"temp- index":ref"(el) > …...

Python 获取淘宝券后价接口的详细指南

在电商领域&#xff0c;淘宝作为国内领先的电商平台&#xff0c;提供了丰富的商品和优惠活动。对于开发者来说&#xff0c;获取淘宝商品的券后价是一个极具价值的功能&#xff0c;可以帮助用户更好地进行购物决策&#xff0c;同时也为相关应用和服务提供了数据支持。本文将详细…...

零服务器免备案!用Gitee代理+GitHub Pages搭建个人博客:绕过443端口封锁实战记录

#GitHub Pages #Gitee代理 #SSH密钥管理 #Jekyll博客 #网络穿透 场景&#xff1a;自己的电脑没有添加github的ssh代理&#xff0c;只有gitee的代理 实现效果&#xff0c;在公网可以运行个人博客。在本地更改内容后公网同步更新。 最开始的模板 最终实现的博客模板&#xff1…...

如何新建一个空分支(不继承 master 或任何提交)

一、需求分析&#xff1a; 在 Git 中&#xff0c;我们通常通过 git branch 来新建分支&#xff0c;这些分支默认都会继承当前所在分支的提交记录。但有时候我们希望新建一个“完全干净”的分支 —— 没有任何提交&#xff0c;不继承 master 或任何已有内容&#xff0c;这该怎么…...

[终极版]Javascript面试全解

this指向 执行上下文 是代码执行时的运行环境作用域 是变量和函数的可访问性规则&#xff08;静态&#xff09;&#xff1b;全局、函数和块状&#xff1b;内层可访问外层&#xff0c;外层不能访问内层词法环境 是实现作用域的引擎内部机制&#xff08;静态&#xff09; 执行上…...