我需要帮助以编程方式绘制比单个Excel系列中更多的点。
根据http://office.microsoft.com/en-us/excel/HP100738491033.aspx,Excel 2007图表上可显示的最大点数为256000.鉴于每个系列的最大值为32000点,需要8个系列来绘制完整的256000个点。由于我们使用的大型数据集,我的客户需要绘制每个图表的最大点数。
我对C#/ Excel互操作有一定的经验,所以我认为以编程方式创建工作表然后循环遍历每组32000点并将它们作为一系列添加到图表中很容易,当数据完全绘制时停止或绘制了8个系列。如果颜色合适,8系列在视觉上与单个系列无法区分。
不幸的是我在这里。我遇到的主要问题是:
(full size) The maximum number of datapoints you can use in a data series for a 2-D chart is 32,000... http://img14.imageshack.us/img14/9630/errormessagen.png
当我执行该行时,会出现奇怪的弹出窗口:
并附有:
Exception from HRESULT: 0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png
在我甚至指定要绘制的数据之前,我不明白如何生成这样的弹出/警告/异常。 Excel在这里试图变得聪明吗?
作为临时解决方法,我将chart.ChartType = chartType语句放入try-catch块中,这样我就可以继续了。
如下所示,我的“分块”代码按预期工作,但在尝试向图表添加数据时仍遇到同样的问题。 Excel表示,当我显然不是时,我试图绘制太多的分数。
(full size image) code block with watch window http://img12.imageshack.us/img12/5360/snippet.png
我知道我可能没有正确地将X值与每个系列相关联,但是我想在我走得更远之前让它工作。
非常感谢任何帮助。
这是完整的代码:
public void DrawScatterGraph(string xColumnLetter, string yColumnLetterStart, string yColumnLetterStop, string xAxisLabel, string yAxisLabel, string chartTitle, Microsoft.Office.Interop.Excel.XlChartType chartType, bool includeTrendline, bool includeLegend)
{
int totalRows = dataSheet.UsedRange.Rows.Count; //dataSheet is a private class variable that
//is already properly set to the worksheet
//we want to graph from
if (totalRows < 2) throw new Exception("Not generating graph for " + chartTitle.Replace('\n', ' ')
+ " because not enough data was present");
ChartObjects charts = (ChartObjects)dataSheet.ChartObjects(Type.Missing);
ChartObject chartObj = charts.Add(100, 300, 500, 300);
Chart chart = chartObj.Chart;
try { chart.ChartType = chartType; }
catch { } //i don't know why this is throwing an exception, but i'm
//going to bulldoze through this problem temporarily
if (totalRows < SizeOfSeries) //we can graph the data in a single series - yay!
{
Range xValues = dataSheet.get_Range(xColumnLetter + "2", xColumnLetter + totalRows.ToString());
Range yValues = dataSheet.get_Range(yColumnLetterStart + "1", yColumnLetterStop + totalRows.ToString());
chart.SetSourceData(yValues, XlRowCol.xlColumns);
SeriesCollection seriesCollection = (SeriesCollection)chart.SeriesCollection(Type.Missing);
foreach (Series s in seriesCollection)
{
s.XValues = xValues;
}
}
else // we need to split the data across multiple series -- this doesn't work yet
{
int startRow = 1;
while (startRow < totalRows)
{
int stopRow = (startRow + SizeOfSeries)-1;
if (stopRow > totalRows) stopRow = totalRows;
Range curRange = dataSheet.get_Range(yColumnLetterStart + startRow.ToString(), yColumnLetterStop + stopRow.ToString());
try
{
((SeriesCollection)chart.SeriesCollection(Type.Missing)).Add(curRange, XlRowCol.xlColumns,
Type.Missing, Type.Missing, Type.Missing);
}
catch (Exception exc)
{
throw new Exception(yColumnLetterStart + startRow.ToString() + "!" + yColumnLetterStop + stopRow.ToString() + "!" + exc.Message);
}
startRow = stopRow+1;
}
}
chart.HasLegend = includeLegend;
chart.HasTitle = true;
chart.ChartTitle.Text = chartTitle;
Axis axis;
axis = (Axis)chart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
axis.HasTitle = true;
axis.AxisTitle.Text = xAxisLabel;
axis.HasMajorGridlines = false;
axis.HasMinorGridlines = false;
axis = (Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
axis.HasTitle = true;
axis.AxisTitle.Text = yAxisLabel;
axis.HasMajorGridlines = true;
axis.HasMinorGridlines = false;
if (includeTrendline)
{
Trendlines t = (Trendlines)((Series)chart.SeriesCollection(1)).Trendlines(Type.Missing);
t.Add(XlTrendlineType.xlLinear, Type.Missing, Type.Missing, 0, 0, Type.Missing, false, false, "AutoTrendlineByChameleon");
}
chart.Location(XlChartLocation.xlLocationAsNewSheet, "Graph");
}
答案 0 :(得分:2)
你的图表实际上是否必须在Excel中?有了这么多数据点,性能就会很糟糕。
一个建议可能是使用第三方组件生成图表。如何实现这一目标的具体技术取决于您是否必须能够在excel中查看数据,或者输出图是否只需要在其他地方可用。
如果图形不需要在Excel中可见,那么只需传递数据点并在图形应用程序或Web浏览器中查看图像。
如果确实需要使用excel查看图形,则可以调用外部图形应用程序并向其传递一组数据点。当它返回图像时,只需将其插入excel并使用vba。
如果需要,我可以为您提供有关这两种方法的更多信息。
此外,其他考虑因素可能包括您是否需要在图表上具有向下钻取功能。有了这么多数据点,我无法想象你会这么做。
如果您能回答以下问题,可能会帮助人们制定更好的答案。
哪种用户界面会显示这些项目的输出? (例如Excel,ASP.NET Web应用程序,Windows窗体,WPF,Silverlight,其他。)
这些图表是应该根据用户的请求实时生成还是生成并存储?如果它们是按需生成的,那么用户认为可以等待的最长时间是多少?
您实际使用Excel有多重要?你使用它是因为它是显示的要求,还是那个方便的东西?
图表显示的“惊人因素”有多重要?只是有图表,还是他们必须非常漂亮?
用户是否需要深入了解图表,或者只是能够充分查看图像?
答案 1 :(得分:2)
如果活动单元格位于数据块中,Excel可能会假设您要绘制范围。
选择一个不在数据旁边的空白单元格,然后插入图表。它将是空白的,而不是预先填充的。
答案 2 :(得分:1)
为了帮助将来遇到这个问题的人,这是Jon的修复工作的完整功能:
public void DrawScatterGraph(string xColumnLetter, string yColumnLetterStart, string yColumnLetterStop, string xAxisLabel, string yAxisLabel, string chartTitle, Microsoft.Office.Interop.Excel.XlChartType chartType, bool includeTrendline, bool includeLegend)
{
int totalRows = dataSheet.UsedRange.Rows.Count; //dataSheet is a private class variable that
//is already properly set to the worksheet
//we want to graph from
if (totalRows < 2) throw new Exception("Not generating graph for " + chartTitle.Replace('\n', ' ')
+ " because not enough data was present");
dataSheet.get_Range("Z1", "Z2").Select(); //we need to select some empty space
//so Excel doesn't try to jam the
//potentially large data set into the
//chart automatically
ChartObjects charts = (ChartObjects)dataSheet.ChartObjects(Type.Missing);
ChartObject chartObj = charts.Add(100, 300, 500, 300);
Chart chart = chartObj.Chart;
chart.ChartType = chartType;
SeriesCollection seriesCollection = (SeriesCollection)chart.SeriesCollection(Type.Missing);
if (totalRows < SizeOfSeries) //we can graph the data in a single series - yay!
{
Range xValues = dataSheet.get_Range(xColumnLetter + "2", xColumnLetter + totalRows.ToString());
Range yValues = dataSheet.get_Range(yColumnLetterStart + "1", yColumnLetterStop + totalRows.ToString());
chart.SetSourceData(yValues, XlRowCol.xlColumns);
foreach (Series s in seriesCollection)
{
s.XValues = xValues;
}
}
else // we need to split the data across multiple series
{
int startRow = 2;
while (startRow < totalRows)
{
int stopRow = (startRow + SizeOfSeries)-1;
if (stopRow > totalRows) stopRow = totalRows;
Series s = seriesCollection.NewSeries();
s.Name = "ChunkStartingAt" + startRow.ToString();
s.XValues = dataSheet.get_Range(xColumnLetter + startRow.ToString(), xColumnLetter + stopRow.ToString());
s.Values = dataSheet.get_Range(yColumnLetterStart + startRow.ToString(), yColumnLetterStop + stopRow.ToString());
startRow = stopRow+1;
}
}
chart.HasLegend = includeLegend;
chart.HasTitle = true;
chart.ChartTitle.Text = chartTitle;
Axis axis;
axis = (Axis)chart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
axis.HasTitle = true;
axis.AxisTitle.Text = xAxisLabel;
axis.HasMajorGridlines = false;
axis.HasMinorGridlines = false;
axis = (Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
axis.HasTitle = true;
axis.AxisTitle.Text = yAxisLabel;
axis.HasMajorGridlines = true;
axis.HasMinorGridlines = false;
if (includeTrendline)
{
Trendlines t = (Trendlines)((Series)chart.SeriesCollection(1)).Trendlines(Type.Missing);
t.Add(XlTrendlineType.xlLinear, Type.Missing, Type.Missing, 0, 0, Type.Missing, false, false, "AutoTrendlineByChameleon");
}
chart.Location(XlChartLocation.xlLocationAsNewSheet, "Graph");
}