Page 1 of 1

Problems stacking bar charts

Posted: Mon Feb 27, 2006 4:07 am
by 9637403
Im using the latest debug build 2.0.2242.29273

I have created a set of data with the following query. essentially 3 columns TYPE, VALUE and X. NOTE that for type 'A' I have no data for 24th January.


select 'A' as type,
10 as value,
cast('23JAN2006' as datetime)as x
union all
select 'A' as type,
10 as value,
cast('25JAN2006' as datetime)as x
union all
select 'A' as type,
10 as value,
cast('26JAN2006' as datetime)as x
union all
select 'B' as type,
15 as value,
cast('23JAN2006' as datetime)as x
union all
select 'B' as type,
15 as value,
cast('24JAN2006' as datetime)as x
union all
select 'B' as type,
15 as value,
cast('25JAN2006' as datetime) as x
union all
select 'B' as type,
15 as value,
cast('26JAN2006' as datetime) as x

If I create a bar graph where the first bar is for type A and the second bar is for type B, the Xaxis is the datetime value and the bar value is value. I get a graph that is correct, it shows the 4 days where the 23rd, 25th and 26th have two bars side-by-side. If however I stack the series the bar for B from the 24th is left hanging i.e. it looks like it's being stacked on something but there is nothing there. The A data for the 26 is not shown, I think it's all moved up a day.

I think what is happening is that TCHART is doing the stacking based on the simple order of the data not on the date/time groups. There is no A data from the 24th, TCHART tries to stack the B data with the A data from the 25th. If I reverse the order of the series ie plot B first and then A it stacks correctly.

Posted: Mon Feb 27, 2006 11:57 am
by narcis
Hi Adrian,

To stack series, all stacked series need to have the same XValues. If a value is missing then add a null point. The code below has the behaviour you request.

Code: Select all

		private void Form1_Load(object sender, System.EventArgs e)
		{
			bar1.Add(1,1);
			bar1.Add(2,2);
			bar1.Add(3,3);
			bar1.Add();
			bar1.Add(5,5);
			bar1.Add(6,6);
			bar1.Add(7,7);

			bar2.Add(1,1);
			bar2.Add(2,2);
			bar2.Add(3,3);
			bar2.Add(4,4);
			bar2.Add(5,5);
			bar2.Add(6,6);
			bar2.Add(7,7);
		}

Posted: Mon Feb 27, 2006 9:23 pm
by 9637403
this is fine when adding points individually however I'm using a datatable and the datasource property. I would have thought that the stack operation should compare XValues for each series and match them up. It's a little impracticle to have to work out if there are any gaps in any series and then adding the points individually don't you think?

Posted: Tue Feb 28, 2006 11:36 am
by narcis
Hi Adrian,

How do you think the series should be painted if they don't have the same X values. For example, if series A has X values for 1, 3 and 4 and series B has values for 1,2,3.9 and 5, the 3.9 and 4 values from series B and A respectively may overlap.

Posted: Tue Feb 28, 2006 8:39 pm
by 9637403
I understand that there are issues like the one you point out however, in my examples I have two series. Series one has XValues of 1,2,4 and 5 (no XValue 3). Series two has XValues of 1,2,3,4,5 (includes a XValue for 3). In this case there is no overlap. What we should see here after painting Series one is a bar at XValues 1,2,4 and 5 and a gap at XValue 3. After painting series two we should see additional bars for XValues 1,2,4 and 5 and a single bar at Xvalue 3. After stacking we should see stacked bars for XValues 1,2,4 and 5 and a single bar (the data from series 2) for XValue 3.

This doesn't work though, we end up with the series 2 bar for Xvalue 4 being painted against XValue 3 , sort of hanging above the XValue 3 point.

Posted: Wed Mar 01, 2006 11:25 am
by narcis
Hi Adrian,
In this case there is no overlap.
Yes, that's right, but you'll understand that implementing a generic solution for that is really complex.

A solution for that would be controlling how data is added to the database or to the series and add null values where necessary.

Another solution would be using:

Code: Select all

			bar1.MultiBar = Steema.TeeChart.Styles.MultiBars.Stacked;
			bar2.MultiBar = Steema.TeeChart.Styles.MultiBars.Stacked;

			double[] X1 = new double[] { 1, 2, 4, 5 };
			double[] X2 = new double[] { 1, 2, 3, 4, 5 };
			double[] Y1 = new double[] { 2, 2, 2, 2 };
			double[] Y2 = new double[] { 2, 2, 2, 2, 2 };

			bar1.Add(X1, Y1);
			bar2.Add(X2, Y2);

			if (bar1.Count < bar2.Count)
			{
				tChart1.Series.Exchange(0, 1);
			}
That is, the series with most x values should be painted first.

Posted: Wed Mar 01, 2006 12:25 pm
by narcis
Hi Adrian,

I noticed that 2nd solution I posted is not good enough. I tested yIT using random values instead of a fix Y value. What happened is that, after the series data gap the bars stacked at the top are still positioned as if they where stacked on the previous bar. So a more robust solution is to do post-modify the values:

Code: Select all

			bar1.MultiBar = Steema.TeeChart.Styles.MultiBars.Stacked;
			bar2.MultiBar = Steema.TeeChart.Styles.MultiBars.Stacked;
			Random rnd = new Random();

			double[] X1 = new double[] { 1, 2, 4, 5 };
			double[] X2 = new double[] { 1, 2, 3, 4, 5 };
			double[] Y1 = new double[] { rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble() };
			double[] Y2 = new double[] { rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble() };

			bar1.Marks.Visible = false;
			bar2.Marks.Visible = false;
			bar1.Add(X1, Y1);
			bar2.Add(X2, Y2);

			if (bar1.Count < bar2.Count)
			{
				for (int i = 0; i < bar2.Count; ++i)
				{
					double xvalue = bar2.XValues[i];
					if (bar1.XValues.IndexOf(xvalue) == -1)
					{
						bar1.Add(xvalue, 0, Color.Transparent);
					}
				}
			}

Posted: Sat Mar 04, 2006 5:08 am
by 9637403
I still think there is a bug here. I know I can fix it up with code but shouldn't TCHART stack the bars properly. If, when I add multiple series unstacked it can place the bars against the correct XVALUES why can't it stack them correctly. It seems that when we change the chart to be stacked TCHART stops matching the bars against the XVALUES. My application provides a generaic charting facility it would be extreamly difficult for me to cater for the stacking problem through coding, this is one of the reasons I chose TCHART for my charting engine. I'm pretty sure this issue was not around with the ACTIVEX version.

Can it be look at again please.

Posted: Mon Mar 06, 2006 11:22 am
by narcis
Hi Adrian,

Thanks for suggestion which is already on our wish-list to be considered for future releases.
I'm pretty sure this issue was not around with the ACTIVEX version.
I'm afraid this also happens with v6 and v7 ActiveX, try using the code below to check it. You need to uncomment the commented line to have it working.

Code: Select all

Private Sub Form_Load()
    TChart1.AddSeries scBar
    TChart1.AddSeries scBar
    
    TChart1.Series(0).asBar.MultiBar = mbStacked
    
    TChart1.Series(0).AddXY 1, 1, "", clTeeColor
    TChart1.Series(0).AddXY 2, 2, "", clTeeColor
    TChart1.Series(0).AddXY 3, 3, "", clTeeColor
'    TChart1.Series(0).AddNullXY 4, 4, ""
    TChart1.Series(0).AddXY 5, 5, "", clTeeColor
    TChart1.Series(0).AddXY 6, 6, "", clTeeColor
    TChart1.Series(0).AddXY 7, 7, "", clTeeColor
    
    TChart1.Series(1).AddXY 1, 1, "", clTeeColor
    TChart1.Series(1).AddXY 2, 2, "", clTeeColor
    TChart1.Series(1).AddXY 3, 3, "", clTeeColor
    TChart1.Series(1).AddXY 4, 4, "", clTeeColor
    TChart1.Series(1).AddXY 5, 5, "", clTeeColor
    TChart1.Series(1).AddXY 6, 6, "", clTeeColor
    TChart1.Series(1).AddXY 7, 7, "", clTeeColor
End Sub

Posted: Sat Nov 11, 2006 4:13 am
by 9637403
Hi Again Narcis,
I'm reviewing my charting code again and this issue is still a problem for me. I think this is the same issue but just to make sure if I have data like
  • X,Y1,Y2
    01/jan/2000,10,5
    02/jan/2000,null,5
    03/jan/2000,30,5
My application builds with the following code

Code: Select all

Dim row As DataRowView
For i As Integer = 0 To dv.Count - 1
     row = dv.Item(i)
     ox = ConvertAxisValue(row, xName)
     oy = ConvertAxisValue(row, yName)

     If Not TypeOf oy Is System.DBNull Then
        iPoint = serBar.Add(ox, oy)
        If LabelName <> "" Then
           serBar.Labels.Item(iPoint) = row(LabelName)
        End If
        If ColourName <> "" Then
           serBar(iPoint).Color = System.Drawing.Color.FromName(row(ColourName))
        End If
     End If
 Next
the convertaxisvalues function just changes date types to a type DOUBLE if possible so that the ADD method doesn't fail. Notice that if I get a Y value that is NULL I do not draw the bar.

Using the above code if I create a bar graph without stacking I get the following, this is exactly what I would expect.

Image

Now if I change the stacking from none to stacked (using the chart editor dialog) I end up with which is not what I expect.

Image

On further investigation, after I've changed the stack type and go back to the editor none of the stack radio buttons are set.

My application is a generic reporting tool, I cannot control what the users input to the charting so I need to have a solution that caters for this situation.

Any ideas would be great...

Posted: Sat Nov 11, 2006 4:14 am
by 9637403
also... If you want to keep the images for your forum could you copy them from my site and change the links in the post as they may be deleted from my site at some stage...

Cheers..

Posted: Sat Nov 11, 2006 4:38 am
by 9637403
If I try adding a null bar

Code: Select all

 
If Not TypeOf oy Is System.DBNull Then
   iPoint = serBar.Add(ox, oy)
   If LabelName <> "" Then
      serBar.Labels.Item(iPoint) = row(LabelName)
   End If
   If ColourName <> "" Then
      serBar(iPoint).Color = System.Drawing.Color.FromName(row(ColourName))
    End If
Else
    iPoint = serBar.Add()
End If
I end up with

Image

Posted: Sun Nov 12, 2006 4:03 am
by 9637403
Ok...

I've done a bit more work on this and have a workaround for the situation where we have X values and NULL Y values. The following code handles this correctly.

Code: Select all

 For i As Integer = 0 To dv.Count - 1
            row = dv.Item(i)

            ox = ConvertAxisValue(row, xName)
            oy = ConvertAxisValue(row, yName)

            If Not TypeOf ox Is System.DBNull Then
               If Not TypeOf oy Is System.DBNull Then
                  If TypeOf ox Is System.String Then
                     iPoint = serBar.Add(oy)
                     serBar(iPoint).Label = ox
                  Else
                     iPoint = serBar.Add(ox, oy)
                  End If
                  If LabelName <> "" Then
                     Try
                        serBar.Labels.Item(iPoint) = row(LabelName)
                     Catch ex As Exception
                        If ex.Message.IndexOf("is neither a DataColumn nor a DataRelation for table") <> -1 Then
                           m_Control.Log.LogMsg("ColourName " & LabelName & " not found in source data", False, Constants.LOG_MESSAGE_TYPES.Warning, -98, Me.m_iChartID)
                        Else
                           Throw ex
                        End If
                     End Try
                  End If
                  If HonourColourMapping Then
                     serBar.Color = m_Control.Read_Colour(yName, DefaultColour, serBar.Title)
                     serBar.ColorEach = False
                  Else
                     If ColourName <> "" Then
                        Try
                           serBar(iPoint).Color = System.Drawing.Color.FromName(row(ColourName))
                           serBar.ColorEach = True
                        Catch ex As Exception
                           If ex.Message.IndexOf("is neither a DataColumn nor a DataRelation for table") <> -1 Then
                              m_Control.Log.LogMsg("Column " & ColourName & " not found in source data", False, Constants.LOG_MESSAGE_TYPES.Warning, -98, Me.m_iChartID)
                           End If
                           serBar(iPoint).Color = DefaultColour
                           serBar.ColorEach = False
                        End Try
                     Else
                        serBar(iPoint).Color = DefaultColour
                        serBar.ColorEach = False
                     End If
                  End If
               Else
                  iPoint = serBar.Add(ox, 0, System.Drawing.Color.Transparent)
               End If
            End If
         Next
The following chart shows the side by side representation, note the missing blue value at 2nd Jan.
Image

after stacking we get the following which is correct....
Image


So I've worked around the situation where we have same number of data points in both series but some Y values are NULL. However there is still a problem where we have a different number of rows in two series. see below.

I have two series of data where one series has a different number of points to the other. I create a bar graph with stacking set to side by side.

Image

As you can see the green series is missing data for 6th, 7th and 8th but is represented correctly is this graph side by side.

When I change it to stacked however i get the following

Image

Now clearly there is an indexing problem here.... The stacking algorithm is aligning the bars correctly i.e. green series for the 9th stacked up on the 9th however it is using the value of the red series for the 6th to position the green bar starting point.

Without having to manipulate my users data and insert missing null rows for the green series I can't see how I can correct this. In any event while I could do it for this simple case it becomes very difficult when several series are stacked each with hundreds or thousands of points.

Your previous suggestion of drawing the series with the most points first would not work as I could have two series with the same number of points but each could be missing some i.e.

Series 1
X,Y
1,1
2,1
4,1

Series 2
X,Y
1,2
3,2
4,2

In any event forcing a particular series to be painted first is no solution as my users require the various series to be stacked in a particular order.

Can you PLEASE... PLEASE... PLEASE look into this as it is clearly a bug in the stacking routines...

ps. apologies to you for all the posts... it has taken me a while to work through this problem.

Ta.

Posted: Sun Nov 12, 2006 6:24 am
by 9637403
A final post on a way around this problem. The following function will "inject" null points into a series if it has less data points than any existing series and it will "inject" null points into any existing series if they do not have the same points as the new series.

Code: Select all

Private Sub Check_BAR_series_counts(ByVal tc As Steema.TeeChart.Chart, ByVal serbar As Steema.TeeChart.Styles.Bar)
      If tc.Series.Count = 0 Then Exit Sub
      ' check for points in serbar that are not in any other series.
      For i As Integer = 0 To serbar.XValues.Count - 1
         For Each s As Steema.TeeChart.Styles.Series In tc.Series
            If Not (s Is serbar) Then
               If s.XValues.IndexOf(serbar.XValues.Item(i)) = -1 Then
                  s.Add(serbar.XValues.Item(i), 0, System.Drawing.Color.Transparent)
               End If
            End If
         Next
      Next

      ' now check that there are no missing points in serbar
      For Each s As Steema.TeeChart.Styles.Series In tc.Series
         If Not (s Is serbar) Then
            For i As Integer = 0 To s.XValues.Count - 1
               If serbar.XValues.IndexOf(s.XValues.Item(i)) = -1 Then
                  serbar.Add(s.XValues.Item(i), 0, System.Drawing.Color.Transparent)
               End If
            Next
         End If
      Next
   End Sub
While this appears to solve the issue I would really like a fix for it in TCHART as you can imagine this additional code WILL significantly slow down the creation of charts with a large number of series and/or data points.

Cheers...