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.
Related
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]] ) )
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.
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!
I have done an average price calculation for apples. See example
In the new column "testar", I only want to show the year 1990 - 1994 (yellow cells), since the other years are not specified in my formula.
Formula I used for average calculation was:
=CALCULATE (
AVERAGEX (Datasrc; Datasrc[C_P2] );
DATESBETWEEN(Datasrc[Year];"1990-01-01";"1994-01-01")
)
Any ideas or advice how to do that?
You'll need to test the values of Datasrc[Year] in an IF().
Here's a sample using a dummy dataset I have.
Testar =
IF(
MAX( DimDate[Year] ) > 2010
&& MAX( DimDate[Year] ) < 2014
,[SumAmt]
)
We're testing MAX() DimDate[Year]. On any given pivot row, only one year is in context, so max is the year on that pivot row.
The measure you used calculates the average in the context of the 5 years you've defined, overriding whatever the current filter context is.
Additionally, AVERAGEX() is unnecessary in this situation; you can use AVERAGE( Datasrc[C_P2] ).
Almost, thank you for your valuable input and put me in the right direction!!!, since my format of the year table is different and I want to have the average price listed I did a little different. I simply combined your formula with my average formula and got the result I wanted.
See final result here
Code used:
=IF(
MAX( Datasrc[Year2] ) >= 1990
&& MAX( Datasrc[Year2] ) <= 1994
;
CALCULATE (
AVERAGEX (Datasrc; Datasrc[C_P2] );
DATESBETWEEN(Datasrc[Year];"1990-01-01";"1994-01-01")
)
)
The first part of the formula, takes out only the years I'm interested in.
The second part of the formula [CALCULATE(AVERAGEX....] calculates the average of the years I'm interested of.
I'm just putting together a simple spreadsheet that calculates tax owed based on a few different bands. I have an 'Invoiced Amount' cell that takes up the yearly invoicing and then applies a tax rate to it based on the following conditions:
If 'Invoiced Amount' is below 10, 600 then Tax Owed = 0
If 'Invoiced Amount' is above 10, 600 but less than 42, 386 then Tax Owed = ((Invoiced Amount - Tax Allowance)/100) * 20
If 'Invoiced Amount' is Equal to or greater than 42, 386 than Tax Owed = ((42, 386 - Tax Allowance)*20)+((InvoicedAmount - 42, 386)*40)
I could be overlooking something really basic here, but just to be sure - Tax Allowance is 10, 600 - Anything over this up to 42, 386 is worked out at 20% tax, and then anything earn above 42, 386 is charged # 40% on top...
The more I type this out the more confused I am. Anyway, Here is my excel formula:
InvoicedTotal = P5
TaxAllowance (10600) ='UK Tax Figures'!C3
TaxAllowanceUpperBand (42386) ='UK Tax Figures'!G5
TaxAllowance Upper Band - Tax Allowance (31784) = H5
UpperTaxAllownace Band (42386.01) = ='UK Tax Figures'!F5
=IF ((P5)<’UK Tax Figures’!C3, 0, IF(P5>=’UK Tax Figures’!C3<'UK Tax Figures'!G5, ((P5-'UK Tax Figures'!C3)/100)*20, IF(P5>='UK Tax Figures'!F5, ((H5/100)*20)+((P5-'UK Tax Figures'!F5)*40))
At the moment I'm getting crazy unexpected values back, so the calculation is obviously VERY wrong... But I can't see the wood for the trees at the moment, so if anyone has any thoughts I would really appreciate it! Going a little crazy here at the moment!
Putting your formula into the Online Excel Formula beautifier I notice several problems with your formula:
=IF ( ( P5 ) < ’UK Tax Figures’!C3 , 0 ,
IF(
P5 >= ’UK Tax Figures’!C3 < UKTaxFigures!G5,
( ( P5 - UKTaxFigures!C3 ) / 100 ) * 20,
IF(
P5 >= UKTaxFigures!F5,
( ( H5 / 100 ) * 20 ) + ( ( P5 - UKTaxFigures!F5 ) * 40 )
)
First of all, you lack two closing parantheses
Furthermore, you have an invalid conditional in P5>=’UK Tax Figures’!C3<'UK Tax Figures'!G5, you need to change this to AND(P5>=UKTaxFigures!C3;UKTaxFigures!C3<UKTaxFigures!G5).
Taking a look at how you reference your worksheets above, I notice that it seems you have two different sheets - one with spaces between the words in the sheet names, and one without. I suspect this is not the case, so you should probably remove those spaces from the formula.
You are also inconsistent with whether or not you use apostrophes (’) around your sheet-names. Trying out your formula in a cell, Excel didn't seem to like them, so they should probably go as well.
there seems to be a return-value missing from your innermost if-statement if it is false.
Guessing a bit at what you want the formula to return, I end up with this formula:
=IF(P5<UKTaxFigures!C3;0;IF(AND(P5>=UKTaxFigures!C3;UKTaxFigures!C3<UKTaxFigures!G5);((P5-UKTaxFigures!C3)/100)*20;IF(P5>=UKTaxFigures!F5;((H5/100)*20)+((P5-UKTaxFigures!F5)*40);0)))
Which looks like this in the beautifier:
=IF(
P5 < UKTaxFigures!C3;
0;
IF(
AND(
P5 >= UKTaxFigures!C3;
UKTaxFigures!C3 < UKTaxFigures!G5
);
( ( P5 - UKTaxFigures!C3 ) / 100 ) * 20;
IF(
P5 >= UKTaxFigures!F5;
( ( H5 / 100 ) * 20 ) + ( ( P5 - UKTaxFigures!F5 ) * 40 );
0
)
)
)
Is that something close to what you want?
As a final word of advice it is an absolute pain to write a formula like that - instead you can try to build it bit by bit, storing each part of the formula in one cell to see if each of them works. Then you can cut and paste so that they all fit in one cell. I.e. if you in cell A1 store:
=IF(P5>=UKTaxFigures!F5;((H5/100)*20)+((P5-UKTaxFigures!F5)*40);0)
Then you can put
=IF(AND(P5>=UKTaxFigures!C3;UKTaxFigures!C3<UKTaxFigures!G5);((P5-UKTaxFigures!C3)/100)*20;A1)
in A2 and this
=IF(P5<UKTaxFigures!C3;0;A2)
in A3. If necessary you can pick the formulas even further apart to make them more readable. Then in the end you just copy the formulas from the cells, and replace the references with them.
Please note that I am not 100 % certain that I got everything correct, without proper data it is somewhat difficult to keep track of all the parantheses and results. But this should at least give you a good starting point.