Unity中画2D图表(4)——给定一系列散点,拟合出一条直线

1、如何通过给定的散点拟合出一条直线
2、用XChart把散点和拟合直线画出来

一、要实现的效果

有六个散点,如下所示:
在这里插入图片描述

6个散点坐标值:

var X = new List<double> { 5, 10, 20, 32, 15, 50 };
var Y = new List<double> { -20, -8, 6, 48, 9, 7 };

如何把上面的散点拟合出一条直线方程:
在这里插入图片描述

二、直线拟合的实现

  • 1、用到的包
using MathNet.Numerics;

-2、用到的方法 Fit.Line(X,Y)

 var s = Fit.Line(X, Y); //XY为相同长度的double数组
 double b = s.Item1;     //截距
 double k = s.Item2;     //斜率

则直线方程为:y = kx + b

测试代码:

#if UNITY_EDITOR
    [ContextMenu("直线拟合操作")]
#endif
    void FitLineTest()
    {
        Tuple<double, double> s = new Tuple<double, double>(0, 0);

        double[] X = new double[] { 1, 2, 3, 4, 5 };
        double[] Y = new double[] { 3, 2, 5, 4, 6 };

        s = Fit.Line(X, Y);
        double b = s.Item1; //截距
        double k = s.Item2; //斜率

        Debug.Log($"拟合的直线方程为 : y = {k} * x + {b}");
    }

输出结果:

拟合的直线方程为 : y = 0.8 * x + 1.6

三、用XChart画散点和拟合直线的过程

请添加图片描述

四、代码清单

备注:右键菜单测试

using MathNet.Numerics;
using System;
using System.Collections.Generic;
using UnityEngine;
using XCharts.Runtime;
using System.Linq;
using Cysharp.Threading.Tasks;
using Unity.Mathematics;

/// <summary>
/// 散点拟合直线
/// </summary>
public class FitLine : MonoBehaviour
{
    public GameObject charRoot; 
    
#if UNITY_EDITOR
    [ContextMenu("直线拟合操作")]
#endif
    void FitLineTest()
    {
        Tuple<double, double> s = new Tuple<double, double>(0, 0);

        double[] X = new double[] { 1, 2, 3, 4, 5 };
        double[] Y = new double[] { 3, 2, 5, 4, 6 };

        s = Fit.Line(X, Y);
        double b = s.Item1; //截距
        double k = s.Item2; //斜率

        Debug.Log($"拟合的直线方程为 : y = {k} * x + {b}");
    }

#if UNITY_EDITOR
    [ContextMenu("画散点图并拟合一条直线")]
#endif
    void DrawLine()
    {
        Flow();
    }

    private async UniTask Flow()
    {
        //【1】=============画散点图=============
        var scatter = charRoot.AddComponent<ScatterChart>();
        scatter.Init();//初始化极为重要,不然在running状态会报空

        var title = scatter.GetOrAddChartComponent<Title>();
        title.text = "用给定的散点拟合出一条直线";         //主标题

        await UniTask.Delay(TimeSpan.FromSeconds(2.0f));
        scatter.SetSize(1024, 768);
        scatter.AddSerie<Serie>("数据分布图");
        Debug.Log($"数据长度为:{scatter.series[0].data.Count}"); //默认自带10个数据,需要删除
        scatter.series[0].data.Clear();

        await UniTask.Delay(TimeSpan.FromSeconds(2.0f));
        var X = new List<double> { 5, 10, 20, 32, 15, 50 };
        var Y = new List<double> { -20, -8, 6, 48, 9, 7 };

        var test = X.Zip(Y, (x, y) => new List<double> { x, y }).ToList();

        X.Zip(Y, (x, y) => new List<double> { x, y }).ToList().ForEach(t =>
        {
            //Debug.Log($"添加数据:{t[0]} => {t[1]}");
            scatter.AddData(0, t);
        });
        scatter.series[0].symbol.size = 9;//设置大小

        //【2】=============直线拟合计算=============
        await UniTask.Delay(TimeSpan.FromSeconds(2.0f));
        var s = Fit.Line(X.ToArray(), Y.ToArray());
        double b = s.Item1; //截距
        double k = s.Item2; //斜率

        //【3】=============画拟合出来的直线=============
        //【11】画拟合的直线
        var line1 = scatter.AddChartComponent<MarkLine>(); //添加一根直线
        line1.data.Clear();//清空默认值,添加组的时候,会默认包含一个item
        
        //增加一个端点数据
        await UniTask.Delay(TimeSpan.FromSeconds(2.0f));
        var p1 = new MarkLineData();
        p1.type = MarkLineType.None;
        p1.group = 1;
        p1.xPosition = 0;
        p1.yPosition = 0;
        p1.xValue = X.Min();
        p1.yValue = X.Min() * k + b;
        p1.zeroPosition = false;

        //标注【直线方程式】
        await UniTask.Delay(TimeSpan.FromSeconds(2.0f));
        var op = b > 0 ? "+" : "-";

        var kStr = Math.Round(k,2, MidpointRounding.AwayFromZero).ToString();
        var bStr = Math.Round(math.abs(b), 2, MidpointRounding.AwayFromZero).ToString();
        p1.name = $"y = {kStr} * x {op}{bStr}";
        p1.label.formatter = "{b}";

        //直线的形状设置
        p1.lineStyle.type = LineStyle.Type.Solid;
        p1.startSymbol.type = SymbolType.None;
        p1.endSymbol.type = SymbolType.None;

        //添加第二个端点数据
        var p2 = new MarkLineData();
        p2.type = MarkLineType.None;
        p2.group = 1;
        p2.xPosition = 0;
        p2.yPosition = 0;
        p2.xValue = X.Max();
        p2.yValue = X.Max() * k + b;
        p2.zeroPosition = false;
        p2.name = $"y = {kStr} * x {op}{bStr}"; ;//"y = 0 * x + 15";
        p2.label.formatter = "{b}";

        Debug.Log($"y = {kStr} * x {op}{bStr}");

        //直线的形状设置
        p2.lineStyle.type = LineStyle.Type.Solid;
        p2.startSymbol.type = SymbolType.None;
        p2.endSymbol.type = SymbolType.None;

        //端点数据加入直线中
        line1.data.Add(p1);
        line1.data.Add(p2);

        scatter.RefreshAllComponent();
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值