最近需要用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();
}
}
}
效果图如下所示。
来源:oschina
链接:https://my.oschina.net/propagator/blog/4265317