In Referencing Pivot Table Ranges in VBA I showed how to identify certain sections of a pivot table using VBA. In Dynamic Chart using Pivot Table and VBA I showed how to update a non-pivot chart when a pivot table updates.
How about some code that makes a chart based on only part of a pivot table, and updates it on demand?
The Pivot Table
The data we’re using shows causes of failure in a system, broken down by categories and types of failure. It’s the same data used in the dynamic chart article cited above.
Here’s the pivot table from the dynamic chart article. The duplicate columns Main Category and Category were needed in order to have the categories (electrical, mechanical, etc.) in both the row area and column area of the pivot table. Doing this plots each category’s defect data in a different series, so each category is shown in a different format.
If we just make a chart from the pivot table, here is the result. Not bad, but our objective is to show only one category, say, Environmental.
One of the limitations of a pivot chart is that we can’t plot a subset of the data. Well, we could, but we’d have to filter the pivot table so only the information we wanted to chart was visible.
Even if we selected only the data we wanted to plot, Excel would automatically expand the data range to the extent of the pivot table, and hand us a pivot chart.
We can still plot only part of the pivot table in a regular chart, but we need to take some special measures, as described in Making Regular Charts from Pivot Tables. For example, the selected range has to be nowhere near the pivot table when we insert the chart. Then we need to add the chart data one series at a time. Any attempt to enter a portion of the pivot table in the top portion of the Select Data Source dialog will convert the chart to a pivot chart and convert the source data range to the entire pivot table.
Chart Source Data in the Pivot Table
Basically, we need the subcategories of the environmental category for our X axis labels, we want the corresponding defect totals for our Y values, and we need the environmental category label for the series name.
I’ll use the approach I described in Referencing Pivot Table Ranges in VBA to identify these ranges.
The code to select the environmental category label (series name) is
Very good, the label is selected. One part done, the easy part.
If we use
we would select the entire column of environmental defect data, including the blank cells for the other categories.
Likewise, if we try
ActiveSheet.PivotTables(1).PivotFields("Main Category").PivotItems("Environmental") .DataRange.Select
we end up with the entire row of data, including those blank cells.
What we need is the intersection of those two ranges, or
Intersect(ActiveSheet.PivotTables(1).PivotFields("Category") .PivotItems("Environmental").DataRange, ActiveSheet.PivotTables(1).PivotFields("Main Category") .PivotItems("Environmental").DataRange).Select
Perfect. Now for the category labels. Trying
gives us all of the subcategories.
We only want some of those, so let’s poke around a little deeper.
ActiveSheet.PivotTables(1).PivotFields("Main Category").PivotItems("Environmental") .LabelRange.Select
gives us the four cells we want, but not in the right column.
So let’s use the interesection of the column of subcategory labels and the entire row of environmental data:
Intersect(ActiveSheet.PivotTables(1).PivotFields("Subcategory").DataRange, ActiveSheet.PivotTables(1).PivotFields("Main Category") .PivotItems("Environmental").DataRange.EntireRow).Select
Good, so we’ve figured out the ranges we’ll need.
Let’s Write Some Code
Now that we know what we’re starting with (a pivot table), how much of it we’ll need (the chart data ranges we divined above), and the output (a chart), it’s time to write some code.
I’ll do this in two parts. I’ll use a routine that updates a specified chart based on the ranges within the pivot table. I’ll write a routine that makes sure that the selected range doesn’t overlap with a pivot table and insert a new chart, then call the first routine to populate the chart. Finally I’ll write a short routine that calls the update chart routine, specifying the active chart.
The following routine accepts a chart from whatever code calls it, defines the various ranges it needs, uses the addresses of these ranges to construct the series formula, then clears the data out of the specified chart and replaces it with a series that uses this formula.
Sub UpdateChart(cht As Chart) Dim rXVals As Range Dim rYVals As Range Dim rName As Range Dim sSrsFmla As String Dim PT1 As PivotTable On Error GoTo LeaveThisMess ' define chart data ranges Set PT1 = ActiveSheet.PivotTables(1) Set rName = PT1.PivotFields("Category").PivotItems("Environmental").LabelRange Set rXVals = Intersect(PT1.PivotFields("Subcategory").DataRange, _ PT1.PivotFields("Main Category").PivotItems("Environmental") _ .DataRange.EntireRow) Set rYVals = Intersect(PT1.PivotFields("Category").PivotItems("Environmental") _ .DataRange, PT1.PivotFields("Main Category").PivotItems("Environmental") _ .DataRange) ' write series formula sSrsFmla = "=series(" & rName.Address(, , , True) & "," & _ rXVals.Address(, , , True) & "," & rYVals.Address(, , , True) & ",1)" With cht ' clear out chart data Do If .SeriesCollection.Count = 0 Then Exit Do .SeriesCollection(1).Delete Loop ' add brand new series with data we've defined With .SeriesCollection.NewSeries .Formula = sSrsFmla End With End With LeaveThisMess: End Sub
The following routine moves the active cell to the bottom right cell of the sheet, where hopefully there won’t be any pivot tables. I could do more checking, but I won’t. Then the sheet is scrolled up to the top left cell.
It disables screen updating while it does this, so the user doesn’t get a case of vertigo.
The routine inserts a chart in the default position (centered in the window), makes it a clustered column chart, then sends the chart to the routine above to be populated for the first time.
Sub CreateChartFromPartialPivotTable() Dim cht As Chart Application.ScreenUpdating = False On Error GoTo BailOut ' make sure active cell isn't in pivot table ' if it were, the chart would be a pivot chart With ActiveSheet .Cells(.Rows.Count, .Columns.Count).Select End With With ActiveWindow .ScrollColumn = 1 .ScrollRow = 1 End With ActiveSheet.Shapes.AddChart Set cht = ActiveSheet.ChartObjects(1).Chart cht.ChartType = xlColumnClustered UpdateChart cht ActiveSheet.Cells(1, 1).Select BailOut: Application.ScreenUpdating = True End Sub
This last routine makes sure a chart is selected, and if so, calls the first routine to repopulate it.
Sub UpdateActiveChart() If Not ActiveChart Is Nothing Then UpdateChart ActiveChart End If End Sub
Fairly elegant for something so quick and dirty.
Finally, The Chart
And oh yeah, here’s the chart. I’ve shown it next to the pivot table with the plotted series selected, so you can see the highlighted ranges used to plot the data. Environmental defect totals as Y values (blue highlights), environmental subcategories as X values (purple highlights), and Environmental label as series name (red highlight).