I'm using the Chart library in .Net 4.0 to create a stacked column chart with several series. My goal is a histogram that shows the cumulative number of actions (report completions) per day across several series (teachers). There is often missing data (no activity that day by a particular teacher).
I get gaps in the bars when there is missing data in a series:
My code:
public ActionResult CompletionHistogram(int sid, int width, int height)
{
Site site = SiteRepository.Get(sid);
if (site == null)
return new HttpNotFoundResult();
Chart chart = new Chart();
chart.Height = height;
chart.Width = width;
ChartArea area = chart.ChartAreas.Add("Default");
// Treat each teacher as a series
foreach (Teacher t in site.Teachers)
{
Series series = chart.Series.Add(t.FullName);
series.ChartType = SeriesChartType.StackedColumn;
series.Name = t.FullName;
// Group completions by day (filter out incomplete reports and null timestamps)
var groups = t.StudentReports
.Where<StudentReport>(rep => rep.IsComplete && rep.FirstSaveTimestamp.HasValue)
.GroupBy<StudentReport, DateTime>(rep => rep.FirstSaveTimestamp.Value.Date);
bool hasPoints = false;
foreach (var g in groups)
{
series.Points.AddXY(g.Key, g.Count());
hasPoints = true;
}
series.IsValueShownAsLabel = true;
series.ToolTip = "#VALX";
if (hasPoints)
chart.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, series);
}
area.AxisX.LabelStyle.Format = "ddd M/d";
return new ChartResult(chart);
}
How can I remove the
I have a way to make this work.
Sorry for the long answer, but I found the manner with which you attempt to implement this answer will affect if it works or not.
You need to set the point to zero manually as you are adding points. Note: I was not able to make this work by adding the zero points after the fact.
See the next to examples and resulting screenshots: chart1.Series.Clear(); chart1.Series.Add(new Series()); chart1.Series.Add(new Series()); chart1.Series.Add(new Series()); chart1.Series.Add(new Series());
foreach (Series s in chart1.Series)
{
s.ChartType = SeriesChartType.StackedColumn;
}
//chart1.Series[0].Points.Add(new DataPoint(0, 0));
chart1.Series[0].Points.Add(new DataPoint(1, 3));
chart1.Series[0].Points.Add(new DataPoint(2, 3));
chart1.Series[0].Points.Add(new DataPoint(3, 3));
chart1.Series[1].Points.Add(new DataPoint(0, 3));
//chart1.Series[1].Points.Add(new DataPoint(1, 0));
chart1.Series[1].Points.Add(new DataPoint(2, 3));
chart1.Series[1].Points.Add(new DataPoint(3, 3));
chart1.Series[2].Points.Add(new DataPoint(0, 3));
chart1.Series[2].Points.Add(new DataPoint(1, 3));
//chart1.Series[2].Points.Add(new DataPoint(2, 0));
chart1.Series[2].Points.Add(new DataPoint(3, 3));
chart1.Series[3].Points.Add(new DataPoint(0, 3));
chart1.Series[3].Points.Add(new DataPoint(1, 3));
chart1.Series[3].Points.Add(new DataPoint(2, 3));
//chart1.Series[3].Points.Add(new DataPoint(3, 0));
chart1.SaveImage("C:\\Before.png", ChartImageFormat.Png);
Image of "before.png":
Now remove the comments for the series with no data points at a given x value:
(Note! I found it does not work if you add the points at a given x-value for values where you make y = 0 at the end - aka right before I save the image. Order of the points in the series appears to matter for StackedColumn, I have never worked with this type except for investigating how to answer this question so that might be common knowledge for users of this type)
chart1.Series.Clear();
chart1.Series.Add(new Series());
chart1.Series.Add(new Series());
chart1.Series.Add(new Series());
chart1.Series.Add(new Series());
foreach (Series s in chart1.Series)
{
s.ChartType = SeriesChartType.StackedColumn;
}
chart1.Series[0].Points.Add(new DataPoint(0, 0));
chart1.Series[0].Points.Add(new DataPoint(1, 3));
chart1.Series[0].Points.Add(new DataPoint(2, 3));
chart1.Series[0].Points.Add(new DataPoint(3, 3));
chart1.Series[1].Points.Add(new DataPoint(0, 3));
chart1.Series[1].Points.Add(new DataPoint(1, 0));
chart1.Series[1].Points.Add(new DataPoint(2, 3));
chart1.Series[1].Points.Add(new DataPoint(3, 3));
chart1.Series[2].Points.Add(new DataPoint(0, 3));
chart1.Series[2].Points.Add(new DataPoint(1, 3));
chart1.Series[2].Points.Add(new DataPoint(2, 0));
chart1.Series[2].Points.Add(new DataPoint(3, 3));
chart1.Series[3].Points.Add(new DataPoint(0, 3));
chart1.Series[3].Points.Add(new DataPoint(1, 3));
chart1.Series[3].Points.Add(new DataPoint(2, 3));
chart1.Series[3].Points.Add(new DataPoint(3, 0));
// If you add the empty points here, it does not seem to work.
// Empty points are as follows, and are already added above in the 'after' example.
// chart1.Series[0].Points.Add(new DataPoint(0, 0));
// chart1.Series[1].Points.Add(new DataPoint(1, 0));
// chart1.Series[2].Points.Add(new DataPoint(2, 0));
// chart1.Series[3].Points.Add(new DataPoint(3, 0));
chart1.SaveImage("C:\\After.png", ChartImageFormat.Png);
Image of "after.png":
So, given that you can not add the zero points after the fact (although you may be able to insert them?) you will need to modify your code to something like this:
var allPossibleGroups = t.StudentReports;
var groups = t.StudentReports
.Where<StudentReport>(rep => rep.IsComplete && rep.FirstSaveTimestamp.HasValue)
.GroupBy<StudentReport, DateTime>(rep => rep.FirstSaveTimestamp.Value.Date);
bool hasPoints = false;
foreach (var g in allPossibleGroups)
{
if(groups.ContainsKey(g))
{
series.Points.AddXY(g.Key, g.Count());
hasPoints = true;
}
else
{
series.Points.AddXY(g.Key, 0);
}
}
Sorry for the long code blocks, but the example was necessary to demonstrate how to make it work, without falling into the trap of adding the empty points (where y = 0) at the end, as that will not work.
Let me know if you need more help.