I'm not sure if this is possible but as you can see I have a pivot table with multiple dependent and expandable fields. I am trying to concatenate the data from columns A:D into one cell which works fine in row 2 but doesn't work with blank parent cells, as you can see in column F.
Any ideas for how to achieve this?
Pivot table
This answer assumes that you don't want to just Repeat All Item Labels in the PivotTable from the "Report Layout" drop-down on the Pivt Table Tools "Design" tab.
A formula to get the first non-blank value on or above the same row as the current cell from Column B can be constructed with a combination of AGGREGATE, SUMPRODUCT and OFFSET, like so:
=OFFSET($B2,SUMPRODUCT(AGGREGATE(14,6,ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0),1))-ROW(),0)
How does it work?
Starting with the outermost part, OFFSET($B2, VALUE, 0) - this will start in cell B2, then look up or down by VALUE rows to get the value.
Next we need to know how many rows we will need to look up-or-down. Now, if we can work out the bottom-most row with data, we can subtract the current ROW() from that, giving us OFFSET($B2, NON_BLANK-ROW(),0)
So, to finish up we need to work out which rows are not blank, AND which rows are on-or-above our current row, then take the largest of those. This is going to take an ArrayFormula, but we can use SUMPRODUCT to make that calculate properly. To find the largest number we could use MAX or LARGE - but we get less errors if we pick AGGREGATE(14,6,..,1). (The 14 means "we want the kth largest number", the 6 means "ignore error values", and the 1 is k - so "we want the largest number, ignoring errors")
But, what list of numbers are we going to look at, I don't hear you ask. Well, we want the ROW for output from our range (I'm using $B$1:$B$100, because using the whole column B would take far to long to calculate repeatedly), a comparison against the current ROW(), and check that the LENgth is > 0. Those last two are comparisons, so let's write them out first:
ROW($B$1:$B100)<=ROW()
and
LEN($B$1:$B$100)>0
We want to use -- to convert TRUE and FALSE to 1 and 0 - this means that any "bad" values become 0, and any "good" values are larger than 0:
ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0)
This gives us the Row number when the Row is on-or-before the current row AND Column B is not blank - if either of those are False, then we get 0 instead. Stick that in the AGGREGATE to find the largest number:
AGGREGATE(14, 6, ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0), 1)
Then put it in a SUMPRODUCT to force Excel to treat it as an ArrayFormula, and that's your NON_BLANK. This then gives you that first formula right at the top of the post
Related
Hi, I am totally new to Excel VBA. Firstly, I want to copy the data when the condition is met(copy data with reference to 144)
Secondly, compare the cells, if it is IT Operations(Table1) to IT Operations(Table2) then copy the price(money) to column F. If the variable is no there then leave blank.
This can be done with formulas. Here is one way of thinking about filling column F, with the prices for the matching items in column E, by matching the number given in the last row in E (144 Total); which i shall assume is E10 in this case.
Total formula in F1 which you then drag down is:
=IFERROR(IFERROR(VLOOKUP(E1,INDIRECT(CELL("address",OFFSET($H$1,MATCH(1*LEFT($E$10,FIND(" ",TRIM($E$10),1)-1),$G:$G,0)-1,,1,1))&":"&CELL("address",OFFSET($I$1,MATCH($E$10,$G:$G,0)-1,,1,1))),2,FALSE),VLOOKUP(E1,G:I,3,FALSE)),"")
In steps:
Extract the number of interest e.g. 144, and get rid of any trailing/leading whitespace using:
LEFT($E$10,FIND(" ",TRIM($E$10),1)-1)
Find which row this value is in as this will be the first row of the lookup range for this number. *1 converts text to a number.
MATCH(1*LEFT($E$10,FIND(" ",TRIM($E$10),1)-1),$G:$G,0)
This gives row 9.
We can use something simpler to find the last row of the range, which holds 144 Total
MATCH($E$10,$G:$G,0)
This gives row 15. So we know the data lies between rows 9 and 15 for 144.
We can turn this into a range to use in a VLOOKUP with INDIRECT and OFFSET.
=CELL("address",OFFSET($G$1,MATCH(1*LEFT($E$10,FIND(" ",TRIM($E$10),1)-1),$G:$G,0)-1,,1,1))&":"&CELL("address",OFFSET($H$1,MATCH($E$10,$G:$G,0)-1,,1,1))
This gives us $G$9:$H$15. Note adjustments of -1, to put OFFSET back in the right row, and that the OFFSET start cells are in different columns to provide the columns required for the VLOOKUP.
So we can now lookup column E values e.g. Enhancement, in our newly defined range which is accessed via INDIRECT:
=VLOOKUP(E1,INDIRECT(CELL("address",OFFSET($H$1,MATCH(1*LEFT($E$10,FIND(" ",TRIM($E$10),1)-1),$G:$G,0)-1,,1,1))&":"&CELL("address",OFFSET($I$1,MATCH($E$10,$G:$G,0)-1,,1,1))),2,FALSE)
This is saying VLOOKUP(E1,$G$9:$H$15,2,FALSE) i.e. get the price column from the range for the item specified in E1.
If this is not found i.e. returns #N/A, we can use this to first check if this is because of the merged cell that holds the 144 Total; where the value is actually in column G not H, and use an IFERROR to say, if not found in $G$9:$H$15 then try for a match using columns G:I and return column 3.
Which with pseudo formula, using priorLookup as placeholder, for the formula described in the steps above, looks like:
IFERROR(priorLookup, VLOOKUP(E1,G:I,3,FALSE))
If this still returns #N/A, we know the value is not present and we should return "". This we can handle this with another IFERROR:
IFERROR(IFERROR(priorLookup, VLOOKUP(E1,G:I,3,FALSE)),"")
So giving us the entire formula stated at the start.
Here it is used in the sheet:
I've got a challenge in Excel i hope you guys can solve for me.
I have a drop down list (weeks) where i select the week 2015-18 to 2016-17. (nr 1 in picture)
In the cell called LY (nr 2 in picture) I want that to type the result of a dynamic sum range, based on the weeks input.
Picture of setup of the text above
The calculation logic is:
If i select week 2015-20, the VBA or formula should sum(F5:F7)/(G5:G7).
So in other words, I want a dynamic calculation that starts from week 2015-18 (F5/G5) and then sums the values down to the value that i have selected in "Weeks"
Picture of the setup of the logic values
The value of this calculation should be shown in LY (first picture, nr 2).
I really hope you can solve this for me. I've got more rows to calculate, so if you could come up with a "global" code that works for that, it would be great
ok, so you do not need VBA for this.....
for the sake of easier updates; mark the weekrange, right click them and give them a name, with Define name
I called it yearlyweeks
The two drop downs lists, will contain the value corresponding to the text indicating that week.
So we can use this to get a cells address by value (if that text is unique in the range named yearlyweeks):
=ADDRESS(MATCH(H8,yearlyweeks, 0), 2)
where H8 is the cell address of a dropdownlist cell. 2 is the column index of the "B" column
this will result in something like:
$B$2
where the 2 indicates the relative row index, in the range, not the absolute row number.
we are really not interested in the column, or relative row, only the absolute row, but so far we can live with relative..:
=ROW(INDIRECT(ADDRESS(MATCH(H8,yearlyweeks, 0), 2)))
this will give you the relative row of the cell coresponding to what you selected on the drop downlist for one of the lists. So I would do this in two calculation cells, just to avoid the next piece getting too long..
lets let the calculation cells be in I10, and J10..
I10 hold the relative row for the beginning week
J10 for the ending week
below create a new set of cells with:
="F"&(I10 + 7)
and
="G"&(J10 + 7)
where 7 is the row offset of yearlyweeks
in yet one more cell J12 we make the range string:
=CONCATENATE(I11;":";J11)
and then does the calculation in the final cell:
=SUM(INDIRECT(J12))
Now I can do this as a one liner, but you really would hate that
I have the following array formula which works for what I want to do but I'm trying to change the formula when a user selects a value.
=INDEX($A$2:$B$70,SMALL(IF($A$2:$B$70=$A$121,ROW($A$2:$B$70)),ROW(1:1))-1,1)
It's used for a monthly report and the user will choose from a drop down the day of the month, e.g 1,2,3 - 31.
So if the user selects 1 from the drop down menu I want the formula to use the above formula.
If they select 2 for example I want the formula to move over a column so it would change to
=INDEX($A$2:$C$70,SMALL(IF($A$2:$C$70=$A$121,ROW($A$2:$C$70)),ROW(1:1))-1,1)
and so on moving over a column at a time.
It this possible at all or can it even be done without VBA?
I have an example of what I want done on the following link
https://docs.google.com/spreadsheets/d/1MDOzoQxYLgW-UOyljZsMwSu8zyAB7O2k1V-bTNP5_F0/edit?usp=sharing
All the data is on the first tab called staff. Each employee has a row and the duty assigned under the corresponding day column.
On the Roster tab it summarises each day. So what I am trying to get to happen is when you choose the day of the month (or preferably the actual date) the sheet changes to reflect the data.
At the moment the code I have working does for just Day 1 because the column references are coded into the formula. I was hoping to somehow choose 6 for example from the drop down and then the formula will map chosen day to the corresponding range in the raw data and update the formula and change the formula from Staff!$A$2:$B$68 to Staff!$A$2:$G$68.
If the formula finds no more entries if shows #NUM! but I intended to use the function ISERROR() to replace #NUM! with "".
This is what I'm trying to achieve it if makes sense?
There are a few issues here/ You are returning the value from column A so the first range can be $A$2:$A$70 and that means you don't need the 1 to specify the column_num. The IF statement was covering A2:C70 when you really only want either B2:70 or C2:C70 depending on the 1 or 2.
Assuming that A122 has either a 1 or 2 in it then,
=INDEX($A$2:$A$70, SMALL(IF(INDEX($B$2:$C$70, 0, $A$122) = $A$121, ROW($1:$69)), ROW(1:1)))
Standard non-array alternative,
=INDEX($A$2:$A$70, SMALL(INDEX(ROW($1:$69)+(INDEX($B$2:$C$70, 0, $A$122) <> $A$121)*1E+99,, ), ROW(1:1)))
I am new to asking questions here so I hope I get this correct. I am helping my dad with a spreadsheet and I'm having issues with figuring out how to do one formula. Dont know if it can be done with a formula or if it has to be done with macros.
This is a scoring sheet with multiple matches. For each match there is a total score and the cell next to the score is an X count (number of bulleyes). In the same row (column K) I calculate the top 6 total scores and average them:
=AVERAGE(LARGE((N15,Q15,T15,W15,Z15,AC15,AF15,AI15,AL15,AO15,AR15,AU15,AX15,BA15,BD15,BG15,BJ15),{1,2,3,4,5,6}))
Now I need to take the AVG of the X counts that are next to the total scores that are used in the formula above and put solution in column L.
For example, if the cells that are used for AVG score in that row are:
N15,Q15,T15,W15,Z15,AC15
then the cells that would need to be used for the X count AVG would be:
O15,R15,U15,X15,AA15,AD15
This result would be put into L15
Please help. If any clarification is needed just let me know.
Screen Shot:
Please try the following formula:
=SUMPRODUCT(O15:BM15,
--(MOD(COLUMN(N15:BL15)-COLUMN($N15),3)=0),
--(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6>=
LARGE(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6,6))
)/6
How does it work?
SUMPRODUCT has 3 parameters - first is the array to sum, next 2 parameters return an array of 0 and 1 to choose only interesting elements of the first array.
MOD(COLUMN(N15:BL15)-COLUMN($N15),3)=0)
This part is included to avoid listing every single cell. If the score is in every third column of the input range, we can calculate column number relative to first column, and function MOD(column,3) returns: {1,0,0,1,0,0...}. So only every third column of input array will be included in sum.
(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6>=
LARGE(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6,6)
This part is to decide which 6 of the scores should be included in the final sum. The trickiest part is to decide what to do with ties. My approach is to take:
if two scores are the same, take the one with higher number of bulleyes
if it is still tied, take the one from first columns
This means that instead of N15 value we calculate:
N15+O15/10^3+COLUMN(N15)/10^6
With your sample data it evaluates to: 566.017014. First three decimal places is the number of bulleyes, next 3 is column number.
You can use the same formula to calculate average of top 6 scores by changing the first parameter:
=SUMPRODUCT(N15:BL15,
--(MOD(COLUMN(N15:BL15)-COLUMN($N15),3)=0),
--(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6>=
LARGE(N15:BL15+O15:BM15/10^3+COLUMN(N15:BL15)/10^6,6))
)/6
You can try this not so elegant solution:
=SUMPRODUCT(INDEX(N15:BK15,MATCH(LARGE((N15,Q15,T15,W15,Z15,AC15,AF15,AI15,AL15,AO15,AR15,AU15,AX15,BA15,BD15,BG15,BJ15),{1,2,3,4,5,6}),N15:BK15,0)+1))/6
Entered as array formula by Ctr+Shift+Enter in Cell L15:M15 (2 cells) which should look like this:
{=SUMPRODUCT(INDEX(N15:BK15,MATCH(LARGE((N15,Q15,T15,W15,Z15,AC15,AF15,AI15,AL15,AO15,AR15,AU15,AX15,BA15,BD15,BG15,BJ15),{1,2,3,4,5,6}),N15:BK15,0)+1))/6}
with added braces.
The number 6 is the equates to the number of top scores you want returned.
Now, why 2 cells (L15:M15). I cannot make SUMPRODUCT evaluate the resulting array from the INDEX so we have to enter it at 2 cells. I don't think that would be a problem since in your screen shot, Column M is not used.
Note: If the range evaluated have less than 6 items, it will error out. Also good point by user3964075. It may or may not be able to deal with ties.
I am trying to find the average for the last 3 instances only. I am using the AVERAGEIF statement and it will calculate the average for the entire range but I need it to only calculate for that last 3 instances it finds (or less if there is less than 3 available). I need the entire column for G and H to have the average for the last 3 games that the Team played.
This is what I have:
=AVERAGEIF(B3:C17,B17,D3:E17)
You can do this with array formulas (They have to be entered using the keys Ctrl+Shift+Enter)...
Basic steps are:
Find the row (including and above current) that is the third highest row number containing the team name (or use row 1 otherwise)
Use the INDIRECT ranges in your AVERAGEIF from B-that_row to C-current_row and D_that_row to E-current_row
So in cell F17 you would have the formula
{=AVERAGEIF(INDIRECT("B"&LARGE(IF(--($B$3:B17=B17)+($C$3:C17=B17),ROW($B$3:B17),1),3)&":"&CELL("address",C17)),B17,INDIRECT("D"&LARGE(IF(--($B$3:B17=B17)+($C$3:C17=B17),ROW($B$3:B17),1),3)&":"&CELL("address",E17)))}
We repeat some of the logic, because we have two ranges (criteria range and average range).
IF(--($B$3:B17=B17)+($C$3:C17=B17),ROW($B$3:B17),1) means that if column B or (using +) column C has the value of in B17, give me the row number, otherwise 1 (our <3 case... we could make this 3, the first row of team names)
LARGE(...,3) will give us the third highest of this array --> the third highest row number having our team name
INDIRECT("B"&...&":"&CELL("address",C17)) is going to give us the range using our third highest row number to the current row, columns B and C
then we do exactly the same thing as you were doing in AVERAGEIF but using this INDIRECT range and the equivalent for columns D and E
Fun question! Good luck. And remember to use Ctrl+Shift+Enter to enter it!
EDIT The above was giving an #NUM! error for the first two rows - that was because the LARGE function was trying to get the third largest in an array of 2! Also noticed that there were some cases where the column letter needed to be absolute (i.e. $) for copying to the Away column. So the updated formula:
{=AVERAGEIF(INDIRECT("B"&LARGE(IF(--($B$3:$B17=B17)+($C$3:$C17=B17),ROW($B$3:$B17),1),MIN(3,ROW()-2))&":"&CELL("address",$C17)),B17,INDIRECT("D"&LARGE(IF(--($B$3:$B17=B17)+($C$3:$C17=B17),ROW($B$3:$B17),1),MIN(3,ROW()-2))&":"&CELL("address",$E17)))}
Replaced the 3 with MIN(3,ROW()-2) so that we get 3 if there are, but 1 or 2 if we are in one of the first two data rows
OK I posted this prematurely and attempted to delete it when I realised it wouldn't work. It should work now.... providing you add another condition which is the game dates in column A. Remember that this is an array formula so hit ctrl+shift+enter. Dates in column A; teams in column B; stats in column D. This formula can reside somewhere permanent on the sheet so you can enter the team name (shown as F13 here) to get the three most recent stats.
=AVERAGE(VLOOKUP(LARGE(IF(B3:B24=F13,A3:A24),1),A3:D24,4),VLOOKUP(LARGE(IF(B3:B24=F13,A3:A24),2),A3:D24,4),VLOOKUP(LARGE(IF(B3:B24=F13,A3:A24),3),A3:D24,4))