Find the weighted average of two rows based on another containing dates - excel

I have a table in which one row contains dates, another row contains AHT (Avg Handle Time) and the third row contains no of calls handled.
I have situation where I need to find the Weighted average for each week in another table. I am able to find simple average for each week. However not getting this weighted average for each week.
Thanks

This should work in most versions of Excel
=SUMPRODUCT(INDEX($3:$3,MATCH("Week "&A9,$1:$1,0)):INDEX($3:$3,MATCH("Week "&A9,$1:$1,0)+6),
INDEX($5:$5,MATCH("Week "&A9,$1:$1,0)):INDEX($5:$5,MATCH("Week "&A9,$1:$1,0)+6))
/SUM(INDEX($3:$3,MATCH("Week "&A9,$1:$1,0)):INDEX($3:$3,MATCH("Week "&A9,$1:$1,0)+6))
May need to be array-entered pre Excel 365.
Notes
The weighted mean formula is
Weighted mean = Σwx/Σw
where are the weights and x are the values. So in this case, from OP's comment, the third row is the weights (number of calls) and the last row is the values (AHT).
The easiest way to get Σwx is to use Sumproduct, and to get Σw is just to use Sum. So the basic formula for (say) week 40 would be simply
=SUMPRODUCT(A3:G3,A5:G5)/SUM(A3:G3)
However, I reasoned that it would be inconvenient to re-write this formula for each different week, so I have used Match to find the starting column of each week from row 1 , then index to find the corresponding position in either row 3 or 5 (let's call it startpos), then index again to find the position six places to the right of startpos (let's call it endpos). The required range to be placed in each part of the short formula above is therefore startpos:endpos (I can use this notation because startpos and endpos, the values returned from the Index function, are both references).
If Excel 365 is available, this can all be expressed much more succinctly and clearly using Let to assign variables names to each part of the formula.
=LET(startCol,MATCH("Week "&A9,$1:$1,0),
startWeight,INDEX($3:$3,startCol),
endWeight,INDEX($3:$3,startCol+6),
startValue,INDEX($5:$5,startCol),
endValue,INDEX($5:$5,startCol+6),
weightRange,startWeight:endWeight,
valueRange,startValue:endValue,
SUMPRODUCT(weightRange,valueRange)/SUM(weightRange))

Related

Excel: Make a dynamic formula that counts a specified max sum of X consecutive days

I am trying to make a formula that could count the max sum of any number of consecutive days that I indicate in some cell. Here is the dataset and the formula:
Dataset
The formula that calculates the maximum sum of three consecutive days:
=MAX(IFERROR(INDEX(
INDEX(E2:AI2,0)+
INDEX(F2:AI2,0)+
INDEX(G2:AI2,0),
0),""))
As you can see the number of days here is determined by the number of rows in the formula that start with "Index". The only difference between these rows is the letters (E, F, G). Is there any way I could reference a cell in which I could put a number for those days, instead of adding more rows to this formula?
Another approach avoding use of Offset is to use Scan to generate an array of running totals, then subtract totals which are N elements apart (where N is the number of consecutive cells to be added):
=LET(range,E2:AI2,
length,A1,
runningTotal,SCAN(0,range,LAMBDA(a,b,a+b)),
sequence1,SEQUENCE(1,COLUMNS(range)-length+1,A1),
sequence2,SEQUENCE(1,COLUMNS(range)-length+1,0),
difference,INDEX(runningTotal,sequence1)-IF(sequence2,INDEX(runningTotal,sequence2),0),
MAX(difference))
The answer here was posted by another user on another website, so I will repost it here:
One way to achieve this without relying on a VBA solution would be to use the BYCOL() function (available for Excel for Microsoft 365):
=BYCOL(array, [function])
The array specifies the range to which you want to apply your function, and the function itself is specified in a lambda statement. In the end, you want to get the minimum value of the sum of x consecutive days. Assuming that your data is stored in the range E2:AI2 and the number of consecutive days is stored in cell A1, the function looks like this:
=MIN(BYCOL(E2:AI2,LAMBDA(col,SUM(OFFSET(col,,,,A1)))))
The MIN() part ensures that you get only the smallest sum of the array (all sums of the x consecutive values) returned. The array is simply the range in which your data is stored; it is named in the lambda argument col and consequently used by its name. In your case, you want to apply the sum function for, e.g., x = 4 consecutive days (where 4 is stored in cell A1).
However, with this simple specification, you run into the problem of offsetting beyond cells with values toward the right end of the data. This means that the last sum you get would be 81.8 (value on 31 Jan) + 3 times 0 because the cells are empty. To avoid this, you can combine your function with an IF() statement that replaces the result with an empty cell if the number of empty cells is greater than 0. The adjusted formula looks like this:
=MIN(BYCOL(E2:AI2,
LAMBDA(col,IF(COUNTIF(OFFSET(col,,,,A1),"")>0,"",SUM(OFFSET(col,,,,A1))))))
If you do not have the Microsoft 365 version, there are two approaches that would also work. However, the two approaches are a bit more tedious, especially for cases with multiple days (because the number of days can not really be set automatically; except for potentially constructing the ranges with a combination of ADDRESS() and INDIRECT()), but I would still argue a bit neater than your current specification:
=MIN(INDEX(E2:AF2+F2:AG2+G2:AH2+H2:AI2,0))
=SUMPRODUCT(MIN(E2:AF2+F2:AG2+G2:AH2+H2:AI2))
The idea regarding the ranges is the same in both scenarios, with a shift in the start and end of the range by 1 for each additional day.
Another approach getting to the same result:
=LET(range,E2:AI2,
cons,4,
repeat,COLUMNS(range)-cons+1,
MAX(
BYROW(SEQUENCE(repeat,cons,,1)-INT(SEQUENCE(repeat,cons,0,1/cons))*(cons-1),
LAMBDA(x,SUM(INDEX(range,1,x))))))
This avoids OFFSET (volatile, slowing your file down) and the repeat value, consecutive number and/or the range are easily changeable.
Hope it helps (I answered to the max sum, as stated in the title). Change max to min to get the min sum result.
Edit:
I changed the repeat part in the formula to be dynamic (max number of consecutive columns in range), but you can replace it by a number or a cell reference.
The cons part can also be linked to a cell reference.
Also found a big in my formula which is fixed.

How to get smallest result from multi-criteria index/match function

I have a sheet with transactions. Each txn has an airport and an amount of fuel pumped. I have a second sheet with a list of locations, each row of which has min and max values for fuel bands (e.g, 1-500, 501-1000, min/max stored in separate columns), and each each of which have a price (for fuel per gallon).
I need to use the values per row in the first spreadsheet to search the second spreadsheet for a match on airport (ICAO code), greater than bottom of band, and less than top of band, and then return the unit price of fuel. The catch is that I can have multiple matches, and I need the smallest/lowest value.
I have some familiarity with Index/Match multi criteria arrays. So, I wrote the following and tried it:
=INDEX(FuelPrices!$D$2:$D$3398,MATCH(1,(FuelPrices!A:A=H2)*(FuelPrices!B:B>=N2)*(FuelPrices!C:C<=N2),0))
Where "Fuel" is my first sheet and "FuelPrices" is the sheet I'm looking up the values in. No matter WHAT I do it throws an #NA error. So, I figured maybe the problem was I was returning an array? I tried this:
=INDEX(FuelPrices!$D$2:$D$3398,SMALL(MATCH(1,(FuelPrices!A:A=H2)*(FuelPrices!B:B>=N2)*(FuelPrices!C:C<=N2),0),1))
Figuring it would give me the smallest value from the returned array. No go. I've tried some other tricks (using another Index function around the match) and nothing seems to work.
Basically I just want to get the function to return the lowest matching value for the provided criteria.
The short answer is that the < and > are the wrong way round. This does give an answer
=INDEX(FuelPrices!$D$2:$D$10,MATCH(1,(FuelPrices!$A$2:$A$10=H2)*(FuelPrices!$B$2:$B$10<=N2)*(FuelPrices!$C$2:$C$10>=N2),0))
if entered as an array formula using CtrlShiftEnter
I have changed all ranges to a small number of rows for testing purposes.
FuelPrices sheet
Fuel sheet
If you do want to find the smallest subject to the same conditions, you don't need index but can use small (or min)
=SMALL(IF((FuelPrices!$A$2:$A$10=H2)*(FuelPrices!$B$2:$B$10<=N2)*(FuelPrices!$C$2:$C$10>=N2),FuelPrices!$D$2:$D$10),1)
Or if you prefer you can use Aggregate (non-array formula)
=AGGREGATE(15,6,FuelPrices!$D$2:$D$10/((FuelPrices!$A$2:$A$10=H2)*(FuelPrices!$B$2:$B$10<=N2)*(FuelPrices!$C$2:$C$10>=N2)),1)

Replace Excel vlookup with a dynamic formula

I have created a schedule where I am trying to lookup the value of rent for a period of time based on a given date (located in cell B1).
For example, I have the following data set:
Rent Change Date is the date when the rent increases for a specified tenant
Amount is the amount it increases to on the specified Rent Change Date
The schedule off to the right is the monthly rent schedule as dictated by the Rent Change Date
I am currently using a VLOOKUP to identify the range for each tenant using TRUE (or approximate match) to find the rent for the current month (as dictated by the date in B1).
Sample (located in cell G5):
=VLOOKUP(G4, C5:D10, 2, TRUE).
For each tenant, I then reset the table_array range. This works well for a small data set, but I have been searching for a way to set the range automatically. Is there an efficient way to get all of the Rent Change Dates by tenant? Maybe an Excel array formula?
So I'm still not 100% sure I understand what you're looking for, but here is what I came up with.
Using this formula in cell G5 (committing it with Ctrl + Shift + Enter as it's an array formula), and then copying the formula to all other cells in the G5:R7 range, you should get what you're looking for.
Array Formula:
=IF(AND(G$4>=MIN(IF((--($A$5:$A$19=$F5))=0,2,1)*($C$5:$C$19)),G$4<=EOMONTH(MAX(($A$5:$A$19=$F5)*($C$5:$C$19)),11)),INDEX($D$5:$D$19,MATCH($F5&G$4,$A$5:$A$19&$C$5:$C$19,1),1),0)
Example Results:
Result when a tenant is commencing during the year
Result when a tenant is occupied for the whole year
Result when a tenant is expiring during the year
Explanation:
There's a lot going on with this formula, and it's possible there's a simpler way, but I'll do my best to explain. After all, learning isn't just about getting an answer, but also an explanation.
Essentially what you're looking at is a large logical IF function (with three arguments: logical_test, [value_if_true], [value_if_false]). To understand its arguments better, let's break them down:
logical_test
AND(G$4>=MIN(IF((--($A$5:$A$19=$F5))=0,2,1)*($C$5:$C$19)),G$4<=EOMONTH(MAX(($A$5:$A$19=$F5)*($C$5:$C$19)),11))
I started with the AND function because I am attempting to find if a given date (G$4) is either the same as or greater than the first Rent Change Date for a Tenant (this is found using the MIN function) and if the same given date (G$4) is either the same as or less than the end of the month (EOMONTH function) 11 months in the future from the last Rent Change Date for a Tenant.
The embedded IF function within the MIN function is simply there to ensure the correct minimum date is returned. Removing this logical causes the minimum to return 0, which is incorrect for our needs.
I used the EOMONTH function due to the assumption that when the rent changes - including the final rent change - it lasts for a year. Failing to add this piece to the formula would end the rent the same month as the Rent Change Date.
If both statements within the AND function return TRUE, the logical_test then proceeds to the [value_if_true] argument.
[value_if_true]
INDEX($D$5:$D$19,MATCH($F5&G$4,$A$5:$A$19&$C$5:$C$19,1),1)
INDEX(array, row_num, [column_num])
MATCH(lookup_value, lookup_array, [match_type])
COMBINED:
INDEX(array, MATCH(lookup_value, lookup_array, [match_type]), [column_num])
Using the INDEX and MATCH functions together is allowing us to look at the data from any angle without having constraints placed on us. In this case, our array argument - $D$5:$D$19 - spans the entire area of our Amount column. This is where we want to return our value from, so it's important that we cover the full area.
In the place of row_num we use the MATCH function. In our table spanning F4:S8 (which includes the column and row names) we are given both the tenant name ($F5) as well as a reference date (G$4). Combining these with an ampersand (&), we now have a concatenated lookup_value for MATCH. By concatenating these two together, we are increasing the likelihood that our lookup_value is unique and will return us the required information.
If two tenants happen to have the same Rent Change Dates but different names OR if two tenants happen to have the same name but different Rent Change Dates, we will get a unique match; in the rare instance that two tenants share the same name and Rent Change Dates, one tenant will need to have something different to stand out, such as a unit number in the name
Now that we have our lookup_value for MATCH, we need to supply a lookup_array. Given that our lookup_value is a combination of the tenant name ($F5) and a reference date (G$4), we set our lookup_array for MATCH to be a concatenation of $A$5:$A$19 (spans the entire area of our Tenant column) and $C$5:$C$19 (spans the entire area of our Rent Change Date column), joined together with an ampersand (&).
The last argument our MATCH function needs is the [match_type]. For this formula I chose 1 - Less than (Finds the largest value that is less than or equal to lookup_value. The values in the lookup_array argument must be placed in ascending order) due to the fact that we are looking for a date that is either the date itself or one that is less than it as well as the fact that our dates are in ascending order for each Tenant. If we instead looked for an exact match ([match_type] set to 0 - Exact match), we would receive a lot of errors since the Rent Change Dates increase annually, not monthly (like our reference dates in G4:R4). Similarly, looking for a greater than match ([match_type] set to -1 - Greater than) also returns a lot of errors, primarily because the dates aren't in the required descending order.
Closing out of the MATCH function with a ), we return to the [column_num] of the INDEX function from earlier. While this argument is optional with a one-column array, I entered a 1 for clarity. All this means is that once the MATCH function determines which row to grab, I want to get the intersection of [row_num] (which is 1 in the case of G5 using the presented formula along with 2/1/2018 as the date in B1) and [column_num] (1) from the array (the Amount column, $D$5:$D$19). Row 1, Column 1 from the array is $150.00, which is exactly the expected result.
The last piece of our formula is to finish off the IF statement we started with, entering a value for the [value_if_false] argument.
[value_if_false]
0
In the case of this IF function, we simply entered 0 for the [value_if_false] argument. I chose 0 because if the tenant hasn't yet commenced or has expired, we want to reflect a total rent of $0.00 for a given month.
Hopefully this all makes sense and is what you're looking for.
If you create a pivot table based on your data table it can automatically put all the dates across the top row (and you can group by quarter, months, years, days if you so desire).
If you want to search for months, but only have rent rates for years in your source data you may need to use sumproduct. (A pivot table will only include dates where they exist in the source data that I'm aware of, it won't add all dates covered by a range unless explicitly included in that range)
It would like something like below;
=Sumproduct($c$5:$c$300,--($a$5:$a$300=$a2),--($b5:$b$300>b$1),--($b5:$b$300<c$1))
Assumption here is the grid showing data across the top starts from cell a1, and the source data starts from a5.
Because sumproduct effectively is an array formula (you don't need to use Ctrl + shift + enter, it just behaves the same way as an array formula) it's generally not recommended to apply it to a full column for performance reasons.

EXCEL calculating average with OFFSET (coord unspecified) of a given keyword

Fun situation I'm trying to calculate. Basically in one row I have names of products, and the row to the right of it the number of days that have passed since the product was first received.
The calculation to do the days for ex is
=TODAY()-BB2
What I'm trying to do NOW, is identify let's say the product word "truck," and then calculate how many days on average the holding time of truck is.
I understand the logic of the formula I need, just not how to execute precisely. Basically the formula is going to need to use this average calculator with a keyword identified COUNTIF
=COUNTIF($A$2:$A$900,"TRUCK")/COUNTA($A$2:$A$900)
What I'm missing is some type of...IF "TRUCK, OFFSET (GIVEN CELL) -1)
Thanks for any thoughts!
-Wilson
A formula (in C1 in the example):
=AVERAGEIF(A:A,"truck",B:B)
should work but I would recommend a PivotTable for the additional functionality it provides:

excel count/sum stop count/sum match?

I have tried to see if this question has been asked before, but I can't seem to find an answer.
I have a column of cells (>3000 rows), with either a value of 1 or 0 (call this column A). The value will depend on the value in column B (which will contain either a value or nothing). The values in column B are a SUMIFS function based, summing from column C, and based on months in column D.
The values in B are paid out on the first business day of the next month. So, the SUMIFS function will calculate the dates that match the last month. This works well in theory, however, not every first business day is the first day of the month. This leads the SUMIFS function to not include everything in the correct month, and allows for some discrepancy, which, when you are dealing with people's money is not great. Further, this discrepancy is compounded across multiple periods (in some cases, there are over 100 periods, and a discrepancy of $1 in period 1 amounts to nearly $1000 in period 100)
What I am wondering is:
Is there any way that I can tell the SUMIFS function (column B) to stop when the value in column A is 0? This would tell the SUM function start the summing from the current value in column B and continue the function to the cell below the preceding value in column B.
I've seen suggestions that the MATCH function may work, but I can't see how to do this with either COUNT or SUM.
For security reasons, this solution needs to be entered into the cell, and can't be VBA. Also, it can't be too large, as it will need to be replicated across 200 worksheets in the workbook (not my file originally, and I would have done it differently, but that is another story). There is no problem entering another column or two if that is required.
Any help is gratefully appreciated.
EDIT:
Unfortunately, I can't post an image of the screenshot. I've included a similar screenshot (columns are not the same layout, but hopefully it gives the idea) here:
Rates calculations
The SUMIF formula is (for B2)
=SUMIFS(C2:C35,D2:D35,D2-1,A2:A35,1)
This works fine if I want all the values in the month, irrelevant of when the payment was made.
However, what I need the formula to do is:
SUM (C2:C35,D2:D35,D2-1, but stop when the first 0 is encountered in A2:A35)
Thanks
The INDEX function can provide a valid cell reference to stop using a MATCH function to find an exact match on 0.
This formula is a bit of a guess as there was no sample data to reference but I believe I have understood your parameters.
=SUMIFS(C2:index(C2:C35, match(0, A2:A35, 0)), D2:index(D2:D35, match(0, A2:A35, 0)), D2-1)
This seems to be something that will stand-alone and not be filled down so I have left the cell addresses relative as per your sample(s).

Resources