C # / Excel: работа вокруг максимального размера серии на графике - PullRequest
8 голосов
/ 14 сентября 2009

Мне нужна помощь для составления графиков большего количества точек, чем может поместиться в одной серии Excel.

Согласно http://office.microsoft.com/en-us/excel/HP100738491033.aspx максимальное количество точек, отображаемых на диаграмме Excel 2007, составляет 256000. Учитывая, что каждая серия ограничивается 32000 точками, для построения полных 256000 точек требуется 8 серий. Моему клиенту требуется график максимального количества точек на графике из-за больших наборов данных, с которыми мы работаем.

У меня умеренный опыт взаимодействия с C # / Excel, поэтому я подумал, что было бы легко программно создать рабочий лист, а затем выполнить цикл по каждому набору из 32000 точек и добавить их на график в виде серии, останавливаясь, когда данные были полностью построены или 8 серий были построены. При правильной окраске серия 8 будет визуально неотличима от одной серии.

К сожалению, вот и я. Основная проблема, с которой я сталкиваюсь:

(полный размер) Максимальное количество точек данных, которые вы можете использовать в серии данных для двумерной диаграммы, составляет 32 000 ... http://img14.imageshack.us/img14/9630/errormessagen.png

Это всплывающее окно, как ни странно, появляется, когда я выполняю строку:

chart.ChartType = chartType (where chartType is xlXYScatterLines)

и сопровождается:

Исключение из HRESULT: 0x800AC472 http://img21.imageshack.us/img21/5153/exceptionb.png

Я не понимаю, как я мог генерировать такое всплывающее окно / предупреждение / исключение, прежде чем я даже указал данные, которые будут отображаться. Excel пытается быть умным здесь?

В качестве временного решения я поместил оператор chart.ChartType = chartType в блок try-catch, чтобы я мог продолжать работу.

Как показывает следующее, мой «чанкинг» код работает как задумано, но я все еще сталкиваюсь с той же проблемой при попытке добавить данные в график. Excel говорит, что я пытаюсь наметить слишком много точек, когда явно нет.

( полноразмерное изображение ) кодовый блок с окном часов 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");
    }

Ответы [ 3 ]

2 голосов
/ 15 сентября 2009

Если активная ячейка находится в блоке данных, Excel может предположить, что вы хотите построить диапазон.

Выберите пустую ячейку, которая не находится рядом с данными, затем вставьте диаграмму. Он будет пустым, а не предварительно заполненным.

2 голосов
/ 15 сентября 2009

Ваш график действительно должен быть в Excel? С таким количеством точек данных производительность будет ужасной.

Одним из предложений может быть использование стороннего компонента для создания графика. Конкретный способ достижения этой цели зависит от того, нужно ли вам просматривать данные в формате Excel или просто нужно, чтобы выходной график был доступен в другом месте.

Если график не требуется отображать в Excel, просто передайте точки данных и просмотрите изображение в графическом приложении или веб-браузере.

Если вам нужно просмотреть график с помощью Excel, вы можете позвонить во внешнее графическое приложение и передать ему набор точек данных. Когда он вернет изображение, просто вставьте его в Excel с помощью VBA.

Я могу дать вам больше информации об обоих подходах, если вам нужно.

Кроме того, другие соображения могут включать вопрос о том, нужно ли вам иметь возможность детализации на графике. С таким количеством точек данных, я не могу себе представить, чтобы вы это сделали.


Если вы можете ответить на следующие вопросы, это может помочь людям сформулировать лучшие ответы.

  1. Какой пользовательский интерфейс будет отображать вывод этих элементов? (например, Excel, веб-приложение ASP.NET, Windows Forms, WPF, Silverlight и др.)

  2. Предполагается, что эти графики генерируются в режиме реального времени по запросу пользователя или они создаются и хранятся? Если они создаются по требованию, то сколько времени ваши пользователи сочтут приемлемым ждать?

  3. Насколько важно, что вы на самом деле используете Excel? Вы используете его, потому что это требование для отображения, или это то, что удобно?

  4. Насколько важен «вау-фактор» для отображения графиков? Просто есть графики, или они должны быть очень красивыми?

  5. Требуется ли пользователям какая-либо способность углубляться в график или достаточно просто просмотреть изображение?

1 голос
/ 15 сентября 2009

Чтобы помочь любому, кто столкнется с этим в будущем, вот полная функция с исправлением Джона:

    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");
    }
...