I have the following three tables (table header are on line 01):
Table1: Subscriptions based on dates
| A | B | C | D |
---|-----------|--------------|------------|---------------------|
01 | ID | Type | Start Date | End Date |
02 | ID1 | T1 | 17.4.1 | |
03 | ID2 | T2 | 17.9.1 | 18.5.15 |
04 | ID3 | T1 | 20.6.1 | |
...
Table2: Monthly cost per subscription type
| A | B
---|-------------|---------------
01 | Type | Yearly Cost
02 | T1 | 120
03 | T2 | 144
...
Table3: Expenses Projections
| A | B
---|-----------|------------
01 | Year | Expense s
02 | 2017 | 138 (T1 9 months: 90 + T2 4 months: 48)
03 | 2018 | 180 (T1 12 months: 120 + T2 5 months 60)
04 | 2019 | 120 (T1 12 months)
05 | 2020 | 190 (T1 12 months + 7 months)
06 | 2021 | 1240 (T1 24 months)
...
I would like to compute the column B of the third table automatically (without using helper columns, currently it is done using a helper table...)
I know that it cannot be done using sumif(), but is it possible to achieve this using an array formula ?
I have the following pseudo code of the formula to place in table 3:
for each line of table 2
'
'
' case 1: start date in year, end date not in year
'
if and(table3[year]=year(table1[startdate]), or(isblank(year(table1[enddate]),table3[year] < year(table1[enddate]))) then
'
' count months from startdate to end of year then multiply with monthlyrate
'
addtosum (12-month(table1[startdate]))*index(Table2,match(Table1[#[Type]],Table2[Type],0),2)
'
'
' case 2: startdate in year, enddate in year
'
elseif and(table3[year]=year(table1[startdate]),table3[year] = year(table1[enddate])) then
'
' count months between startdate and enddate then multiply with monthlyrate
'
addtosum (month(table1[enddate])-month(table1[startdate]))*index(Table2,match(Table1[#[Type]],Table2[Type],0),2)
'
'
' case 3: year larger than startdate, enddate is not reached
'
elseif and(table3[year]>year(table1[startdate]), or(isblank(year(table1[enddate]),table3[year]<year(table1[enddate]))) then
'
' multiply 12 months with monthlyrate
'
addtosum 12*index(Table2,match(Table1[#[Type]],Table2[Type],0),2)
'
'
' case 4: year larger than startdate, enddate is reached
'
elseif and(table3[year]>year(table1[startdate]),table3[year]=year(table1[enddate])) then
'
' count months from begining of year to enddate then multiply with monthlyrate
'
addtosum (month(table1[enddate])) * index(Table2,match(Table1[#[Type]],Table2[Type],0),2)
end if
end for each
Here is the helper table I created:
Table3: Expenses Projections
| J | K | L | M | N ...
---|-------|----|------|-----| ----- ...
01 | Year | | ID1 | ID2 | ID3
02 | 2017 | | 90 | 48 |
03 | 2018 | | 120 | 60 |
04 | 2019 | | 120 | 0 |
05 | 2020 | | 190 | |
...
And this is the formula I currently have in cell L2 (this is copied to L2:U5):
' Note: when calling index, column 4 is the Type, 5 is startdate, 6 is enddate)
=IF(NOT(ISBLANK(INDEX(Table1,MATCH(L$1,Table1[ID],0),5))),
IF(AND($J2 = YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),5)), OR(ISBLANK(INDEX(Table1,MATCH(L$1,Table1[ID],0),6)),$J2 < YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),6)))),
(12+1-MONTH(INDEX(Table1,MATCH(L$1,Table1[ID],0),5))),
IF(AND($J2 = YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),5)), $J2 = YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),6))),
(MONTH(INDEX(Table1,MATCH(L$1,Table1[ID],0),6))-MONTH(INDEX(Table1,MATCH(L$1,Table1[ID],0),5))),
IF(AND($J2 > YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),5)), OR(ISBLANK(INDEX(Table1,MATCH(L$1,Table1[ID],0),6)),$J2 < YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),6)))),
12,
IF(AND($J2 > YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),5)), $J2 = YEAR(INDEX(Table1,MATCH(L$1,Table1[ID],0),6))),
(MONTH(INDEX(Table1,MATCH(L$1,Table1[ID],0),6))),
0
)
)
)
)*INDEX(Table2,MATCH(INDEX(Table1,MATCH(L$1,Table1[ID],0),4),Table2[Type],0),2)/12,
0
)
Thanks in advance for your help,
You asked for a formula. Use this array formula:
=SUMPRODUCT(((J2>=YEAR($C$2:$C$4))*((J2<=YEAR($D$2:$D$4))+($D$2:$D$4=""))),
SUMIFS($G$2:$G$3,$F$2:$F$3,$B$2:$B$4),
((DATEDIF(IF($C$2:$C$4>DATE(J2,12,31),0,$C$2:$C$4),DATE(J2,12,31),"m")+1>12)*12-IF($D$2:$D$4<>"",MONTH(D1:D3)+2,0))+
(DATEDIF(IF($C$2:$C$4>DATE(J2,12,31),0,$C$2:$C$4),DATE(J2,12,31),"m")+1<=12)*
((DATEDIF(IF($C$2:$C$4>DATE(J2,12,31),0,$C$2:$C$4),DATE(J2,12,31),"m")+1)--IF($D$2:$D$4<>"",MONTH(D1:D3)+2,0)))
Being an array formula it must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
Here's another solution:
=SUM(IF(ISBLANK(Table1[End Date]),IF(YEAR(Table1[Start Date])<Table3[#Year],12,IF(YEAR(Table1[Start Date])=Table3[#Year],13-MONTH(Table1[Start Date]),0)),IF(Table3[#Year]<YEAR(Table1[Start Date]),0,IF(Table3[#Year]=YEAR(Table1[Start Date]),IF(Table3[#Year]<YEAR(Table1[End Date]),13-MONTH(Table1[Start Date]),IF(Table3[#Year]=YEAR(Table1[End Date]),MONTH(Table1[End Date])-MONTH(Table1[Start Date])+1,0)),IF(Table3[#Year]<YEAR(Table1[End Date]),12,IF(Table3[#Year]=YEAR(Table1[End Date]),MONTH(Table1[End Date]),0)))))*SUMIFS(Table2[Monthly Cost],Table2[Type],Table1[Type]))
This is an array formula CTRL-SHIFT-ENTER.
Hope that helps
This is an example that illustrates my problem
So I have a table of participants, each with their own unique ID number. They all do a writing test and their time, in minutes, is recorded in the fourth column as an integer. Some patients have repeated the test, there is no maximum number of repeats.
+============+===========+===============+======+
| Patient ID | Hand Used | Ambidextrous? | Time |
+============+===========+===============+======+
| 01 | Right | Yes | 12 |
+------------+-----------+---------------+------+
| 01 | Right | Yes | 10 |
+------------+-----------+---------------+------+
| 01 | Right | Yes | 11 |
+------------+-----------+---------------+------+
| 01 | Left | Yes | 13 |
+------------+-----------+---------------+------+
| 01 | Right | Yes | 12 |
+------------+-----------+---------------+------+
| 02 | Right | No | 9 |
+------------+-----------+---------------+------+
| 02 | Right | No | 7 |
+------------+-----------+---------------+------+
| 02 | Right | No | 8 |
+------------+-----------+---------------+------+
| 03 | Left | Yes | 8 |
+------------+-----------+---------------+------+
| 03 | Right | Yes | 8 |
+------------+-----------+---------------+------+
I want the "Ambidextrous?" column to be a formula. I've been trying to accomplish this with INDEX() and MATCH(), to no avail. It needs to check the "Hand Used" column, if there is both a Right handed and Left handed test for a particular Patient ID, we say "Yes".
Use:
=IF(COUNTIFS(A:A,A2,B:B,IF(B2="Right","Left","Right")),"Yes","No")
and thanks for your help! Without helper columns/rows I'm trying to find the average difference in LVEDi between first and last visits of the patients.
E.g., by hand you would work it out as:
(diff for patient a + diff for pt b + diff for pt c)/3 =
((55-45) + (40-31) + (25-31))/3 =
(10 + 9 + (-6))/3 =
13/3 =
4.333
I'm sure the answer involves indexing with an array function, but I can't get started on how to tackle it!
Any help?
Thanks!
A | B | C | D | E
01 Patient ID | Date | First-Last | LVEDi | LOTS of similar cols
02 a | 3/3/03 | LAST | 55
03 a | 1/1/01 | FIRST | 45
04 a | 2/2/02 | | 65
05 b | | |
06 b | 3/2/16 | LAST | 40
07 b | | |
08 b | | |
09 b | 2/1/12 | FIRST | 31
10 c | 1/7/14 | LAST | 25
11 c | | |
12 c | 2/2/03 | FIRST | 31
13 c | 2/2/08 | | 46
You can use the * as and AND criterion in arrays, so with MATCH you search for the instance, where both conditions multiplied are TRUE (i.e. 1). The rest is just filling in your calculation:
{=((INDEX(D2:D13,MATCH(1,(A2:A13="a")*(C2:C13="LAST"),0))-INDEX(D2:D13,MATCH(1,(A2:A13="a")*(C2:C13="FIRST"),0)))+(INDEX(D2:D13,MATCH(1,(A2:A13="b")*(C2:C13="LAST"),0))-INDEX(D2:D13,MATCH(1,(A2:A13="b")*(C2:C13="FIRST"),0)))+(INDEX(D2:D13,MATCH(1,(A2:A13="c")*(C2:C13="LAST"),0))-INDEX(D2:D13,MATCH(1,(A2:A13="c")*(C2:C13="FIRST"),0))))/3}
And remember to use CTRL + SHIFT + ENTER to enter the array.
Found a slightly less verbose method:
=SUM(SUMIFS(D:D,C:C,"LAST",A:A,{"a","b","c"})-SUMIFS(D:D,C:C,"FIRST",A:A,{"a","b","c"}))/3
EDIT 1
Building on Lukas' comment:
=(SUMIF(C:C,"LAST",D:D)-SUMIF(C:C,"FIRST",D:D))/COUNTIF(C:C,"LAST")
I need to list a running count in column A of each occurrence of the value "4A" in column B, but the count needs to reset to 0 if the value in column B equals "2B".
I have attached a sample of my workbook, column A shows how I need the count to operate. I am open to using VB or a formula.
Or in text format:
| A | B |
+---+----+
| 0 | 2B |
| | 3A |
| 1 | 4A |
| | 4B |
| | 4B |
| 2 | 4A |
| | 4B |
| 3 | 4A |
| | 9A |
| 0 | 2B |
| | 3A |
| 1 | 4A |
| | 4C |
| 2 | 4A |
| | 9A |
| 0 | 2B |
| | 3A |
| 1 | 4A |
| | 9A |
| 0 | 2B |
| | 3A |
| 1 | 4A |
| | 4C |
| | 9A |
| | 9B |
| | 9Z |
EDIT:
#Jeeped and #trincot proposed better non-array versions. I modified them slightly here:
#Jeeped's formula:
=IF(OR(B1={"2B","4A"}), COUNTIF(INDEX(B:B,AGGREGATE(14, 6, ROW(B$1:B1)/(B$1:B1="2B"), 1)):B1, "4A"),"")
After modification it uses LARGE function in the aggregate rather than SMALL and "" instead of TEXT(,).
#trincot's formula:
=IF(OR(B2={"2B","4A"}),(B2="4A")*(1+IFERROR(INDEX(G:G,MATCH(MAX(G$1:OFFSET(G2,-1,0))+1,G$1:OFFSET(G2,-1,0))),0)),"")
Compared to original, it's been rearranged slightly 'IF-wise', I also replaced magic 9999 with MAX([...]) + 1 and made it so that you can just paste it in 1st cell and drag down rather than have two separate formulas for first cell and the consecutive ones.
My original version:
I think it has the benefit of perhaps being the easiest to understand (? - I am obviously biased here so I might be wrong) and it's physically the shortest of all.
It has the obvious drawback of being an array-formula.
In cell "A1" you can write:
=IF(OR(B1="2B",B1="4A"),SUM((B$1:B1="4A")*(ROW(B$1:B1)>MAX(ROW(B$1:B1)*(B$1:B1="2B")))),"")
It's an array formula so you have to confirm it with ctrl + shift + enter.
Then you can drag it down.
Basically it counts number of occurrences of "4A" in a range extending from current row to the first provided that rows of counted "4A" are larger than that last row of "2B".
Here is a way with standard (non-array) formulas:
In A1 put:
=IF(B1="4A",1,0)
In A2 put:
=IF(B2="2B",IF(INDEX(A:A,MATCH(9999,A$1:A1))>0,0,""),
IF(B2="4A",1+INDEX(A:A,MATCH(9999,A$1:A1)),""))
Drag/copy A2 down to the remaining cells in the A column.
This will produce blanks where otherwise the same value would be displayed.
If repeating values don't need to be suppressed, then you can simplify the formula in A2 to:
=IF(B2="2B",0,IF(B2="4A",1+A1,A1))
... and copy it down.