关于d3js生成节点画布的个人笔记

news2024/11/26 4:36:35

实现功能

  1. 根据鼠标位置生成节点
  2. 根据节点位置通过鼠标拖拽生成连线
  3. 实现自定义线段颜色功能
  4. 删除节点以及连线功能
  5. 实现单个节点拖动功能
  6. 实现整条线路的拖动功能
    界面如下:

主要模块介绍

绘制连线

const line = svg.selectAll(".line")
    .data(links, d => `${d.source}-${d.target}`);
line.enter()
    .append("line")
    .attr("class", "line")
    .attr("stroke", d => d.color)
    .attr("id", d => d.objId)
    .merge(line)
    .attr("x1", d => nodes.find(n => n.id === d.source).x)
    .attr("y1", d => nodes.find(n => n.id === d.source).y)
    .attr("x2", d => nodes.find(n => n.id === d.target).x)
    .attr("y2", d => nodes.find(n => n.id === d.target).y)
    });
line.exit().remove();

绘制节点

设置为四种节点类型,初始节点,杆塔,开关,电表箱

const node = svg.selectAll(".node")
    .data(nodes, d => d.id);
node.enter()
	// 这里可以换成circle,加入属性r,10换成原型节点,移除xlink
    .append("image")
    .attr("class", d => `node ${d.type}`)
    .attr("width", iconSize)
    .attr("height", iconSize)
    .attr("xlink:href", d => {
        if (d.type === 'pole') return './pole.png';
        if (d.type === 'meterBox') return './meterBox.png';
        if (d.type === 'switch') return './switch.png';
        if (d.type === 'init') return './init.png';
    })
    .merge(node)
    .attr("x", d => d.x - iconSize / 2)
    .attr("y", d => d.y - iconSize / 2)
    .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended)
    )
    .on("click", function(event, d) {
        if (isDeletingNode) {
            links = links.filter(link => link.source !== d.id && link.target !== d.id);
            nodes = nodes.filter(node => node !== d);
            update();
            // isDeletingNode = false;
        }else{
            console.log("点击了节点", d.name);
        }
    });
node.exit().remove();

添加节点

svg.on("click", function(event) {
    if (isAddingNode) {
        const coords = d3.pointer(event);
        const id = getUuid();
        const name = `Node${nodes.length + 1}`;
        const type = nodeType;
        nodes.push({ id, name, type, x: coords[0], y: coords[1] });
        update();
    }
});

完整实现如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive Line Loss Diagram with D3.js</title>
    <script src="https://d3js.org/d3.v6.min.js"></script>    
    <style>
        .pole {
            fill: orange;
            stroke: black;
            stroke-width: 1px;
        }
        .meterBox {
            fill: green;
            stroke: black;
            stroke-width: 1px;
        }
        .gateway {
            fill: blue;
            stroke: black;
            stroke-width: 1px;
        }
        .switch {
            fill: gray;
            stroke: black;
            stroke-width: 1px;
        }
        .line {
            stroke-width: 2px;
        }
        .label {
            font-size: 12px;
            text-anchor: middle;
        }
        .button-container {
            margin-bottom: 10px;
        }
        button {
            margin-right: 10px;
        }
        #colorPicker {
            display: none;
            position: absolute;
        }
    </style>
</head>
<body>
    <div class="button-container">
        <button id="addNodeButton" onclick="addNodeButton('pole')">新增节点</button>
        <button id="addNodeButton" onclick="addNodeButton('meterBox')">新增表箱</button>
        <button id="addNodeButton" onclick="addNodeButton('switch')">新增开关</button>
        <button id="addLinkButton">连线</button>
        <button id="deleteNodeButton">删除节点</button>
        <button id="deleteLinkButton">删除连线</button>
        <button onclick="logNodeButton()">打印节点</button>
        <button onclick="selectButton()">选择节点</button>
        <button onclick="selectAllButton()">整体拖动</button>
        <input type="color" id="colorPicker">
        <button
            onclick="updateColor()"
        >
            更新颜色
        </button>
    </div>
    </div>
    <svg width="800" height="600">
        <defs>
            <pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
                <path d="M 20 0 L 0 0 0 20" fill="none" stroke="gray" stroke-width="0.5"/>
            </pattern>
        </defs>
        <rect width="100%" height="100%" fill="url(#grid)" />
    </svg>
    <script src="test.js"></script>
    <script>
        const svg = d3.select("svg");
        // 节点以及连线的拖动行为
        const dragNode = d3.drag()
            .on("start", dragNodeStarted)
            .on("drag", draggedNode)
            .on("end", dragNodeEnded);
  
        function dragNodeStarted(event, d) {
            d3.select(this).raise().classed("active", true);
        }
  
        function draggedNode(event, d) {
            d.x = event.x;
            d.y = event.y;
  
            d3.select(this)
                .attr("cx", d.x)
                .attr("cy", d.y);
  
            // 更新连线的位置
            svg.selectAll(".line")
                .attr("x1", l => nodes.find(n => n.id === l.source).x)
                .attr("y1", l => nodes.find(n => n.id === l.source).y)
                .attr("x2", l => nodes.find(n => n.id === l.target).x)
                .attr("y2", l => nodes.find(n => n.id === l.target).y);
        }
  
        function dragNodeEnded(event, d) {
            d3.select(this).classed("active", false);
        }
        // let nodes = [];
        // let links = [];
        let links = [];
        let nodes = [{
            "id": "ec61a8f4-73ec-46fe-adb2-4473119a006c",
            "name": "init",
            "type": "init",
            "x": 119.99999237060547,
            "y": 43.71427917480469
        }];
  
        // 虚拟数据

//        nodes = jsonData.nodes;
//      links = jsonData.links;

  

        let isAddingNode = false;
        let isAddingLink = false;
        let isDeletingNode = false;
        let isDeletingLink = false;
        let sourceNode = null;
        let dragLine = null;
        let nodeType = null;
        let selectedLink   = null;
        let allDrag = false;
        const iconSize = 20;

  

        // 绘制节点和连线
        function update() {
            // 绘制连线
            const line = svg.selectAll(".line")
                .data(links, d => `${d.source}-${d.target}`);
            line.enter()
                .append("line")
                .attr("class", "line")
                .attr("stroke", d => d.color)
                .attr("id", d => d.objId)
                .merge(line)
                .attr("x1", d => nodes.find(n => n.id === d.source).x)
                .attr("y1", d => nodes.find(n => n.id === d.source).y)
                .attr("x2", d => nodes.find(n => n.id === d.target).x)
                .attr("y2", d => nodes.find(n => n.id === d.target).y)
                .on("click", function(event, d) {
                    if (isDeletingLink) {
                        links = links.filter(link => link !== d);
                        update();
                        // isDeletingLink = false;
                    } else{
                        selectedLink = d;
                        const colorPicker = document.getElementById("colorPicker");
                        colorPicker.style.display = "block";
                        colorPicker.style.left = `${event.pageX}px`;
                        colorPicker.style.top = `${event.pageY}px`;
                        colorPicker.value = d.color || "#000000";
                        colorPicker.focus();
                    }
                });

            line.exit().remove();

            // 绘制节点
            const node = svg.selectAll(".node")
                .data(nodes, d => d.id);
            node.enter()
                .append("image")
                .attr("class", d => `node ${d.type}`)
                .attr("width", iconSize)
                .attr("height", iconSize)
                .attr("xlink:href", d => {
                    if (d.type === 'pole') return './pole.png';
                    if (d.type === 'meterBox') return './meterBox.png';
                    if (d.type === 'switch') return './switch.png';
                    if (d.type === 'init') return './init.png';
                })
                .merge(node)
                .attr("x", d => d.x - iconSize / 2)
                .attr("y", d => d.y - iconSize / 2)
                .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended)
                )
                .on("click", function(event, d) {
                    if (isDeletingNode) {
                        links = links.filter(link => link.source !== d.id && link.target !== d.id);
                        nodes = nodes.filter(node => node !== d);
                        update();
                        // isDeletingNode = false;
                    }else{
                        console.log("点击了节点", d.name);
                    }
                });
            node.exit().remove();

            // 添加标签
            const label = svg.selectAll(".label")
                .data(nodes, d => d.id);
            label.enter()
                .append("text")
                .attr("class", "label")
                .merge(label)
                .attr("x", d => d.x)
                .attr("y", d => d.y - iconSize/2 - 5)
                // .text(d => d.name);
                // .text(d => d.type);
            label.exit().remove();
        }

  

        // 添加新节点

        svg.on("click", function(event) {
            if (isAddingNode) {
                const coords = d3.pointer(event);
                const id = getUuid();
                const name = `Node${nodes.length + 1}`;
                // const type = nodes.length % 3 === 0 ? 'tower' : (nodes.length % 3 === 1 ? 'station' : 'gateway');
                const type = nodeType;
                nodes.push({ id, name, type, x: coords[0], y: coords[1] });
                update();
                // isAddingNode = false; // Reset the flag
            }
        });

        function dragstarted(event, d) {
            if (isAddingLink) {
                sourceNode = d;
                let id = getUuid();
                sourceNode.objId = id;
                dragLine = svg.append("line")
                    .attr("class", "dragLine")
                    .attr("stroke", "gray")
                    .attr("stroke-width", 2)
                    .attr("x1", d.x)
                    .attr("y1", d.y)
                    .attr("x2", d.x)
                    .attr("y2", d.y)
                    .attr("id", id);
            }
            if (!isAddingNode && !isAddingLink && !isDeletingNode && !isDeletingLink) {
                d3.select(this).raise().classed("active", true);
            }
        }

  

        function dragged(event, d) {
            if (dragLine) {
                dragLine.attr("x2", event.x)
                        .attr("y2", event.y);
            }
            if (!isAddingNode && !isAddingLink && !isDeletingNode && !isDeletingLink) {
                d.x = event.x;
                d.y = event.y;

                d3.select(this)
                    .attr("x", d.x - iconSize / 2)
                    .attr("y", d.y - iconSize / 2);

                // 更新连线的位置
                svg.selectAll(".line")
                    .attr("x1", l => nodes.find(n => n.id === l.source).x)
                    .attr("y1", l => nodes.find(n => n.id === l.source).y)
                    .attr("x2", l => nodes.find(n => n.id === l.target).x)
                    .attr("y2", l => nodes.find(n => n.id === l.target).y);
            }
            if (allDrag) {
                const dx = event.dx;
                const dy = event.dy;

                // 更新被拖动节点的位置
                d.x += dx;
                d.y += dy;

                d3.select(this)
                    .attr("x", d.x - iconSize / 2)
                    .attr("y", d.y - iconSize / 2);

                // 递归更新与当前节点相连的所有节点和连线的位置
                const visitedNodes = new Set();
                updateConnectedNodes(d, dx, dy, visitedNodes);

                // 更新所有连线的位置
                svg.selectAll(".line")
                    .attr("x1", l => nodes.find(n => n.id === l.source).x)
                    .attr("y1", l => nodes.find(n => n.id === l.source).y)
                    .attr("x2", l => nodes.find(n => n.id === l.target).x)
                    .attr("y2", l => nodes.find(n => n.id === l.target).y);

                // 更新所有节点的位置
                svg.selectAll(".node")
                    .attr("x", n => n.x - iconSize / 2)
                    .attr("y", n => n.y - iconSize / 2);
            }
        }
  
        function dragended(event, d) {
            if (dragLine) {
                dragLine.remove();
                dragLine = null;
                const targetNode = nodes.find(n => Math.hypot(n.x - event.x, n.y - event.y) < 10);
                if (targetNode && targetNode !== sourceNode) {
                    const color = determineLinkColor(sourceNode, targetNode);
                    links.push({objId:sourceNode.id, source: sourceNode.id, target: targetNode.id, color: color });
                    update();
                }
                sourceNode = null;
                // isAddingLink = false; // Reset the flag
            }
            if (!isAddingNode && !isAddingLink && !isDeletingNode && !isDeletingLink) {
                d3.select(this).classed("active", false);
            }
        }
  
        /**
         * 递归更新与当前节点相连的所有节点的位置
         * @param {object} node - 当前节点
         * @param {number} dx - x方向的移动距离
         * @param {number} dy - y方向的移动距离
         * @param {Set} visitedNodes - 已访问的节点集合
         */
        function updateConnectedNodes(node, dx, dy, visitedNodes) {
            visitedNodes.add(node.id);
  
            links.forEach(link => {
                if (link.source === node.id && !visitedNodes.has(link.target)) {
                    const targetNode = nodes.find(n => n.id === link.target);
                    targetNode.x += dx;
                    targetNode.y += dy;
                    updateConnectedNodes(targetNode, dx, dy, visitedNodes);
                } else if (link.target === node.id && !visitedNodes.has(link.source)) {
                    const sourceNode = nodes.find(n => n.id === link.source);
                    sourceNode.x += dx;
                    sourceNode.y += dy;
                    updateConnectedNodes(sourceNode, dx, dy, visitedNodes);
                }
            });
        }
  
        function determineLinkColor(sourceNode, targetNode) {
            if (sourceNode.type === 'pole' && targetNode.type === 'pole') {
                return '#1e90ff';
            } else if (sourceNode.type === 'station' && targetNode.type === 'station') {
                return 'green';
            } else if (sourceNode.type === 'gateway' && targetNode.type === 'gateway') {
                return 'blue';
            } else {
                return '#7f8c8d';
            }
        }
        // 按钮事件处理
        function addNodeButton(type) {
            nodeType = type;
            isAddingNode = true;
            isAddingLink = false;
            isDeletingNode = false;
            isDeletingLink = false;
            allDrag = false;
        };
  
        d3.select("#addLinkButton").on("click", function() {
            isAddingLink = true;
            isAddingNode = false;
            isDeletingNode = false;
            isDeletingLink = false;
            allDrag = false;
        });

        d3.select("#deleteNodeButton").on("click", function() {
            isDeletingNode = true;
            isAddingNode = false;
            isAddingLink = false;
            isDeletingLink = false;
            allDrag = false;
        });
  
        d3.select("#deleteLinkButton").on("click", function() {
            isDeletingLink = true;
            isAddingNode = false;
            isAddingLink = false;
            isDeletingNode = false;
            allDrag = false;
        });
        // 整体拖动
        function selectAllButton() {
            isDeletingLink = false;
            isAddingNode = false;
            isAddingLink = false;
            isDeletingNode = false;
            allDrag = true;
        }
  
        update();
        function logNodeButton() {
            console.log("Node type:", nodeType);
            console.log("Adding node:", isAddingNode);
            console.log("Adding link:", isAddingLink);
            console.log("Deleting node:", isDeletingNode);
            console.log("Deleting link:", isDeletingLink);
            console.log("Nodes:", nodes);
            console.log("Links:", links);
        }
        function getUuid () {
            if (typeof crypto === 'object') {
              if (typeof crypto.randomUUID === 'function') {
                return crypto.randomUUID();
              }
              if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
                const callback = (c) => {
                  const num = Number(c);
                  return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
                };
                return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, callback);
              }
            }
            let timestamp = new Date().getTime();
            let perforNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
              let random = Math.random() * 16;
              if (timestamp > 0) {
                random = (timestamp + random) % 16 | 0;
                timestamp = Math.floor(timestamp / 16);
              } else {
                random = (perforNow + random) % 16 | 0;
                perforNow = Math.floor(perforNow / 16);
              }
              return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
            });
        };
        function selectButton() {
            isDeletingLink = false;
            isAddingNode = false;
            isAddingLink = false;
            isDeletingNode = false;
            allDrag = false;
        }
        /**
         * 更新指定线段的stroke属性
         * @param {string} sourceId - 连线起点节点的ID
         * @param {string} targetId - 连线终点节点的ID
         * @param {string} color - 新的颜色值
         */
         function updateLinkStroke(sourceId, targetId, color) {
            // 查找指定起点和终点的连线
            const link = links.find(l => l.source === sourceId && l.target === targetId);
            if (link) {
                // 更新连线的颜色属性
                link.color = color;
                // 使用D3.js选择器选择指定的连线,并更新其stroke属性
                d3.selectAll(".line")
                    .filter(d => d.source === sourceId && d.target === targetId)
                    .attr("stroke", color);
            }
        }

         // 颜色选择器事件处理
        function updateColor() {
            const colorPicker = document.getElementById("colorPicker");
            selectedLink.color = colorPicker.value;
            updateLinkStroke(selectedLink.source, selectedLink.target, selectedLink.color);
            colorPicker.style.display = 'none';
        }
    </script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1717807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BUUCTF Crypto RSA详解《1~32》刷题记录

文章目录 一、Crypto1、 一眼就解密2、MD53、Url编码4、看我回旋踢5、摩丝6、password7、变异凯撒8、Quoted-printable9、篱笆墙的影子10、Rabbit11、RSA12、丢失的MD513、Alice与Bob14、大帝的密码武器15、rsarsa16、Windows系统密码17、信息化时代的步伐18、凯撒&#xff1f;…

加密金字塔的秘密:「高层」的回报你无法想象

原文标题&#xff1a;《The Secrets of the Crypto Pyramid!》 撰文&#xff1a;DUO NINE⚡YCC 编译&#xff1a;Chris&#xff0c;Techub News 本文来源香港Web3科技媒体&#xff1a;Techub News 意外成为一名 KOL 让我有机会深入了解这个领域的运作机制。在这个行业的幕后…

【xilinx】vivado中的xpm_cdc_gray.tcl的用途

背景 【Xilinx】vivado methodology检查中出现的critical Warning-CSDN博客 接上篇文章&#xff0c;在vivado进行 methodology检查时出现了严重警告&#xff0c;顺着指示查到如下一些问题 TIMING #1 Warning An asynchronous set_clock_groups or a set_false path (see con…

【Python】解决由于PYTHONPATH配置错误导致的ModuleNotFoundError: No module named ‘xxx‘错误

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

智慧楼宇:城市生活的新篇章

在城市的喧嚣与繁华中&#xff0c;楼宇不仅是我们工作与生活的场所&#xff0c;更是智慧科技发展的前沿阵地。当传统的建筑遇上智慧的火花&#xff0c;便诞生了令人瞩目的智慧楼宇。 山海鲸可视化搭建的智慧楼宇数字孪生系统 一、智慧楼宇&#xff0c;定义未来生活 智慧楼宇不…

利用WMI横向移动

一. WMI介绍和使用 1. WMI介绍 WMI是Windows在Powershell还未发布前&#xff0c;微软用来管理Windows系统的重要数据库工具&#xff0c;WMI本身的组织架构是一个数据库架构&#xff0c;WMI 服务使用 DCOM或 WinRM 协议, 在使用 wmiexec 进行横向移动时&#xff0c;windows 操…

生活杂货品牌首合作!The Green Party与数图共同探索品类空间管理全新策略!

数图服务业态再扩展&#xff0c;生活杂货品牌首合作&#xff0c;近日&#xff0c;数图与国内知名品牌The Green Party&#xff08;绿光派对&#xff09;展开合作&#xff0c;双方本着创新共赢的原则&#xff0c;携手共进&#xff0c;共同探索和实践品类空间管理领域的全新路线&…

Flink搭建

目录 一、standalone模式 二、Flink on Yarn模式 一、standalone模式 解压安装Flink [rootbigdata1 software]# tar -zxvf flink-1.14.0-bin-scala_2.12.tgz -C /opt/module/ [rootbigdata1 module]# mv flink-1.14.0/ flink-standalone 2.进入conf修改flink-conf.yaml job…

STM32_HAL_看门狗

介绍 STM32的"看门狗"&#xff08;Watchdog&#xff09;是一种硬件安全特性&#xff0c;用于监控STM32微控制器的正常操作。当系统出现故障或异常时&#xff0c;看门狗能够检测到这些情况&#xff0c;并采取相应的措施&#xff0c;通常是重置微控制器&#xff0c;以…

关系数据库:关系模式

文章目录 基本概述关系的相关名词术语笛卡儿积与关系关系的类型 关系模式总结 基本概述 关系的相关名词术语 关系&#xff1a;简单来说&#xff0c;就是一张二维表格。属性(Attribute)&#xff1a;也称字段或列&#xff0c;在现实世界中&#xff0c;要描述一个事务常常取若干…

私域加持业务 快消门店运营新玩法

两个月前&#xff0c;某快消品企业的李总急切地联系了纷享销客&#xff0c;希望能找到解决终端门店运营难题的有效方法。 Step1、连接终端门店&#xff0c;导入私域进行深度维系与运营 一、与终端门店建立联系 为了与众多门店老板建立紧密的联系&#xff0c;并将他们转化为企…

ERV-Net:一种用于脑肿瘤分割的高效3D残差神经网络| 文献速递-深度学习肿瘤自动分割

Title 题目 ERV-Net: An efficient 3D residual neural network for brain tumor segmentation ERV-Net&#xff1a;一种用于脑肿瘤分割的高效3D残差神经网络 01 文献速递介绍 脑肿瘤在全球范围内是致命的&#xff0c;与其他类型的肿瘤相比。胶质瘤是最具侵略性的脑肿瘤类…

pyinstaller将py文件打包成exe

pyinstaller将py文件打包成exe 一、为什么需要将python文件打包成exe文件?二、具体操作步骤一、为什么需要将python文件打包成exe文件? python文件需要在python环境中运行,也就是需要安装python解释器。有时我们自己写的python程序需要分享给自己的朋友、同事或者合作伙伴,…

两数之和 II - 输入有序数组,三数之和

题目一&#xff1a; 代码如下&#xff1a; vector<int> twoSum(vector<int>& numbers, int target) {int left 0;int right numbers.size() - 1;vector<int> ret;while (left < right){int tmp numbers[left] numbers[right];if (tmp target){r…

flink 操作mongodb的例子

简述 该例子为从一个Collection获取数据然后插入到另外一个Collection中。 Flink的基本处理过程可以清晰地分为以下几个阶段&#xff1a; 数据源&#xff08;Source&#xff09;&#xff1a; Flink可以从多种数据源中读取数据&#xff0c;如Kafka、RabbitMQ、HDFS等。Flink会…

SpringBoot案例,通关版

项目目录 此项目为了伙伴们可以快速入手SpringBoot项目,全网最详细的版本,每个伙伴都可以学会,这个项目每一步都会带大家做,学完后可以保证熟悉SpringBoot的开发流程项目介绍:项目使用springboot mybatis进行开发带你一起写小项目先把初始环境给你们第一步新建springboot项目返…

HTTP协议介绍与TCP协议的区别

1、HTTP介绍 HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 是基于TCP/IP 进行数据的通信&#xff0c;通常使用端口 80/8080。HTT…

项目纪实 | 版本升级操作get!GreatDB分布式升级过程详解

某客户项目现场&#xff0c;因其业务系统要用到数据库新版本中的功能特性&#xff0c;因此考虑升级现有数据库版本。在升级之前&#xff0c;万里数据库项目团队帮助客户在本地测试环境构造了相同的基础版本&#xff0c;导入部分生产数据&#xff0c;尽量复刻生产环境进行升级&a…

机器学习笔记(1):sklearn是个啥?

sklearn 简介 Sklearn是一个基于Python语言的开源机器学习库。全称Scikit-Learn&#xff0c;是建立在诸如NumPy、SciPy和matplotlib等其他Python库之上&#xff0c;为用户提供了一系列高质量的机器学习算法&#xff0c;其典型特点有&#xff1a; 简单有效的工具进行预测数据分…

GEYA格亚GRT8-S1S2间歇性双时间循环继电器时间可调交流220V 24v

品牌 GEYA 型号 GRT8-S2 AC/DC12-240V 产地 中国大陆 颜色分类 GRT8-S1 A220,GRT8-S1 AC/DC12-240V,GRT8-S2 A220,GRT8-S2 AC/DC12-240V GRT8-S&#xff0c;循环延时&#xff0c;时间继电器&#xff1a;LED指示灯&#xff0c;触头容量大&#xff0c;电压超宽&#xff0…