Excel formula to sum an array based on multiple criteria of arrays -- Fantasy Football - excel-formula

I have 2 tables and need to use criteria from table1 (Team) to look up information from Schedule2 (Team's Schedule for the year) and then use that array from Schedule2 to find the matches in table1 (opponents) and pair that with the criteria of matching the position within table 1 (position = position) to then sum up the associated numbers that match (2021 opp position points) to calculate the expected total points for the year.
In English: use the team to look up the schedule, use the schedule to look up the array of opponents the team has for the year, use the array of opponents for the year to find the opponents in table1, and then match the position of the current row to the position that is playing each of those opponents, and then sum up the associated points.
Goal: This is to calculate a forecast of each team's position's total points for the year based on how many points their opponents for the year are currently giving away to each position.
Explained Example: So if Dallas WR plays NYG, MIA, WAS, and MIA again, sum up the total points NYG, MIA, WAS, and MIA are currently giving to WRs they are currently play against.
FYI WR = Wide Receiver RB = Running Back, TE = Tight End D/ST = Defense string, QB = Quarterback
I can't use helper cells and need a single formula. Schedule2 will give duplicate values (Some teams play the same team twice and each of those values need to be calculated and not considered unique and not counted just once.
The current formula I have is below but it only seemed to work for one row and didnt work when I drug it down to the row below. could be a referencing issue but I didnt notice anything that would cause this.
The first part of the formula is meant to get the total for the non-duplicated values and the second is meant to get the duplicated values.
= SUM( IF( ISERROR( MATCH( Table1[[#All],[Position]],G14,0)
* MATCH(Table1[[#All],[Opponent]],INDEX(Schedule2,0,MATCH(Table1[#Team],Schedules!$AU$3:$CA$3,0)),0) ),
0,
Table1[[#All],[2021 Opp Position]]))
+ SUM( IF( ISERROR( MATCH( Table1[[#All],[Opponent]],
IF( COUNTIF( INDEX( Schedule2, 0, MATCH(Table1[#Team],Schedules!$AU$3:$CA$3,0) ), INDEX(Schedule2,0,MATCH(Table1[#Team],Schedules!$AU$3:$CA$3,0)))=2,
INDEX(Schedule2,0,MATCH(Table1[#Team],Schedules!$AU$3:$CA$3,0)),
""), 0 )
* MATCH(Table1[[#All],[Position]],G14,0)
* MATCH(Table1[[#All],[Opponent]],INDEX( Schedule2,
0,
MATCH(Table1[#Team],Schedules!$AU$3:$CA$3,0) ),0) ),
0,
Table1[[#All],[2021 Opp Position]] ) )

Related

Sumifs with date difference in range

I have a spreadsheet with:
list of recurrent expenses
other lists of recurring stuff, not important
an estimation of inflow and outflow monthly
The estimation is calculated with the following criteria (in AND logic):
date is between "since" and "to" (if since is empty, it is always valid, and the same for "to")
the month is a multiple of "every month" starting from "since", i.e. the voice must be included if "data" = "since" + "every months" * x, where x is an integer number
I wanted to create a sumifs like the following (Italian Excel, sorry):
=SOMMA.PIÙ.SE('Uscite'!$E$2:$E$1048576;'Uscite'!$C$2:$C$1048576;"<="&'v3'!$A2;'Uscite'!$D$2:$D$1048576;">="&'v3'!$A2;?????)
where ????? is the missing piece: I considered using a DATEDIF with "m", in order to get the difference in months between cell Ax and 'Uscite'!Since, then verifying if this difference is a perfect multiple of "every months", but no clue how to do it.
Any suggestion?
Thanks for your time!
In english formulation, you could do:
=SUM( 'Entrate ricorrenti'!$E$2:$E$100 *
( ('Entrate ricorrenti'!$D$2:$D$100 >= 'v3'!A2) + ISBLANK('Entrate ricorrenti'!$D$2:$D$100) ) *
IFERROR( MOD( DATEDIF( 'Entrate ricorrenti'!$C$2:$C$100, 'v3'!A2, "M" ), 'Entrate ricorrenti'!$B$2:$B$100 ) = 0,
0 ) )
In Italian:
=SOMMA( 'Entrate ricorrenti'!$E$2:$E$100 *
( ('Entrate ricorrenti'!$D$2:$D$100 >= 'v3'!A2) + VAL.VUOTO('Entrate ricorrenti'!$D$2:$D$100) ) *
SE.ERRORE( RESTO( DATA.DIFF( 'Entrate ricorrenti'!$C$2:$C$100; 'v3'!A2; "M" ); 'Entrate ricorrenti'!$B$2:$B$100 ) = 0;
0 ) )
So, this uses the very nice dinosaur function that you taught me about today. I have never seen this except for DAX.
It simply filters out the payments that stopped before the target date, ('v3'!A2) by multiplying the payment values, ('Entrate ricorrenti'!$E$2:$E$100), times the boolean expression
(('Entrate ricorrenti'!$D$2:$D$100 >= 'v3'!A2) + ISBLANK('Entrate ricorrenti'!$D$2:$D$100))
that also checks to see if the To Date is blank. Then it multiplies that times the modulo of the DATEDIF in months by the Frequency in months. If that is zero, then a payment lands in that month. DATEDIF has the nice property that if the date difference is negative, it generates an N/A error. This allows us to wrap it in an IFERROR to replace that with zero - this prevents the formula from including payments that have not yet started.

calculating Payment of the Principal (PPMT) starting with a partial month repayment in Excel or VBA

I am trying to calculate the loan repayment schedule for a loan. however the process to get (and repay) a loan could look like this.
some details of loan:
{loanValue=10000, interest=0.1, termMonths=12};
Apply for loan on 6 jan -- but i specify I would like to repay End of Month.
Get funds on the 15th (effectively starting the loan)
I repay my first repayment on the 28th of Jan ( this is the specified EoM date)
I can easily work out the:
Repayment amount:
-PMT((1+0.1)^(1/12)-1,12,10000) $=877.16
Capital portion:
-PPMT((1+$B$2)^(1/12)-1,1,12,10000) = $797.41
Interest portion``:
-IPMT((1+$B$2)^(1/12)-1,1,12,10000) = $79.74
However, I am not sure how to adjust the first months repayment amount ( and effective calculations) to support that initial partial month?
The following is how far i got on a generic function for this:
Private Function createRepaymentSchedule(TotAmnt, anIntRate, nPER, startDate, processDate)
Dim rs As DAO.Recordset
Dim RT As Double
Dim RES As Double
RT = (1 + anIntRate) ^ (1 / 12) - 1
Dim Sql As String
Pval = TotAmnt
For i = 1 To nPER
cap = -PPmt(RT, i, nPER, TotAmnt)
intr = -IPmt(RT, i, nPER, TotAmnt)
Pval = Pval - (cap)
Debug.Print (i & "~" & Round(cap, 2) & "~" & Round(intr, 2) & "~" & Round(Pval, 2))
Next i
A lot of this depends on the loan terms and conditions. The answer below is based on some assumptions (that I would rather verify by comment in advance):
Continuous compounding
Payment on last working day of month after the first payment is treated the same as payment on last day of month
The Process Date is not used in any calculations, so I cannot incorporate it.
The Start Date is the effective loan origination date.
The loan termination date is the last day of the month nPer months after the Start Date
Normally PMT, PPMT, and IPMT are based on another assumption, which is almost never true in reality : payments are uniform. So each period is exactly the same duration, down to the second. The fact that one month is shorter than another and the real payment dates are on working days is usually not a an important factor, so PMT does a "good enough" approximation. However, your first payment date occurring in only half of a period, will make a material difference - especially to the PPMT and IPMT, so your point is correct. These cannot be used.
At first, I attempted a financial trick to answer the question: roll back the loan date to the start of the month and set the loan amount = the PV of the loan as of the real start date (-15 days). That works financially, but it creates a problem in the schedule of Principle, Interest and Capital. Rather than tweak it, I simply did what you proposed: build a new PPMT that is not only based on your start date, but also incorporating your "last weekday of month" requirement. The result is something that would match up to banking standards.
This could be built in VBA as you proposed, but I decided to stick with pure Excel as I am finding that lately the LET is an underutilized approach and solving it with LET would have its own merits. So, this solution is based the Excel LET function and it produces a Dynamic Array that will spill onto the worksheet. It does require Excel 2016 or Microsoft 365. If that is a problem, tell me in comments and I can convert this to VBA (but with some really different methods).
Set Up
I put your key variables (TotAmt, anIntRate, nPer, startDate, and processDate) into cells B1 through B5. NB: processDate is not used. The results are placed in A8, with headers in A7:F7 to label each series of values.
To get started, I created two helper ranges that are also part of the output: Item and Date. I could have incorporated these into the formula, but I think it is better to expose them so that you can see the schedule.
Item is simple a vertical sequence =SEQUENCE( B3 + 1,,0 ). Date is a dynamic array based on a formula that computes the last working day of the month:
=IF( A8#, WORKDAY( EOMONTH( startDate, A8# - 1 ) + 1, -1 ),
startDate )
NB: your last weekday is 28 Jan. You might be in a SUN-THU country.
There is another way to compute last weekday that is based on WEEKDAY
that would allow you to shift it to your country's schedule. If you
need that, ask for it in the comments.
The Formula
I put the following formula into C8:
=LET( loanAmt, B1,
anInt, B2,
nP, B3,
startD, B4,
pmtDates, B8#,
v, SEQUENCE( nP+1,,0 ), h, TRANSPOSE( v ),
pp, SIGN( h ),
ones, SIGN( TRANSPOSE( pp + 1 ) ),
stagr, (v - h + 1) * (v >= h),
dlyInt, ( 1 + anInt ) ^ (1/365 ) - 1,
fvFactors, ( 1 + dlyInt ) ^ ( pmtDates - startD ),
fvarray, INDEX( fvFactors, stagr, ) * SIGN( stagr ),
guess1, PMT( ( 1 + anInt ) ^ (1/12 ) - 1, nP, loanAmt ),
rseq1, MMULT( fvarray * ( loanAmt*(1-pp) + guess1*pp), ones), guess2, guess1 * ( 1 + INDEX( rseq1, 13 )/loanAmt ),
rseq2, MMULT( fvarray * (loanAmt*(1-pp) + guess2*pp), ones ), guess3, guess2 * ( 1 + INDEX( rseq2, 13 )/loanAmt ),
rseq3, MMULT( fvarray * (loanAmt*(1-pp) + guess3*pp), ones ), guess4, ROUND( guess3 * ( 1 + INDEX( rseq3, 13 )/loanAmt ), 2 ),
rseq4, MMULT( fvarray * (loanAmt*(1-pp) + guess4*pp), ones ),
prin, INDEX( rseq4, v+1, ) - INDEX( rseq4, v, ),
i, (TRANSPOSE(loanAmt*(1-pp) + guess4*pp) - prin) * --( v +1 > 1 ),
cflows, TRANSPOSE(loanAmt*(1-pp) + guess4*pp),
tap, IFERROR( INDEX( cflows, v+1, SEQUENCE( 1, 2 ) ), prin ),
tapi, IFERROR( INDEX( tap, v+1, SEQUENCE( 1, 3 ) ), i ),
tapic, IFERROR( INDEX( tapi, v+1, SEQUENCE( 1, 4 ) ), rseq4 ),
tapic )
How it works
I'm a bit pressed at the moment, so I cannot do a detailed explanation right now, so I will give a high level view:
The input variables are straight forward - as mentioned, the Dates are a helper column, so pmDates are the range of payment dates that were created by the last-weekday-of-month range mentioned above.
Array Shapers are just sequences of indexes and matrices that will be used in the shaping and calculation of the financials. They are used next to create a set of financial arrays for future value factors that will be used to create all of the financial outputs.
Payment Convergence is an iterative approach to creating the adjusted real payment amount (i.e. a replacement for PMT) that will truly balance the loan if paid according to the pmtDates that were input. This uses iterations that start with a guess and then adjusts the guess in each iteration. It converges faster then Newton-Raphson and is probably over-engineered for small loan/interest values, but can reliably scale to 7 figure loans and double digit interest rates. The final output is rseq4 which is the remaining Capital schedule that you asked for.
Prin (PPMT), i (IPMT), and cflows (payments) are calculated into columns in Results Columns. Result Table Construction then appends these by using nested over-indexing of the columns to form a single table called tapic (table annuities prin interest capital) which is the final result.
If it is valuable, I can expand on the methods and give more detail about the financials - just ask in comments, but gotta go for now.

Why does the DAX formula in my calculated column use propagation to filter in one instance and not in another?

Suppose I have a couple of tables:
fTransactions
Index ProdID RepID Revenue
1 1 1 10
2 1 1 20
3 2 2 30
4 2 2 10
dSalesReps
RepID RepName CC1 CCC2
1 joe 40 70
2 sue 30 70
3 bob 70
CC1 contains a calculated column with:
CALCULATE(SUM(fTransactions[Revenue]))
It's my understanding that it's taking the row context and changing to filter context to filter the fTransaction table down to the RepID and summing. Makes sense per an sqlbi article on the subject:
"because the filter context containing the current product is automatically propagated to sales due to the relationship between the two tables"
CC2 contains a calculated column with:
SUMX(fTransactions, CALCULATE(SUM(fTransactions[Revenue]))
However, this one puts the same value in all the columns and doesn't seem to propagate the RepID like the other example. The same sqlbi article mentions that a filter is made on the entire fTransactions row. My question is why does it do that here and not the other example, and what happened to the propagation of RepID?
"CALCULATE places a filter on all the columns of the table to identify a single row, not on its row number"
A calculated column is created in a loop: power pivot goes row by row and calculates the results. CALCULATE converts each row into a filter context (context transition).
In the second formula, however, you have 2 loops, not one:
First, it loops dSalesReps table (because that's where you are creating the column);
Second, it loops fTransactions table, because you are using SUMX function, which is an iterator.
CALCULATE function is used only in the second loop, forcing context transition for each row in fTransactions table. But there is no CALCULATE that can force context transition for the rows in the dSalesReps. Hence, there is no filtering by Sale Reps.
Fixing the problem is easy: just wrap the second formula in CALCULATE. Better yet, drop the second CALCULATE - it's not necessary and makes the formula slow:
CCC2 =
CALCULATE(
SUMX(fTransactions, SUM(fTransactions[Revenue]))
)
This formula is essentially identical to the first one (the first formula in the background translates to the second one, SUM function is just a syntax sugar for SUMX).
You could also write the formula as:
CC2 = SUMX( RELATEDTABLE( fTransactions ), fTransactions[Revenue] )
or
CC2 = SUMX( CALCULATETABLE( fTransactions ), fTransactions[Revenue] )
The key is that fTransactions as the first argument of SUMX needs to be filtered for each SalesRep (i.e. on the current row). Without the filter then you are just iterating the entire fTransactions table for each SalesRep. Somehow SUMX needs to know you just want the fTransactions for the SalesRep whose revenue you are trying to compute.

How to combine the index function with multiple criteria?

I am having trouble figuring out an index function. My datasheet is as follows
Tab 1 - complete list of all subcompanies held by all holding companies
- The first column shows holding companies
- the second column shows the subcompanies held by each holding company
- the third, fourth and fifth column give the key financials of these subcompanies (EBITDA)
The tabs after Tab 1 are tabs for every holding company. In these tabs I have a section where I want to only include the subcompanies from Tab 1 with an EBITDA higher than 20 million
Example:
Holding company A has the following subcompanies: Sub1 (EBITDA:10m), Sub2 (EBITDA: 21m), Sub3 (EBITDA: 15m).
In the tab of company A I want to only display the information of Sub2 as it meets the minimum threshold
The function I have now displays all information of each subcompany per holding company regardless of their EBITDA:
=IFERROR(INDEX(Tab1!D$5:D$337,SMALL(IF(Tab1!$C$5:$C$337=$C$6,ROW(Tab1!D$5:D$337)-MIN(ROW(Tab1!D$5:D$337))+1),ROWS(B$55:B55))),"")
Tab1 Column D has all the subcompany names
Tab1 Column C has all holding company names
Column B is where I insert the subcompanies in the holding company tab
The output provides all subcompanies per holding, but I only want the subcompanies per holding with an EBITDA equal or larger than 20m
How can I add a criteria to filter if one or more of the 3 years worth of EBITDAs (Tab1 Column N, O & P) per subcompany is equal or larger than 20m?
Many thanks in advance!
Change the criteria in the IF statement from:
Tab1!$C$5:$C$337=$C$6
to also check columns N, O or P being over 20m:
(Tab1!$C$5:$C$337=$C$6)*((Tab1!$N$5:$N$337>20000000)+(Tab1!$O$5:$O$337>20000000)+(Tab1!$P$5:$P$337>20000000))
You are using a FormulaArray, however, you could also use this standard formula (entered at B6):
= IFERROR( INDEX( 'Tab1'!$D$5:$D$337,
AGGREGATE( 15, 6, ROW(B:B) /
( ( 'Tab1'!$C$5:$C$337 = $C$6 ) *
( ( 'Tab1'!$N$5:$N$337 > 20 ) + ( 'Tab1'!$O$5:$O$337 > 20 ) + ( 'Tab1'!$P$5:$P$337 > 20 ) <> 0 ) ),
ROWS( B$5:B5 ) ) ), "" )

Moving average excluding weekends and holidays

I have a table within PowerPivot currently that tracks a count of customers through our sales pipeline. From (by sales location) first interaction to charged sale. So far, I’ve creates a moving 5-day average that averages each task. Below is the DAX formula I’ve created thus far and an example table.
=
CALCULATE (
SUM ( [Daily Count] ),
DATESINPERIOD ( Table1[Date], LASTDATE ( Table1[Date] ), -7, DAY ),
ALLEXCEPT ( Table1, Table1[Sales Location], Table1[Group] )
)
/ 5
Where I’m struggling is being able to come up with a way to exclude weekends and company observed holidays. Additionally, if a holiday falls on a weekday I would like to remove that from the average and go back an additional day (to smooth the trend).
For example, on 11/26/18 (the Monday after Thanksgiving and Black Friday) I would like to average the five business days previous (11/26/18, 11/21-11/19, and 11/16). In the example above, the moving total and average for the previous 5 days should be Intake = 41 (total) 8.2 (average), Appointment = 30 (total) 6 (average), and Sale = 13 (total) and 2.6 (average).
Based on the formula currently, each of these numbers is inaccurate. Is there an easy way to exclude these days?
Side note: I’ve created an ancillary table with all holidays that is related to the sales data that I have.
Thank you for the help!
For this, I'd recommend using a calendar table related to Table1 on the Date column that also has a column IsWorkday with 1 if that day is a workday and 0 otherwise.
Once you have that set up, you can write a measure like this:
Moving Avg =
VAR Last5Workdays =
SELECTCOLUMNS (
TOPN (
5,
FILTER (
DateTable,
DateTable[Date] <= EARLIER ( Table1[Date] )
&& DateTable[IsWorkday] = 1
),
DateTable[Date], DESC
),
"Workday", DateTable[Date]
)
RETURN
CALCULATE (
SUM ( Table1[Daily Count] ),
Table1[Date] IN Last5Workdays
ALLEXCEPT ( Table1, Table1[Sales Location], Table1[Group] ),
)
/ 5
The TOPN function here returns the top 5 rows of the DateTable where each row must be a workday that is less than or equal to the date in your current Table1 row (the EARLIER function refers to the earlier row context that defines the current row).
I then use SELECTCOLUMNS to turn this table into a list by selecting a single column (which I've named Workday). From there, it's basically your measure with the date filter changed a bit.
#alexisolson Thank you for the response here. I was actually able to figure this out over the weekend but forgot to close out the thread (sorry about that! Appreciate your help either way). But I did something fairly similar to what you mentioned above.
I created a date table (CorpCalendar) that was only inclusive of working days. Then I created an index column within the CorpCalendar table to give each row a unique number in ascending order. From there, I linked the CorpCalendar table to my SalesData table by related dates and used the LOOKUPVALUE function to bring the index value over from the CorpCalendar table to the SalesData table. In a separate column I subtracted 4 from the date index value to get an index adjustment column (for a range of five days from the actual date index and the adjustment...if that makes sense). I then added an additional LOOKUPVALUE helper column to match the adjusted date index column to the appropriate working day.Lastly, I then used the following function to get the 5 day rolling average.
=CALCULATE(sum(Combined[Daily Count]),DATESBETWEEN(Combined[Date - Adjusted],Combined[Date - Adjusted (-5)],Combined[Date - Adjusted]),ALLEXCEPT(Combined,Combined[Group]))/5
This is probably more convoluted than necessary, however, it got me to the answer I was looking for. Let me know if this makes sense and if you have any suggestions for future scenarios like this.
Thanks again!

Resources