用c#处理数据:读excel,最小二乘法,wpf画函数图

亡梦爱人 提交于 2020-05-04 21:10:15

最近需要用C#做数据处理,核心是三点:从excel载入数据,处理,画图。折腾了一下午,总算搞得差不多了,记录一下。

 

首先是用NPOI从excel中载入数据,利用NPOI读取excel不需要安装office,仅依赖于ICSharpCode压缩库,且既能读取office2003格式,也可以读取office2007以上版本的格式,还算是比较好用。

using NPOI.XSSF.UserModel;
using NPOI.SS.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;


namespace FitAlgorithm
{
    class ExcelHelper
    {
        XSSFWorkbook xssfworkbook;
        DataSet dataSet1 = new DataSet();

        void InitializeWorkbook(string path)
        {
            //read the template via FileStream, it is suggested to use FileAccess.Read to prevent file lock.
            //book1.xls is an Excel-2007-generated file, so some new unknown BIFF records are added. 
            using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                xssfworkbook = new XSSFWorkbook(file);
            }

        }

        void ConvertToDataTable()
        {
            ISheet sheet = xssfworkbook.GetSheetAt(0);
            System.Collections.IEnumerator rows = sheet.GetRowEnumerator();

            DataTable dt = new DataTable();
            for (int j = 0; j < 5; j++)
            {
                dt.Columns.Add(Convert.ToChar(((int)'A') + j).ToString());
            }

            while (rows.MoveNext())
            {
                IRow row = (XSSFRow)rows.Current;
                DataRow dr = dt.NewRow();

                for (int i = 0; i < row.LastCellNum; i++)
                {
                    ICell cell = row.GetCell(i);


                    if (cell == null)
                    {
                        dr[i] = null;
                    }
                    else
                    {
                        //dr[i] = cell.ToString();
                        dr[i] = cell;
                    }
                }
                dt.Rows.Add(dr);
            }
            dataSet1.Tables.Add(dt);
        }

        public DataTable GetTable(string path)
        {
            InitializeWorkbook(path);
            ConvertToDataTable();
            return dataSet1.Tables[0];
        }
    }
}

上面的代码基本上沿用NPOI的样例代码,NPOI的源代码可从如下网址下载。

https://github.com/tonyqus/npoi

不过二进制dll不太容易下载,还依赖于ICSharpCode。我把所有用到的dll打了个包,可从下面的链接下载后直接引用。

https://download.csdn.net/download/u014559935/12388124

读取文件的代码如下

private void Button_Click(object sender, RoutedEventArgs e)
{
    DataTable dt = m_excel.GetTable(@"data\数据.xlsx");
}

 

接下来是数据处理,需要用到最小二乘法,下面给了一个简单的实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FitAlgorithm
{
    public class LeastSquare
    {
        /// <summary>
        /// 最小二乘法拟合直线
        /// </summary>
        /// <param name="x">自变量</param>
        /// <param name="y">因变量</param>
        /// <param name="k">斜率</param>
        /// <param name="b">截距</param>
        /// <param name="rel">相关系数</param>
        public static void LinearFit(List<double> x, List<double> y, out double k, out double b, out double rel)
        {
            int n = y.Count;
            double XX = 0.0;
            double YY = 0.0;
            double XY = 0.0;
            double X2 = 0.0;
            double Y2 = 0.0;
            for (int i = 0; i < n; i++)
            {
                XX += x[i];
                YY += y[i];
                XY += x[i] * y[i];
                X2 += x[i] * x[i];
                Y2 += y[i] * y[i];
            }
            double denorminator = n * X2 - XX * XX;
            b = (X2 * YY - XX * XY) / denorminator;
            k = (n * XY - XX * YY) / denorminator;

            double EXY = XY / n;
            double EYY = YY / n;
            double EXX = XX / n;
            double CovXY = EXY - EXX * EYY;   // 协方差
            double EX2 = X2 / n;
            double EY2 = Y2 / n;
            double VarX = EX2 - EXX * EXX;    // 方差
            double VarY = EY2 - EYY * EYY;    // 方差
            VarX = VarX * n / (n - 1.0);   // 校正
            VarY = VarY * n / (n - 1.0);   // 校正
            if (Math.Abs(VarX) < 1e-6 || Math.Abs(VarY) < 1e-6)
            {
                rel = 0.0;
            }
            rel = CovXY / Math.Sqrt(VarX * VarY);
        }
    }
}

因为数据量也不算太大,这样写基本上就可以了,速度也还可以接受。

 

最后是画图,用WPF做了一个window,看起来还凑合。

<Window x:Class="FitAlgorithm.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" Height="600" Width="800" Loaded="Window_Loaded_1">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ToolBar Grid.Row="0">
            <Label Name="mousePoint" Foreground="Red">11</Label>
        </ToolBar>
        <Canvas Grid.Row="1" Name="MainCanvas" Background="#FFFFFFE0"
                MouseMove="MainCanvas_MouseMove" SizeChanged="MainCanvas_SizeChanged" ClipToBounds="True">
            <Canvas.RenderTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="sfr" />
                    <TranslateTransform x:Name="tlt" />
                </TransformGroup>
            </Canvas.RenderTransform>
        </Canvas>
    </Grid>
</Window>

下面是代码

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace FitAlgorithm
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window2 : Window
    {
        /// <summary>
        /// 画板宽度
        /// </summary>
        double BoardWidth { get; set; }
        /// <summary>
        /// 画板高度
        /// </summary>
        double BoardHeight { get; set; }
        /// <summary>
        /// 垂直(纵向)边距(画图区域距离左右两边长度)
        /// </summary>
        double VerticalMargin { get; set; }
        /// <summary>
        /// 平行(横向)边距(画图区域距离左右两边长度)
        /// </summary>
        double HorizontalMargin { get; set; }
        /// <summary>
        /// 水平刻度间距像素
        /// </summary>
        double horizontalBetween { get; set; }
        /// <summary>
        /// 垂直刻度间距像素
        /// </summary>
        double verticalBetween { get; set; }

        /// <summary>
        /// x轴最大值
        /// </summary>
        public double MaxX { get; set; }

        /// <summary>
        /// y轴最大值
        /// </summary>
        public double MaxY { get; set; }

        /// <summary>
        /// x轴最小值
        /// </summary>
        public double MinX { get; set; }

        /// <summary>
        /// y轴最小值
        /// </summary>
        public double MinY { get; set; }

        /// <summary>
        /// 图表区域宽度
        /// </summary>
        double ChartWidth;
        /// <summary>
        /// 图表区域高度
        /// </summary>
        double ChartHeight;
        /// <summary>
        /// 画图区域起点
        /// </summary>
        Point StartPostion;
        /// <summary>
        /// 画图区域终点
        /// </summary>
        Point EndPostion;
        /// <summary>
        /// 数据源
        /// </summary>
        PointCollection DataSourse;

        Point startMovePosition;
        double MapLocationX = 0;
        double MapLocationY = 0;


        public Window2()
        {
            InitializeComponent();
            DataSourse = CollPoint;
        }

        private void MainCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Refresh();
        }

        private void MainCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point currentMousePosition = e.GetPosition((UIElement)sender);
            mousePoint.Content = currentMousePosition.X.ToString() + "," + currentMousePosition.Y.ToString();
        }

        private void Refresh()
        {
            InitCanvas();

            //获取y最大值
            if (MaxY < 0.0001)
            {
                MaxY = DataSourse.Max(m => m.Y);
            }
            //MinY = DataSourse.Min(m => m.Y);

            if (MaxX < 0.0001)
            {
                MaxX = DataSourse.Max(m => m.X);
            }
            //MinX = DataSourse.Min(m => m.X);
            if (Math.Abs(MaxX) < 0.000001 || Math.Abs(MaxY) < 0.000001)
            {
                return;
            }

            DrawAxis();
            DrawXAxisTicks();
            DrawYAxisTicks();
            DrawPolyline();
        }

        private void InitCanvas()
        {
            MainCanvas.Children.Clear();

            BoardWidth = MainCanvas.ActualWidth - SystemParameters.VerticalScrollBarWidth;
            BoardHeight = MainCanvas.ActualHeight - SystemParameters.HorizontalScrollBarHeight;
            HorizontalMargin = 40;
            VerticalMargin = 40;
            ChartWidth = BoardWidth - 2 * HorizontalMargin;//画图区域宽度
            ChartHeight = BoardHeight - 2 * VerticalMargin; //画图区域高度

            StartPostion = new Point(HorizontalMargin, VerticalMargin);
            EndPostion = new Point(BoardWidth - HorizontalMargin, BoardHeight - VerticalMargin);
        }

        private void DrawPolyline()
        {
            var polyline = new Polyline();
            foreach (var t in DataSourse)
            {
                Rectangle tmp = new Rectangle();
                tmp.Height = 10;
                tmp.Width = 10;
                tmp.Stroke = Brushes.Red;
                tmp.StrokeThickness = 5;
                Point point = GetRealPoint(t);
                tmp.Margin = new Thickness(point.X - tmp.Height / 2, point.Y - tmp.Width / 2, 0, 0);
                MainCanvas.Children.Add(tmp);
                polyline.Points.Add(GetRealPoint(t));
            }
            polyline.Stroke = Brushes.Blue;
            MainCanvas.Children.Add(polyline);
        }

        private Point GetRealPoint(Point point)
        {
            var realX = StartPostion.X + (point.X - MinX) * ChartWidth / (MaxX - MinX) + MapLocationX;
            var realY = StartPostion.Y + (MaxY - point.Y) * ChartHeight / (MaxY - MinY) + MapLocationY;
            return new Point(realX, realY);
        }

        /// <summary>
        ///  画y轴刻度
        /// </summary>
        private void DrawYAxisTicks()
        {
            if (MinY >= MaxY)
            {
                return;
            }
            if (verticalBetween < 0.0001)
            {
                verticalBetween = (MaxY - MinY) / 10;
            }
            for (var i = MinY; i <= MaxY + 0.01; i += verticalBetween)
            {
                var y = EndPostion.Y - i * ChartHeight / (MaxY - MinY) + MapLocationY;
                // y轴刻度
                var marker = new Line
                {
                    X1 = StartPostion.X - 5,
                    Y1 = y,
                    X2 = StartPostion.X,
                    Y2 = y,
                    Stroke = Brushes.Black
                };
                MainCanvas.Children.Add(marker);
                // y轴网格
                var gridLine = new Line
                {
                    X1 = StartPostion.X,
                    Y1 = y,
                    X2 = EndPostion.X,
                    Y2 = y,
                    StrokeThickness = 1,
                    Stroke = Brushes.PaleGreen
                };
                MainCanvas.Children.Add(gridLine);
                // y轴字符
                var markText = new TextBlock
                {
                    Text = i.ToString(),
                    Width = 30,
                    Foreground = Brushes.Black,
                    FontSize = 15,
                    HorizontalAlignment = HorizontalAlignment.Right,
                    TextAlignment = TextAlignment.Right
                };
                MainCanvas.Children.Add(markText);
                Canvas.SetTop(markText, y - 10);
                Canvas.SetLeft(markText, 0);
            }
        }

        /// <summary>
        /// 画x轴标签
        /// </summary>
        private void DrawXAxisTicks()
        {
            if (MinX >= MaxX)
            {
                return;
            }
            if (horizontalBetween < 0.0001)
            {
                horizontalBetween = (MaxX - MinX) / 10;
            }
            for (var i = MinX; i <= MaxX + 0.01; i += horizontalBetween)
            {
                var x = StartPostion.X + i * ChartWidth / (MaxX - MinX) + MapLocationX;
                // x轴刻度
                var marker = new Line
                {
                    X1 = x,
                    Y1 = EndPostion.Y,
                    X2 = x,
                    Y2 = EndPostion.Y + 5,
                    Stroke = Brushes.Black
                };
                MainCanvas.Children.Add(marker);
                // x轴网格
                var gridLine = new Line
                {
                    X1 = x,
                    Y1 = StartPostion.Y,
                    X2 = x,
                    Y2 = EndPostion.Y,
                    StrokeThickness = 1,
                    Stroke = Brushes.PaleGreen
                };
                MainCanvas.Children.Add(gridLine);
                // x轴字符
                var markText = new TextBlock
                {
                    Text = i.ToString(),
                    Width = 30,
                    Foreground = Brushes.Black,
                    VerticalAlignment = VerticalAlignment.Top,
                    TextAlignment = TextAlignment.Left,
                    FontSize = 15
                };
                MainCanvas.Children.Add(markText);
                Canvas.SetTop(markText, EndPostion.Y + 5);
                Canvas.SetLeft(markText, x - 10);
            }
        }

        /// <summary>
        /// X轴Y轴
        /// </summary>
        private void DrawAxis()
        {
            var xaxis = new Line
            {
                X1 = StartPostion.X,
                Y1 = EndPostion.Y,
                X2 = EndPostion.X,
                Y2 = EndPostion.Y,
                Stroke = Brushes.Black
            };
            MainCanvas.Children.Add(xaxis);

            var yaxis = new Line
            {
                X1 = StartPostion.X,
                Y1 = StartPostion.Y,
                X2 = StartPostion.X,
                Y2 = EndPostion.Y,
                Stroke = Brushes.Black
            };
            MainCanvas.Children.Add(yaxis);
        }

        /// <summary>
        /// 数据源
        /// </summary>
        /// <returns></returns>
        private PointCollection m_pointCollection = null;
        public PointCollection CollPoint
        {
            get
            {
                if (m_pointCollection == null)
                {
                    m_pointCollection = new PointCollection()
                    {
                        new Point(1,12),
                        new Point(2,20),
                        new Point(3,50),
                        new Point(4,21),
                        new Point(6,10),
                        new Point(21,90)
                    };
                }
                return m_pointCollection;
            }
            set
            {
                m_pointCollection = value;
            }
        }

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            //Refresh();
        }
    }
}

效果图如下所示。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!