Pregunta

Necesito ayuda para graficar programáticamente más puntos de los que caben en una sola serie de Excel.

De acuerdo con http://office.microsoft.com/en-us /excel/HP100738491033.aspx el número máximo de puntos que se pueden mostrar en un gráfico de Excel 2007 es 256000. Dado que cada serie tiene un límite de 32000 puntos, se requieren 8 series para trazar los 256000 puntos completos. Mi cliente requiere trazar la cantidad máxima de puntos por gráfico debido a los grandes conjuntos de datos con los que trabajamos.

Tengo una experiencia moderada con la interoperabilidad de C # / Excel, así que pensé que sería fácil crear programáticamente una hoja de trabajo y luego recorrer cada conjunto de 32000 puntos y agregarlos al gráfico como una serie, deteniéndome cuando los datos se trazaron completamente o se trazaron 8 series. Si se colorea correctamente, la serie 8 sería visualmente indistinguible de una sola serie.

Lamentablemente aquí estoy. El principal problema que encuentro es:

(tamaño completo) El número máximo de puntos de datos que puede usar en una serie de datos para un gráfico 2-D es 32,000 ... http://img14.imageshack.us/img14/9630/errormessagen.png

Esta ventana emergente, curiosamente, aparece cuando ejecuto la línea:

 chart.ChartType = chartType (donde chartType es xlXYScatterLines)

y va acompañado de:

Excepción de HRESULT: 0x800AC472 http://img21.imageshack.us/img21/ 5153 / exceptionb.png

No entiendo cómo podría generar una ventana emergente / advertencia / excepción antes de haber especificado los datos que se graficarán. ¿Excel intenta ser inteligente aquí?

Como solución temporal, puse la instrucción chart.ChartType = chartType en un bloque try-catch para poder continuar.

Como se muestra a continuación, mi " fragmentación " el código funciona según lo previsto, pero aún encuentro el mismo problema cuando intento agregar datos al gráfico. Excel dice que estoy tratando de graficar demasiados puntos cuando claramente no lo estoy.

( imagen a tamaño completo ) bloque de código con ventana de visualización http://img12.imageshack.us/img12/5360/snippet .png

Entiendo que puede que todavía no tenga los valores X correctamente asociados con cada serie, pero estoy tratando de hacer que esto funcione antes de continuar.

Cualquier ayuda sería muy apreciada.

Aquí está el código completo:

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");
    }
¿Fue útil?

Solución

Si la celda activa está en un bloque de datos, Excel puede suponer que desea trazar el rango.

Seleccione una celda en blanco que no esté al lado de los datos, luego inserte el gráfico. Estará en blanco, en lugar de prepoblada.

Otros consejos

¿Su gráfico realmente tiene que estar en Excel? Con tantos puntos de datos, el rendimiento sería horrible.

Una sugerencia podría ser utilizar un componente de terceros para generar el gráfico. La técnica específica de cómo lograr esto depende de si tiene que poder ver los datos en Excel o si el gráfico de salida simplemente necesita estar disponible en otro lugar.

Si el gráfico no necesita ser visible dentro de Excel, simplemente pase los puntos de datos y vea la imagen en la aplicación de gráficos o en un navegador web.

Si necesita ver el gráfico con Excel, puede hacer una llamada a la aplicación de gráficos externa y pasarle una colección de puntos de datos. Cuando devuelva la imagen, simplemente insértela en Excel con vba.

Puedo darle más información sobre ambos enfoques si lo necesita.

Además, otras consideraciones pueden incluir si necesita tener capacidad de desglose en el gráfico. Con tantos puntos de datos, no puedo imaginar que lo haría.


Si puede responder las siguientes preguntas, podría ayudar a la gente a formular mejores respuestas.

  1. ¿Qué tipo de interfaz de usuario presentará la salida de estos elementos? (por ejemplo, Excel, aplicación web ASP.NET, formularios Windows Forms, WPF, Silverlight, otros).

  2. ¿Se supone que estos gráficos se generan en tiempo real a pedido del usuario o se generan y almacenan? Si se generan bajo demanda, ¿cuál es el tiempo máximo que sus usuarios considerarían aceptable esperar?

  3. ¿Qué tan importante es que realmente uses Excel? ¿Lo está utilizando porque es un requisito para la visualización, o es eso lo que es útil?

  4. ¿Qué tan importante es el " Factor Wow " para la visualización de los gráficos? ¿Es simplemente tener los gráficos, o tienen que ser extremadamente hermosos?

  5. ¿Los usuarios requieren alguna habilidad para profundizar en el gráfico, o simplemente pueden ver la imagen lo suficiente?

Para ayudar a cualquiera que se encuentre con esto en el futuro, aquí está la función completa con la solución de 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");
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top