Excel VBA: Conditional Format of Pivot Table based on Column Label - excel

(Note: This is my first question. Please let me know if I can improve how I ask or explain)
I have created a module that can look through a pivot table on any excel worksheet and apply conditional formatting to each column to create quartile performances.
The pivot table can be any size and have different columns at any point - so I can't explicitly reference a particular column name or caption etc
Additionally, the quartile reporting identifies the top 25% for a particular figure, the next 25, next 25 and bottom 25.
For some figures high is better; for others, low is better.
So I list all the values that are high-to-low in an array, then run a quick "if name in array, rank this way; otherwise, rank this way" function.
All of this works like a dream - until we come to certain fields that result in an ambiguous name issue (and error). It seems that some caption names are similar to the source or database names.
The code reads like this (below).
Any ideas how I can refer dynamically to the column name and identify the column beneath it, please?
Dim myColumnNames As Variant
myColumnNames = VBA.Array("CONTRACTGROSSVOLUME", _
"MigrationVolume") 'etc
' Set colour choices for quartiles
Dim myQuartile1 As Long
Dim myQuartile2 As Long
Dim myQuartile3 As Long
Dim myQuartile4 As Long
myQuartile1 = RGB(146, 208, 80) 'Top Quartile - Green
myQuartile2 = RGB(255, 255, 0) '2nd Quartile - yellow
myQuartile3 = RGB(255, 192, 0) '3rd Quartile - orange
myQuartile4 = RGB(255, 0, 0) 'Bottom Quartile - red
Dim myRankingFactor As Boolean
myRankingFactor = True 'true is low to high / false is high to low ranking
Dim myPivotTable As PivotTable
Dim myPivotTableName As String
For Each myPivotTable In ActiveSheet.PivotTables
myPivotTableName = myPivotTable.Name
Next
Dim myPivotField As PivotField
Dim myPivotSourceName As String
Set myPivotTable = ActiveSheet.PivotTables(myPivotTableName)
For Each myPivotField In myPivotTable.DataFields
'get the source name for the pivot field
myPivotSourceName = myPivotField.Name
'Check if column name is in our list to rank high-to-low
If Not IsError(Application.Match(myPivotSourceName, myColumnNames, False)) Then
'This column name is in our list of names that should be ranked high-to-low (ie. Higher is better)
myRankingFactor = True
Else
'This column name is not in our list and should be ranked low-to-high (ie. lower is better)
myRankingFactor = False
End If
The error then comes on the following line:
myPivotTable.PivotSelect (myPivotSourceName), xlDataOnly, True
I've tried refering to the column with .caption, .name etc - no avail.
Any dieas on what I need to do to dynamically get the column name, check if it's in the array of names, then refer to that entire column to apply my formatting, please?
Thanks
Additional info:
The value is passing (apparently) correctly.
The column name displayed is "My Volume" and the variable is displaying "My Volume" as the value.
It's source name is "MYVOLUME" (one word), which I've also tried referencing without success.
The error generated is:
Run-time error '1004': An item name is ambiguous. Another field in the
pivottable report may have an item with the same name. Use the syntax
field [item]. For example, if the item is Oranges and the field is
Product, you would use Product[Oranges].
As an addition, I just manually changed one of the column names to a unique, single word ("ABCDEFG") that is not present in the database, object or anywhere in the data output to see if it would be picked up.
Changing that alias/caption value worked fine and didn't error.
Summary:
It's behaving as if the column name is already used elsewhere in the pivot - but it is not.
How do I explicitly refer to the column name/caption/label, but on-the-fly? :)
FIXED!
I ensured that the pivot table column name was passed as a string:
myPivotSourceName = myPivotField.Name
Then rather than referencing the data field with the pivot field object, I referenced the DataRange with the string:
myPivotTable.PivotFields(myPivotSourceName).DataRange.Select
Works perfectly and is completely portable for any pivottable on any sheet with any fields

I could reproduce the error by having a data item with the same name as the one of the data fields. For example, if you have the following table from which you create a pivot:
Product Price
Cola 123
Fanta 456
Sum of Price 789
then by creating a pivot table, you will have these items: Cola, Fanta, 'Sum of Price', and the following field labels: 'Row labels', 'Sum of Price'.
If you try to use 'Sum of Price' in the PivotTable.PivotSelect function, then the error message in the question will appear.
I think the Name parameter in PivotSelect is not clear enough, I did not find the documentation of the naming convention used in it, so I recommend referring to the datafield explicitly:
myPivotTable.PivotFields("Sum of Price").DataRange.Select
Note: There are many stylistic errors a superfluous parts in your code, e.g. the parentheses in the line that causes the error are not required, the loops just select the last item.

myPivotTable.PivotFields(myPivotSourceName).DataRange.Select
This references the name of the column as a string. Works on all pivot tables I've tested it on.

Related

Repopulate Excel data to report specific information whenever a validation list option is switched

I'm working on an Excel financial template that calculates Standard Costs of Manufacturing, and on one tab I need to select from a list (Current, Increase/Decrease, NPI) to tell the calculator to use either a "standard value" or "manual entry", but coming from the same cell.
The Docs saved so that a new WB its set to 'Current' and displays the current 'List Price', 'Bulk Price', and 'Materials Cost' that are called from the 'ROI - Current' to the 'ROI - Target' tab where gross margin is calculated.
However, when you changes through the options, you need to rewrite the called values to whatever it needs to be. When you're doing process improvement, you doing this this a lot just to see how the numbers are moving. So I'm looking for a way to repopulate data a specific way every time a list option is switched.
What I'd like to accomplish is when 'Current' gets selected, the *List Price $(75), Bulk Price $(60), Materials Costs $(18), are called back from the ROI - C tab. (Preferable, nothing would be modifiable unless you changed options, like when using the Validation's error message; but not essential).
Then if Increase/Decrease is selected, List Price and Material Costs populates with the Current $ (but are modifiable fields). Here, Bulk Price is calculated as (CurTabBulk/CurTabList)xIncDecList then used to find the gross margin.
Then when 'NPI' is selected, if only List Price is modified, it calculates bulk price as 0.7 x List Price to assume Material Costs to get gross margin. So as an example of all this:
$75 List x 0.8 = $60 Wholesale X 0.3 = $18 Materials / $60 Wholesale * 100 = 70% Gross Margin (which is the NPI Market Forecast from the ROI - Current Tab building the ROI - Target Tab.)
I can code all this into the cells directly, but whenever you enter your own value, the code that exists in that cell is overwritten. So, I'm looking for a fast 'n dirty way to repopulate it a specific way every time the option is switched.
Thanks all!
UPDATE: So I made it this far at work today using Alt-F11:
I found this code that I put into the VBA:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$G$4" Then
Range("H4:I7").Value = "Select"
End If
End Sub
That will set all validation boxes H4:I7 to "Select" after choosing the Category, but I want each cell to update with the value rather than having to keep selecting them since it's the only value in the list.
I then found this VBA that will reset as I need, but have no idea how to make it work at all...
Sub ResetDropDowns()
Dim rngLists As Range
Dim ListCell As Range
On Error Resume Next
Set rngLists = Sheets("Entry Sheet").UsedRange.SpecialCells(xlCellTypeAllValidation)
On Error GoTo 0
If Not rngLists Is Nothing Then
For Each ListCell In rngLists.Cells
ListCell.Value = Range(Trim(Mid(Replace(ListCell.Validation.Formula1, ":", String(99, " ")), 2, 99))).Value
Next ListCell
End If
End Sub
Also, After the independent Category is chosen, I will need to enter values into the white boxes under Product 1 and 2. These boxes move around depending on what ROI category is selected: Current = No white; Adj/ Price =- List & Costs, NPI = List & GM. So, I'm hoping I can just reassign the code to accomplish calling the 'dependent recalculated Values' to the correct cells as changes are made.
Follow the URL for pictures of the spread sheet. This is the dependent list code that I put into the Validation Source (the result numbers in the image) for Product 1 List Price: =INDEX($E$43:$G$43,,MATCH($G$4,$E$42:$G$42,0))
Thanks all for the help.
https://www.excelforum.com/excel-programming-vba-macros/1387101-auto-populate-dependent-validation-list-with-fist-values-from-list.html
This solves it. Right click the tab to open the Code editor. Copy and past below to the window. Change Sheet1 and the Cells to what ever you need. Create a Macro button using an inserted Icon and right click it and select make macro.
Sub ResetDropDowns()
Dim rngLists As Range
Dim ListCell As Range
On Error Resume Next
Set rngLists = Sheets("Sheet1").Range("G21,H21")
On Error GoTo 0
If Not rngLists Is Nothing Then
For Each ListCell In rngLists.Cells
ListCell.Value = Range(Trim(Mid(Replace(ListCell.Validation.Formula1, ":", String(99, " ")), 2, 99))).Value
Next ListCell
End If
End Sub

Find the nth instance of a number in a range of cells

I have a spreadsheet that contains entries for a general ledger account that we need to reconcile.
Ideally every entry starts with a set of numbers but sometimes due to human error or other reasons, the entries might have the number within the string of characters. For example, this is how the entry should look:
33333333 Name 12345
But sometimes it can look like this:
Name 33333333 12345
Or
12345 Name 33333333 Fee
What I'm trying to do is have a search for the number and tell me all the different cells that contain but don't necessarily start or end with that 33333333 number. Right now I have figured out how to get the first instance of the number, return the cell reference and then pull the value from the cell reference. Here is the formula for the cell reference:
=CELL("address",INDEX(C9:C199,MATCH("*"&B1&"*",C9:C199,0)))
Where C9:C199 is the range of cells I'm looking up and B1 is the Lookup cell, which will change depending on what number the Searcher wants to lookup. For example, the result could be $C$19 and then I have the Indirect function giving me the value of that cell reference. But I want to be able to pull the 1st, 2nd, 3rd, etc instance of a cell reference because there could be many, up to 5 or 6 cells that contain the number in some way (not all of which are relevant to what we're trying to reconcile but we want to see all the instances). So how do I get the 2nd instance of this formula, such that if there was a second answer of $C$26, I could also do an indirect reference to that cell and get that value as well.
=CELL("address",INDEX(C9:C199,MATCH("*"&B1&"*",C9:C199,0)))
My ultimate goal is to be able to easily see the following such that visually I can easily see that they offset. I've thrown in a random "fee" word because some users like to throw in random stuff like that:
33333333 Name 12345 Debit $500
Name 33333333 12345 Fee Credit $500
33333333 Name 12345 Other Fee Debit $250
Visually you would see the two $500 fees offset and then the remaining would be the $250 entry. Ultimately I would delete the two $500 entries to have the remaining $250 as the leftover to be offset at a future date. I can do the deleting of entries once I figure out how to get the nth cell reference instance.
Thank you!
You can try a custom function like this if you don't mind messing around with VBA and have Excel 365 or 2021 (dynamic array support is required for this function to be used in a cell). Add the function to a new module in the project and call it with =GetMatches(search range, search term). Also note, I've made this case insensitive with the use of LCase on both the search range and search term.
Function GetMatches(rng As Range, searchTerm As String) As Variant
Dim c As Range
Dim matches As New Collection
Dim output() As Variant
Dim iMax As Integer
Dim i As Integer
Set rng = Intersect(rng.Worksheet.UsedRange, rng)
For Each c In rng
If InStr(1, LCase(c), LCase(searchTerm)) > 0 Then matches.Add c
Next c
iMax = matches.Count
ReDim output(1 To iMax, 1)
For i = 1 To iMax
output(i, 0) = matches(i)
output(i, 1) = matches(i).Address
Next i
GetMatches = output
End Function
One way to do this, with Power Query:
Name cell B1 as 'tblSearchTerm' (create a ListObject) then name the source table as 'Table1' (create a LisObject).
If not shown, open the pane "Queries & Connections' (Data>Queries & Connections).
In 'ResultingSearchTable' right click and choose 'Load to...' then choose a starting cell to load the resulting search table.
If any term is found, the data will be listed as follows:
|RowNumber|NumberInRow|
RowNumber is the DataBodyRange row number of the ListObject.
let
tblSearchTerm = Excel.CurrentWorkbook(){[Name="tblSearchTerm"]}[Content],
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"AddedIndex" = Table.AddIndexColumn(Source, "Index", 1, 1, Int64.Type),
#"DividedColumnByDelimiter" = Table.ExpandListColumn(Table.TransformColumns(AddedIndex, {{"TextLines", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "TextLines"),
#"ChangedType" = Table.TransformColumnTypes(#"DividedColumnByDelimiter",{{"TextLines", type text}}),
#"ReplacedValue" = Table.ReplaceValue(#"ChangedType","$","",Replacer.ReplaceText,{"TextLines"}),
#"ChangedType1" = Table.TransformColumnTypes(#"ReplacedValue",{{"TextLines", type number}}),
RemovedErrors = Table.RemoveRowsWithErrors(ChangedType1, {"TextLines"}),
#"RenamedColumns" = Table.RenameColumns(RemovedErrors,{{"Index", "RowNumber"}, {"TextLines", "NumberInRow"}}),
#"FinalFilter" = Table.NestedJoin(#"RenamedColumns", {"NumberInRow"}, tblSearchTerm, {"SearchTerm"}, "tblSearchTerm", JoinKind.Inner),
#"ResultingSearchTable" = Table.SelectColumns(FinalFilter,{"RowNumber", "NumberInRow"})
in
#"ResultingSearchTable"
With data in C2:C199 (adjust as needed)
B2: (the address) =ADDRESS(AGGREGATE(15,6,1/ISNUMBER(FIND($B$1, $C$2:$C$199))*ROW($C$2:$C$199), ROW(INDEX($A:$A,1):INDEX($A:$A, COUNTIF(C2:C199,"*"&$B$1&"*")))),3)
A2: (the contents) =INDEX($C:$C,AGGREGATE(15,6,1/ISNUMBER(FIND($B$1, $C$2:$C$199))*ROW($C$2:$C$199), ROW(INDEX($A:$A,1):INDEX($A:$A, COUNTIF(C2:C199,"*"&$B$1&"*")))))

Sum of a specific range that changes on each iteration of a loop

I have a sheet that the values of a range change each time I change a specific cell. Let's say that the cell C8 is an indentity of a person and column H the scheduled monthly repayments. I need to find the aggregate monthly repayments, hence on each possible value of C8 (and that actually means for every person as you can think of different values of C8) I need the aggegate of repayments, hence the aggegate of cell Hi Hence, keeping row i constant and changing cell C8, I always need to sum Hi. So I actually need sum(Hi) (i constant and the index of the sum is cell c8, so if c8 takes value from 1 to 200, I need the sum(Hi(c8)), again row i . Hi(c8) it is just a notation to show you that Hi depends on the value of c8. The actual formula in cell H10 is INDEX('Sheet2'!R:R,MATCH('Sheet1'!$C$8,'Sheet2'!F:F,0)))). H11 and onwards have the same formula with slight twists for the fact that the repayments are not always equal, but the index function remains the same.
Then, the total of H10 for all possible values of c8 is pasted in c17, the total of H11 is pasted in C18 etc. Please find some images below, maybe that helps to support what I try to achieve. enter image description here
I have the following code for that purpose. Note that the above example was just to explain you a bit the background, the cells and the range that changes are different.
sub sumloop()
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Sheets("Sheet1").Range("C8").Value = 1
Dim i, k As Integer
i = 1
k = Sheets("Sheet1").Range("C9").Value
Dim LR As Long
LR = Sheets("Sheet1").Range("C" &
Sheets("Sheet1").Rows.Count).End(xlUp).row
Sheets("Sheet1").Range("C17:C" & LR).ClearContents
Do While i <= k
If (Sheets("Sheet1").Range("J9").Value = "") Then
Sheets("Sheet1").Range("h10:h200").Copy
Sheets("Sheet1").Range("c17").PasteSpecial
Paste:=xlValues, Operation:=xlAdd, SkipBlanks:= _
False, Transpose:=False
Else
Sheets("Sheet1").Range("h9:h200").Copy
Sheets("Sheet1").Range("c17").PasteSpecial
Paste:=xlValues, Operation:=xlAdd, SkipBlanks:= _
False, Transpose:=False
End If
Sheets("Sheet1").Range("C8").Value = Sheets("Sheet1").Range("C8").Value+1
i = i + 1
Loop
Sheets("Sheet1").Range("C8").Value = 1
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
End Sub
The if inside of the loop is needed as the location of the first value of the range depends on some criteria which have not to do with the code. Also k denotes the maximum number of possible values. What I need is approximately 250.
While the code works, it takes approximately 40 seconds to run for 84 values of cell C8 and approximately 1.5 minute for 250. I tried some things, changed do while to for but nothing significant, used variable ranges instead of fixed ones like h10:h100, very similar to what I do with Sheet1.Range(C17:C&LR). Again no significant changes. As I am very new to vba I don't know if 1.5 minutes are a lot for such a simple code, but to me it seems a lot and this analysis is needed for 10 different combinations of 250 different values for cell c8, which means 15 minutes approximately.
I would appreciate if anyone can suggest me something faster.
Thank you very much in advance.
Here is a complete solution, with explainations in comments.
Because we do not have you source spreadsheet, I could not run any tests on this.
Option Explicit 'This forces you to declare all your varaibles correctly. It may seem annoying at first glance, but will quickly save you time in the future.
Sub sumloop()
Application.ScreenUpdating = False
'Application.DisplayStatusBar = False -> This is not noticely slowing down your code as soon as you do not refresh the StatusBar value for more than say 5-10 times per second.
'Save the existing Calculation Mode to restore it at the end of the Macro
Dim xlPreviousCalcMode As XlCalculation
xlPreviousCalcMode = Application.Calculation
Application.Calculation = xlCalculationManual
'Conveniently store the Sheet into a variable. You might want to do the same with your cells, for example: MyCellWhichCounts = MySheet.Range("c17")
Dim MySheet As Worksheet
MySheet = ActiveWorkbook.Sheets("Sheet1")
MySheet.Range("C8").Value2 = 1 'It is recommended to use.Value2 instead of .Value (notably in case your data type is Currency, but it is good practice to use that one all the time)
Dim LR As Long
LR = MySheet.Range("C" & MySheet.Rows.Count).End(xlUp).Row 'Be carefull with "MySheet.Rows.Count", it may go beyond your data range, for example if you modify the formatting of a cell below your "last" row.
MySheet.Range("C17:C" & LR).Value2 = vbNullString 'It is recommended to use vbNullString instead of ""; although I agree it makes it more difficult to read.
Dim i As Integer, k As Integer 'Integers are ok, just make sure you neer exceed 255
k = MySheet.Range("C9").Value2
For i = 1 To k 'Use a For whenever you can, it is easier to maintain (i.e. avoid errors and also for you to remember when you go back to it years later)
'Little extra so you can track progress of your calcs
Dim z As Integer
z = 10 'This can have any value > 0. If the value is low, you will refresh your app often but it will slow down. If the value is high, it won't affect performance but your app might freeze and/or you will not have your Statusbar updated as often as you might like. As a rule of thumb, I aim to refresh around 5 times per seconds, which is enough for the end user not to notice anything.
If i Mod z = 0 Then 'Each time i is a mutliple of z
Application.StatusBar = "Calculating i = " & i & " of " & k 'We refresh the Statusbar
DoEvents 'We prevent the Excel App to freeze and throw messages like: The application is not responding.
End If
'Set the range
Dim MyResultRange As Range
If (MySheet.Range("J9").Value2 = vbNullString) Then
MyResultRange = MySheet.Range("h10:h200")
Else
MyResultRange = MySheet.Range("h9:h200")
End If
'# Extract Result Data
MyResultRange.Calculate 'Refresh the Range values
Dim MyResultData As Variant
MyResultData = MyResultRange.Value2 'Store the values in VBA all at once
'# Extract Original Data
Dim MyOriginalRange as Range
MyOriginalRange.Calculate
MyOriginalRange = MySheet.Range("c17").Resize(MyResultRange.Rows.Count,MyResultRange.Columns.Count) 'This produces a Range of the same size as MyResultRange
Dim MyOriginalData as Variant
MyOriginalData = MyOriginalRange.Value2
'# Sum Both Data Arrays
Dim MySumData() as Variant
Redim MySumData(lbound(MyResultRange,1) to ubound(MyResultRange,1),lbound(MyResultRange,2) to ubound(MyResultRange,2))
Dim j as long
For j = lbound(MySumData,1) to ubound(MySumData,1)
MySumData(j,1)= MyResultData(j,1) + MyOriginalData(j,1)
Next j
'Instead of the "For j = a to b", you could use this, but might be slower: MySumData = Application.WorksheetFunction.MMult(Array(1, 1), Array(MyResultData, MyOriginalData))
MySheet.Range("C8").Value2 = MySheet.Range("C8").Value2 + 1
Next i
MySheet.Range("C8").Value2 = 1
Application.ScreenUpdating = True
Application.StatusBar = False 'Give back the status bar control to the Excel App
Application.Calculation = xlPreviousCalcMode 'Do not forget to restore the Calculation Mode to its previous state
End Sub
Added by OP (see comments)
Image 1 Code written in the initially question. enter image description here
Image 2 Code above enter image description here
OK, A few things.
Firstly, Dim i, k As Integer doesn't do what you think it does, you need to do: Dim i As Integer, k As Integer
Secondly don't use Integer in VBA use Long so Dim i As Long, k As Long
Third the calculations are killing you. Turn them off with Application.Calculation = xlCalculationManual at the start of your code and back on with Application.Calculation = xlCalculationAutomatic at the end of your code.
Now we are presented with really fast code but the problem that it doesn't update on each iteration which you need it to do. You can calculate just a range like so: Sheets("Sheet1").Range("h10:h200").Calculate so put that in just before you copy the range
There will be an even faster way to do this but I just can't seem to wrap my head around your requirements so I am unable to assist further.
Welcome to StackOverflow.
I must admit I got a bit confused by your narrative, as I did not fully understand if you are doing a sum(a,b,c) or a sum(sum(a,b,c), sum(d,e,f), ...).
In any cases, a trick that will dramatically accelerate your script is the use of arrays.
Performing calcs with VBA is not slow, but retrieving the data from Excel (communicating with the application) IS slow, and pretty much depending on the number of "requests", rather than the quantity of data requested.
You can use arrays to request the data from a range all at once, isntead of requesting the value of each cell separately.
Dim Arr() As Variant
Arr = Range("A1:E999")
It is as simple as this.
Give it a try and if you are still struggling let us know.
BONUS
If you are new to Arrays, keep in mind you can have a two-dimmensionnal array:
Dim 2DArray(0 to 10, 0 to 50)
Or a stacked array (an array of arrays):
Dim MyArray() as String
Dim StackedArray() as MyArray
Dim StackedArray() as Variant
You will need a 2D-Array for extracting the data from a range, but I feel you may need an Array of 2D-Arrays for your Sum of Sums.
Some recommended reading: https://excelmacromastery.com/excel-vba-array/
How to achieve the same through pivot charts (no VBA)
Step 1
First, you must organize your data in a specific way, where each column is a field, and each row is a data entry. If you are not familiar with databases, this is the most tricky point as you may arrange your data in different ways.
Long story short, we will take an example where you have 3 customers and 4 dates.
So that is 12 data entries, which will provide the repayment value for each of the possible customer ID and date.
Step 2
Select that data and insert a PivotChart.
Note: you could insert a PivotTable alone, or a PivotChart alone. I recommend the option hwere you insert both, as managing your data will be more intuitive when working on the Chart. The table is updated at the same time you update the chart.
Step 3
Make sure the all your data is selected, including the top row which will dictate the name of each field (the name of each column).
Step 4
A new sheet has just been create, and you can see where both your PivotTble and PivotCharts will appear. Select the chart.
Step 5
A menu to the right will appear (it might have already been there, so make sure you selected the Chart and not the Table, as that menu would be slightly different).
Step 6
Drag and drop the field names into the categories as shown.
What you are doing here is telling Excel what data you want to see (Values) and how you want to break it down (per date, and per customer).
Step 7
By default dates data is always groupped quartile and year. To be able to see all the date we have data for, you can click the [+] near the data on the Table: this will show more details for both the table and the chart.
Step 8
But we want to get completely rid of the quartils and years. In order to achieve this, you need to right click any value of your date column in the Table, and choose "Ungroup" as displayed.
Step 9
Your data now looks like this.
Note the time axis is not on scale. For example if you hae monthly data and a month is missing, there will be no gap. This is one of the difficulties with Pivot data. This can be overcomes, but it is off topic here.
Step 10
Now we want to have a cumulative view of the data, so we want to play with the way the values are proessed by Excel.
Select the chart, then in the right panel: right click on the "Sum of Repayment" field, and select "Value Field Settings".
Step 11
In the "Show Values As" tab, select "Show values as" "Running Tital In".
Then choose "Date".
Here we are telling Excel that the value to display should be a cumulative total, cumulated according to the "Date" field.
Press OK.
Step 12
You now have what you are looking for. If you look in the Table, you have one column per Customer ID, and one row per date. For a given Date, you have the cumulative repayment made by a given Customer ID. At the very right, you have the Grand Total, which is, for a given date, the sum of all the Customer ID values.
Step 13
The Chart keeps showing the cumulative payment per CUstomer ID, and we cannot see the grand total.
In orer to achieve this, simply remove the "Customer ID" field from the "Legend (Series)" category area in the Fields Panel, as shown. (you can untick the Customer Id [x] box, or you can drag and drop it from the category area to the main list area).
Step 14
Now we only have the Grand total in the chart. But why?
If you display the "Value Field Settings" of Sum of Repyament" (Step 10), the first tab "Summarize Values By" will tell Excel what to do when several value meet the same Legend and Axis values.
Now that we removed the Customer ID field from the Legend area, for each date, we have 3 repayment values (one for each Customer ID). In the field settings, we tell Excel to use a "Sum". So it returns the sum of the 3 values.
But you could play around and return the Average, or even use "Count", which will show you how many records you have (it will return 3).
That is why pivot charts are so powerful: with only a few clicks and/or drag and drop, you can display a myriad of different graphics for your data.
For future interest, you should look online for Filters, and "Insert Slicer" (which is equivalent to filtering, but will add button directly on your chart: great when showing the data to colleagues and switch from one setting to another)
Hope this helped!

How to add all available value or label filters from a pivot table into a listbox? (Excel VBA)

Okay, so I have a pivot table where there is a column of provinces and other columns of sales per years.
I have created macros through vba to apply filtering on the pivot table.
(Recorded macros)
I want to add all available label and value filters into a listbox eg:
Label filters are :
Equals...
Does not equal...
Begins with...
Etc...
I want all of them in a listbox, or combobox, or anything the user can click and expand to select what filter they want.
Any help please?
jamesalainheffer#gmail.com
Okay well I seemed to have figured it out. Most people don't know about the filtering options in excel. And if they do, they probably would go about doing it in VBA by saying something like
`With
ActiveSheet.PivotTables("pvt_Data").PivotFields("Province")`
.PivotItems("Yukon").Visible = false
and so on... By just making the field or item not visible, doesn't neccessarily mean you're applying a filter to anything...
So, here's what I found...
dim prov as string
dim letter as string
dim val2 as string
dim pvtPivotTable as PivotTable
dim pfdValueField as PivotField
dim pfdTRowField as PivotField
set pvtPivotTable = ActiveSheet.PivotTables("pvt_Data")
set pfdValueField = ActiveSheet.PivotTables("Sum of Sales")
set pfdTRowField = pvtPivotTable.PivotFields("Province")
letter = TextBox4.Value
prov = TextBox1.Value
msg = ", Caption Filters, " & listbox1.value
with pvtPivotTable
.AllowMultipleFilters = True
pfdTRowField.PivotFilters.Add2 Type:=Listbox1.Value, Value1:=prove, Value2:=letter
end with
set pfdValueField = nothing
set pfdRowField = nothing
filterlistbox.AddItem(Listbox1.Text & " " & prov & msg
now what this does along with the rest of my projects is it loads every xlFilterType into 3 listboxes, per categpry of Caption filters (text), Value Filters (numerical), and date filter (date)
3 listbox's for 3 categories of filter types, each listbox is loaded with them (filter types [xlBefore, xlCaptionIsEqual]) when you select a filter type, enter a value into a textbox and push a button and it weill then apply that filter onto my pivot table.
The listbox where the filter type gets added I did like this:
.addItem("Equall...")
.list(0, 1) = "15"
What this does is add that "Equall" to the list box, the list statement just inserts 15 into the 2nd column on the 1 st index (index starts at 0, 1 is the 2nd column)
Now, the xlFilterTypes have a value as well as a name for example XlCaptionEqualls is the name and its value is 15, the listbox1.value is this value becuase i set the bound column to number 2, so the listbox pushes out the filterTypes value (number) instead of the text I hard coded, you can then use case statements to get the name from the value and insert them into another listbox.
this is a bit all over the show but I would like to know if nayone has managed to add multiple filters onto their pivot table using vba? and the add2 method?
Kick ass.

Autofilter criteria value in VBA

I have a table with activated autofilter mode and it is known that only Criteria1-type filtering is applicable (i.e. items of interest are implicitly indicated). My goal is to extract a criteria list for each column in VBA. I used IsArray(.Filters(i).Criteria1) to determine if there is more than 1 item selected for a particular column and everything works fine when either 1 or more than 2 items are selected. However, when I select 2 items, .Filters(i).Criteria1 is not recognized as an array for some reason. .Filters(i).Criteria1 returns only the item that is higher in the list.
Could anyone explain me: why is it so and what is the best way to deal with this problem?
This is an old as question, but in case anyone ends up confused here, confused.
With the a filters object (part of the worksheet.autofilter.filters collection probably)
Here is 1 happens:
1 filter on a column:
filters(i).criteria1 is a string
2 filters on a column:
filters(i).criteria1 is a string
filters(i).criteria2 is a string
3 or more filters on a column:
filters(i).criteria1 is an array of strings (which is a variant)
filters(i).criteria2 doesn't exist
This isn't that clear from MSDN, but it's what happens. My only guess is that in past versions of excel it wasn't possible to add more than 1 filter criteria, and so the old criteria1 and criteria2 types were maintained for backwards compatibility. Still though, it's a stupid system.
Which means that the only way to get the filter properties (as far as I can see) is as follows:
For Each f In ActiveSheet.AutoFilter.Filters
If f.On Then
If IsArray(f.Criteria1) Then
cA = f.Criteria1
Else
c1 = f.Criteria1
On Error Resume Next
c2 = f.Criteria2
On Error GoTo 0
End If
End If
Next
Because Criteria2 errors for both IsEmpty(f.Criteria1) and IsNull(f.Criteria1) so as far as I can see the only way to check if it exists is with an ugly on error resume next

Resources