domingo, 22 de abril de 2018

Gráfico con datos matriciales

Puede descargar el archivo manchasSolares.xlsm
Disponemos de dos casos, en ambos creamos un gráfico de dispersión XY con puntos constituidos por parejas de datos aleatorios. En el primer caso los datos se encuentran en la propia hoja de cálculo y en segundo caso los datos se generan de forma aleatoria en la propia macro, usando simplemente código VBA.

Hoja1

La capacidad de representar datos en un gráfico está limitada en Excel según la versión. Seguidamente se muestran las limitaciones de la versión 2016.


La información anterior está obtenida de la página de Microsoft. En la Hoja1 hemos creado 30.000 parejas de valores.


En las columnas A y B creamos aleatoriamente el radio y el ángulo que son las dos coordenadas polares que vamos a manejar. Luego en las columnas C y D convertimos las coordenadas polares en coordenadas cartesianas con la fórmula siguiente.


Si el radio se obtiene con la función aleatorio() esto nos permite crear un gráfico como el siguiente.


Observamos una mayor concentración de puntos en el centro. Si queremos que la distribución de puntos aleatorios sea uniforme en toda la circunferencia hemos de elegir el radio como la raiz cuadrada de una uniforme cero uno.

=raiz(aleatorio())

Con este cambio obtendremos el siguiente gráfico que podemos ver en forma de gif animado obtenido al recalcular los valores aleatorios pulsando la tecla de función F9 de forma reiterada.


Hoja2

Con el segundo caso veremos cómo generar los puntos aleatorios con una macro. Mediante código VBA crearemos una matriz para el eje X y otra matriz para el eje Y. Luego introduciremos ambas matrices como las series de valores para poder generar el gráfico de tipo dispersión XY.


Primera macro

 Sub generaChartConArray1()  
 Dim A(16000) As Variant  
 Dim B(16000) As Variant  
 Dim i As Long  
 Dim grafico As ChartObject  
 Dim c As Byte  
 Dim ChtObj As ChartObject  
 Worksheets("Hoja2").Activate  
 c = 0  
 Randomize  
 For i = 1 To 16000  
  A(i) = Rnd  
  B(i) = Rnd  
 Next i  
 For Each grafico In Worksheets("Hoja2").ChartObjects  
   If grafico.Name = "migas" Then  
     c = c + 1  
   End If  
 Next  
 If c = 0 Then  
  Set ChtObj = Worksheets("Hoja2").ChartObjects.Add(Left:=10, Top:=10, _  
       Width:=400, Height:=400)  
  With ChtObj  
   .Chart.ChartType = xlXYScatter  
   .Chart.SetSourceData Source:=Range("Hoja2!$A$1:$B$2")  
   .Name = "migas"  
 End With  
 End If  
 ActiveSheet.ChartObjects("migas").Activate  
 ActiveChart.SeriesCollection(1).XValues = A  
 ActiveChart.SeriesCollection(1).Values = B  
 ActiveChart.Axes(xlCategory).MaximumScale = 1  
 ActiveChart.Axes(xlCategory).MinimumScale = 0  
 ActiveChart.Axes(xlValue).MaximumScale = 1  
 ActiveChart.Axes(xlValue).MinimumScale = 0  
 ActiveChart.SetElement (msoElementLegendNone)  
 ActiveChart.SetElement (msoElementPrimaryValueGridLinesNone)  
 ActiveChart.SetElement (msoElementPrimaryCategoryAxisNone)  
 ActiveChart.SetElement (msoElementPrimaryValueAxisNone)  
 ActiveChart.SetElement (msoElementChartTitleNone)  
 ActiveChart.FullSeriesCollection(1).MarkerStyle = -4118  
 ActiveChart.FullSeriesCollection(1).MarkerSize = 2  
 Range("A1").Select  
 End Sub  

Segunda macro

 Sub generaChartConArray2()  
 Dim A() As Variant  
 Dim B() As Variant  
 Dim i As Long  
 Dim radio As Double  
 Dim angulo As Double  
 Dim grafico As ChartObject  
 Dim c As Byte  
 Dim n As Long  
 Dim ChtObj As ChartObject  
 Worksheets("Hoja2").Activate  
 c = 0  
 n = 16384  
 ReDim A(n)  
 ReDim B(n)  
 Randomize  
 For i = 1 To n  
  radio = Rnd  
  angulo = Rnd * 2 * (WorksheetFunction.Pi)  
  A(i) = radio * Cos(angulo)  
  B(i) = radio * Sin(angulo)  
 Next i  
 For Each grafico In Worksheets("Hoja2").ChartObjects  
   If grafico.Name = "migas" Then  
     c = c + 1  
   End If  
 Next  
 If c = 0 Then  
  Set ChtObj = Worksheets("Hoja2").ChartObjects.Add(Left:=10, Top:=10, _  
       Width:=400, Height:=400)  
  With ChtObj  
   .Chart.ChartType = xlXYScatter  
   .Chart.SetSourceData Source:=Range("Hoja2!$A$1:$B$2")  
   .Name = "migas"  
 End With  
 End If  
 ActiveSheet.ChartObjects("migas").Activate  
 ActiveChart.SeriesCollection(1).XValues = A  
 ActiveChart.SeriesCollection(1).Values = B  
 ActiveChart.Axes(xlCategory).MaximumScale = 1  
 ActiveChart.Axes(xlCategory).MinimumScale = -1  
 ActiveChart.Axes(xlValue).MaximumScale = 1  
 ActiveChart.Axes(xlValue).MinimumScale = -1  
 ActiveChart.SetElement (msoElementLegendNone)  
 ActiveChart.SetElement (msoElementPrimaryValueGridLinesNone)  
 ActiveChart.SetElement (msoElementPrimaryCategoryAxisNone)  
 ActiveChart.SetElement (msoElementPrimaryValueAxisNone)  
 ActiveChart.SetElement (msoElementChartTitleNone)  
 ActiveChart.FullSeriesCollection(1).MarkerStyle = -4118  
 ActiveChart.FullSeriesCollection(1).MarkerSize = 2  
 Range("A1").Select  
 End Sub  

Tercera macro

 Sub generaChartConArray3()  
 Dim A() As Variant  
 Dim B() As Variant  
 Dim i As Long  
 Dim radio As Double  
 Dim angulo As Double  
 Dim grafico As ChartObject  
 Dim c As Byte  
 Dim n As Long  
 Dim ChtObj As ChartObject  
 Worksheets("Hoja2").Activate  
 c = 0  
 n = 16384  
 ReDim A(n)  
 ReDim B(n)  
 Randomize  
 For i = 1 To n  
  radio = Sqr(Rnd)  
  angulo = Rnd * 2 * (WorksheetFunction.Pi)  
  A(i) = radio * Cos(angulo)  
  B(i) = radio * Sin(angulo)  
 Next i  
 For Each grafico In Worksheets("Hoja2").ChartObjects  
   If grafico.Name = "migas" Then  
     c = c + 1  
   End If  
 Next  
 If c = 0 Then  
  Set ChtObj = Worksheets("Hoja2").ChartObjects.Add(Left:=10, Top:=10, _  
       Width:=400, Height:=400)  
  With ChtObj  
   .Chart.ChartType = xlXYScatter  
   .Chart.SetSourceData Source:=Range("Hoja2!$A$1:$B$2")  
   .Name = "migas"  
 End With  
 End If  
 ActiveSheet.ChartObjects("migas").Activate  
 ActiveChart.SeriesCollection(1).XValues = A  
 ActiveChart.SeriesCollection(1).Values = B  
 ActiveChart.Axes(xlCategory).MaximumScale = 1  
 ActiveChart.Axes(xlCategory).MinimumScale = -1  
 ActiveChart.Axes(xlValue).MaximumScale = 1  
 ActiveChart.Axes(xlValue).MinimumScale = -1  
 ActiveChart.SetElement (msoElementLegendNone)  
 ActiveChart.SetElement (msoElementPrimaryValueGridLinesNone)  
 ActiveChart.SetElement (msoElementPrimaryCategoryAxisNone)  
 ActiveChart.SetElement (msoElementPrimaryValueAxisNone)  
 ActiveChart.SetElement (msoElementChartTitleNone)  
 ActiveChart.FullSeriesCollection(1).MarkerStyle = -4118  
 ActiveChart.FullSeriesCollection(1).MarkerSize = 2  
 Range("A1").Select  
 End Sub  

La segunda y tercera macro son prácticamente iguales, únicamente cambia la forma en la que elegimos el radio. En la segunda macro se hace según una distribución de probabilidad uniforme entre cero y uno. En la tercera macro lo que pretendemos es que los puntos del gráfico se distribuyan de forma uniforme por el área de la circunferencia por lo que hemos tenido que modificar la distribución de probabilidad introduciendo la raiz cuadrada de una uniforme entre cero y uno.

Puede ver un desarrollo parecido en otro lenguaje de programación en el siguiente enlace.

Canvas en HTML5+CSS+JS creando puntitos aleatorios

El él se emplea HTML5+CSS+JavaScript.