C # / Excel: работа с максимальным размером серии на диаграмме

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

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

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

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

(полный размер) http://img14.imageshack.us/img14/9630/errormessagen.png

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

chart.ChartType = chartType (где chartType - xlXYScatterLines)

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

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

person Vincent    schedule 14.09.2009    source источник


Ответы (3)


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

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

person Community    schedule 15.09.2009
comment
Спасибо, Джон! Я добавил следующее в верхней части моей функции: Range tempRange = dataSheet.get_Range (E1, E2); tempRange.Select (); Где столбец E пуст (мои данные только в столбцах A - C). С этим изменением все заработало. Еще раз спасибо! - person Vincent; 15.09.2009

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

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

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

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

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

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


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

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

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

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

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

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

person Anthony Gatlin    schedule 15.09.2009
comment
Спасибо за вклад, Энтони. Я был близок к тому, чтобы рассмотреть другие альтернативы, прежде чем Джон дал свой совет. К вашему сведению, я начал работать с Excel, потому что 1) он легко доступен для моей базы пользователей с огороженным садом и 2) Иногда графики нужно настраивать вручную после автоматического создания, и все пользователи обучены в Excel. - person Vincent; 15.09.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");
    }
person Vincent    schedule 15.09.2009