博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用c#类库绘制柱状图
阅读量:5277 次
发布时间:2019-06-14

本文共 8392 字,大约阅读时间需要 27 分钟。

代码有注释,在通用性方面进行了处理

可以指定极值,可以自定义分段,对样本数据分段比较灵活,

不填临界值时平均分段,不指定极值时样本数据中的最大值为极大值。极小值默认为0,但可以设置。

///     /// 提供获取统计图的相关方法    /// 适合有极值和无极值,无极值时采用样本内的极值    /// 除极小值包含在近右片外,其他临界值包含在近左片    ///     public class StatisticsGraph    {        ///         /// 样本数据        ///         private List
Samples = null; private int NumberOfSegments = 10;//段数 即得到的图线有10个柱 private double MaxSample = 0;//最大样本数据 private double MinSample = 0;//最小样本数据 private int MaxExtremum = 100;//样本极值 ,如统计学生成绩时 极值一般为100,即卷满分 private float[] Demarcation = new float[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 };//限有极值时设置 ///
/// 根据样本数据初始化 /// ///
public StatisticsGraph(List
samples) { this.Init(samples); } ///
/// 通过指定分片段数和样本数据进行初始化 /// ///
///
public StatisticsGraph(List
samples, int numberOfSegments) { this.HasSpecifiedExtremum = false; this.Init(samples); this.NumberOfSegments = numberOfSegments; this.Demarcation = GetDemarcation(); } ///
/// 通过指定分片段数、极大值和样本数据进行初始化 /// ///
///
///
public StatisticsGraph(List
samples, int numberOfSegments, int maxExtremum) { this.HasSpecifiedExtremum = true; this.MaxExtremum = maxExtremum; this.Init(samples); this.NumberOfSegments = numberOfSegments; this.Demarcation = GetDemarcation(); } ///
/// 通过指定极大值、样本数据和自定义分段临界值进行初始化 /// ///
///
///
public StatisticsGraph(List
samples, float[] demarcation, int maxExtremum) { this.HasSpecifiedExtremum = true; this.Init(samples); this.MaxExtremum = maxExtremum; this.Demarcation = demarcation; this.NumberOfSegments = demarcation.Count(); } public int MinExtremum = 0;//最小极值默认为0 private void Init(List
samples) { this.Samples = samples; this.MaxSample = Samples.Max(); this.MinSample = Samples.Min(); } ///
/// 是否指定了极大值 /// private bool _hasSpecifiedExtremum = true; ///
/// 是否已经指定了极大值 /// public bool HasSpecifiedExtremum { get { return _hasSpecifiedExtremum; } private set { _hasSpecifiedExtremum = value; } } ///
/// 获取每个分片的临界点 /// ///
private float[] GetDemarcation() { if (NumberOfSegments <= 1) { return null; } float[] result = new float[NumberOfSegments - 1]; if (HasSpecifiedExtremum == false)//未指定极大值时 样本中最大值为统计范围内极大值 { MaxExtremum = Convert.ToInt32(Math.Floor(MaxSample)) + 1; MinExtremum = Convert.ToInt32(Math.Floor(MinSample)) - 1; } int span = MaxExtremum - MinExtremum;//极值跨度 float segSpan = span * 1f / NumberOfSegments;//每个片段的跨度 for (int i = 0; i < NumberOfSegments - 1; i++) { result[i] = MinExtremum + segSpan * (i + 1); } return result; } ///
/// 获取各个分片的样本数量 /// 结果《分片的极右值,该片的样本数》 /// 分片总数为临界点数+1个 /// ///
private Dictionary
GetSampleNumbersOfPerSegment() { Dictionary
result = new Dictionary
(); List
segRightValue = new List
();//分片极右值 foreach (var item in Demarcation) { segRightValue.Add(item); } segRightValue.Add(MaxExtremum); for (int i = 0; i < segRightValue.Count; i++) { int value = 0; foreach (float m in Samples)//计算每个片段的样本数 { if (i == 0) { if (m <= segRightValue[i]) { value += 1; } } else { if (m <= segRightValue[i] && m > segRightValue[i - 1]) { value += 1; } } } result.Add(segRightValue[i], value); } return result; } ///
/// 获取每个分片的左上顶点坐标 /// ///
最大利用宽度 ///
最小利用宽度 ///
private List
GetTopLeftPointFOfSegment(PointF bottomLeft, float UsableWidth, float UsableHeight, out float widthPerSeg, out float unitHeight, out Dictionary
SampleNumbersOfPerSegment) { List
result = new List
(); SampleNumbersOfPerSegment = GetSampleNumbersOfPerSegment();//获取每个片段占有的样本数 int maxSampleNumbersOfSegment = SampleNumbersOfPerSegment.Max(x => x.Value);//所有分片中 最大分片样本数 widthPerSeg = UsableWidth * 1f / (SampleNumbersOfPerSegment.Count * 2 + 1);//每个分片的宽度 分片之间还有空白分片 unitHeight = UsableHeight * 1f / maxSampleNumbersOfSegment;//充分利用高度的情况下 单位样本数所占高度 for (int i = 0; i < SampleNumbersOfPerSegment.Count; i++) { PointF pf = new PointF(); pf.X = bottomLeft.X + (i * 2 + 1) * widthPerSeg;//每个片段的左边X坐标 pf.Y = bottomLeft.Y - SampleNumbersOfPerSegment.ElementAt(i).Value * unitHeight;//每个片段上边Y坐标 result.Add(pf); } return result; } ///
/// 获取每个分片的 条形数据(包括:左上角坐标,高度和宽度) /// ///
///
///
private RectangleF[] GetRectangleFs(PointF bottomLeft, float UsableWidth, float UsableHeight, out Dictionary
SampleNumbersOfPerSegment) { float widthPerSeg = 0;//每个片段的宽度 float unitHeight = 0;//单位样本数据在Y轴上表示需要的高度 //每个片段的左上角坐标 List
pfs = GetTopLeftPointFOfSegment(bottomLeft, UsableWidth, UsableHeight, out widthPerSeg, out unitHeight, out SampleNumbersOfPerSegment); RectangleF[] RFs = new RectangleF[pfs.Count]; for (int i = 0; i < pfs.Count; i++) { //通过计算宽度和高度 结合左上角坐标 以准确描述每个矩形的大小和位置 RFs[i] = new RectangleF(pfs[i], new SizeF(widthPerSeg, SampleNumbersOfPerSegment.ElementAt(i).Value * unitHeight)); } return RFs; } ///
/// 获得10段柱状图 /// 横轴 分数段;纵轴 该分数段的 人数 /// ///
///
///
///
///
public Bitmap GetBargraph(int width, int heigh, string XunitName, string YunitName, string familyName = "宋体") { Font font = new Font(familyName, 10); Bitmap bitmap = new Bitmap(width, heigh); Graphics gdi = Graphics.FromImage(bitmap); //用白色填充整个图片,因为默认是黑色 gdi.Clear(Color.White); //抗锯齿 gdi.SmoothingMode = SmoothingMode.HighQuality; //高质量的文字 gdi.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; //像素均偏移0.5个单位,以消除锯齿 gdi.PixelOffsetMode = PixelOffsetMode.Half; int margin = 30;//坐标轴与边框的距离 int padding = 20;//实际表示内容区域 与坐标轴右和上边的距离 PointF bottomLeft = new PointF(margin, heigh - margin);//坐标原点 PointF topLeft = new PointF(bottomLeft.X, margin);//Y轴最上顶端坐标 PointF topLeft_bl = new PointF(topLeft.X - 6, topLeft.Y + 10);//Y轴箭头左下端坐标 PointF topLeft_br = new PointF(topLeft.X + 6, topLeft_bl.Y);//Y轴箭头右下端坐标 gdi.DrawLines(Pens.Black, new PointF[] { topLeft_bl, topLeft, topLeft_br });//画坐标轴Y轴箭头 gdi.DrawString(string.Format("({0})", YunitName), font, Brushes.Black, topLeft_bl.X - 20, topLeft.Y - 14);//在Y轴箭头左下角写上 Y轴表示的单位 PointF bottomRight = new PointF(width - margin, bottomLeft.Y);//X轴最右端坐标 PointF bottomRight_lt = new PointF(bottomRight.X - 10, bottomRight.Y - 6);//X轴箭头左上端坐标 PointF bottomRight_lb = new PointF(bottomRight_lt.X, bottomRight.Y + 6);//X轴箭头右下端坐标 gdi.DrawLines(Pens.Black, new PointF[] { bottomRight_lt, bottomRight, bottomRight_lb });//画坐标轴X轴箭头 gdi.DrawString(string.Format("({0})", XunitName), font, Brushes.Black, bottomRight_lt.X - 3, bottomRight_lt.Y + 13);//在X轴箭头的下方 写上X轴表示的单位 gdi.DrawLines(Pens.Black, new PointF[] { topLeft, bottomLeft, bottomRight });//画坐标轴 float usableHeight = bottomLeft.Y - margin - padding;//内容区高度 float usableWidth = width - margin * 2 - padding;//内容区宽度 Dictionary
SampleNumbersOfPerSegment = null;//各个片段的描述数据 RectangleF[] RFs = GetRectangleFs(bottomLeft, usableWidth, usableHeight, out SampleNumbersOfPerSegment);//获取条形图位置数据 gdi.FillRectangles(new SolidBrush(Color.FromArgb(70, 161, 185)), RFs);//填充柱形 //标上坐标轴上的数据 //X轴上写的内容 string Xcontent = string.Empty; //条形顶上方写的内容 string Ycontent = string.Empty; for (int i = 0; i < SampleNumbersOfPerSegment.Count; i++) { if (i == 0) Xcontent = "X<=" + SampleNumbersOfPerSegment.ElementAt(i).Key.ToString("F0"); else { Xcontent = string.Format("{0}
<={1}", SampleNumbersOfPerSegment.ElementAt(i - 1).Key.ToString("F0"), SampleNumbersOfPerSegment.ElementAt(i).Key.ToString("F0")); } gdi.DrawString(Xcontent, font, Brushes.Black, RFs[i].X - 8, bottomLeft.Y + 5); //写上X轴上的数据 Ycontent = SampleNumbersOfPerSegment.ElementAt(i).Value.ToString(); gdi.DrawString(Ycontent, font, Brushes.Black, RFs[i].X + 3, RFs[i].Y - 13); //写上Y轴上的数据 } return bitmap; } }

调用:

StatisticsGraph sg = new StatisticsGraph(scores.ConvertAll(x => (float)x),10,100);            System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();            w.Start();            Bitmap bitmap = sg.GetBargraph(800, 480,"分","人数");            bitmap.Save("tt.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);            Debug.WriteLine(w.Elapsed);

一张图片大约耗时20毫秒。

测试数据得到的柱形图:

 

 

转载于:https://www.cnblogs.com/langu/archive/2013/05/13/3075658.html

你可能感兴趣的文章
[每天一道A+B]签到检测程序
查看>>
[HDOJ4588]Count The Carries(数学,规律)
查看>>
Python学习笔记——杂项
查看>>
ubuntu mysql汉字写入只写入了一个字符
查看>>
css基础
查看>>
读取一个文件中的字符,统计每个字符出现的次数
查看>>
获取本地的json并展示
查看>>
利用CSS的translate属性或利用CSS实现图片居中的效果
查看>>
git步骤
查看>>
A+B (带有,的数字)
查看>>
[ActionScript 3.0] 对数组中的元素进行排序Array.sort()的方法
查看>>
[工作积累] shadow map问题汇总
查看>>
H3C交换机和路由器的命名规范
查看>>
jCryptoJS 、C#互通加密(MD5版)
查看>>
【Asp.Net】:如何处理大量页面的身份验证跳转
查看>>
管道拥塞
查看>>
问题集合与纠正
查看>>
[C] 跨平台使用Intrinsic函数范例3——使用MMX、SSE2指令集 处理 32位整数数组求和...
查看>>
[iOS] UITextField隐藏软键盘心得(隐藏自身软键盘、点击Return自动转到下个文本框、轻触背景隐藏软键盘)...
查看>>
The method setOnClickListener(View.OnClickListener) in the type View is not applicable
查看>>