I'm evaluating my portfolio, and I make a record of it in some form like this:
I wish to get the profit% of those stocks, from either;
Stocks with sold: (Price at SELL/Price at BUY)-1
Stocks not sold: (Current Price/Price at BUY)-1
For 1), I was thinking of getting the Last Price (Col E) of Stock A by using VLOOKUP conditioning with 'Stock A' of Col B and a 'SELL' of Col C (then obviously using the VLOOKUP again with 'SELL' condition changed to 'BUY').
Is there a way to look up a value with two conditions? Any other suggestions (using a touch of VBA for eg) would be greatly appreciated.
A VLOOKUP is, in effect, a special form of INDEX MATCH (or XLOOKUP, from Office365 onwards). In other words, VLOOKUP("Value", A:C, 3, FALSE) is the same as INDEX(C:C, MATCH("Value", A:A, 0)), and VLOOKUP("Value", A:C, 3, TRUE) is the same as INDEX(C:C, MATCH("Value", A:A, 1))
Then, to create a VLOOKUP equivalent that checks multiple columns, we need to create a MATCH function to do the same. Fortunately, this is possibly using Array Calculations.
Essentially, we replace the second argument of the MATCH (the list of values to look for) with a formula which creates an Array based on our Conditions, and we Search the list for where the conditions are True. For example, if our Conditions are Column B = "Stock A" and Column C = "BUY", then our Array Calculation is (B:B="Stock A")*(C:C="Buy"), and our MATCH looks like this:
MATCH(1, (B:B="Stock A")*(C:C="BUY"), 0)
(At a Boolean or Bitwise level, * is the same as AND: TRUE*TRUE=TRUE, FALSE*FALSE=FALSE, TRUE*FALSE=FALSE)
Rather than using Entire Columns (which are Slow), it is often best to limit the number of rows. You can either do this manually:
=INDEX($E$1:$E$5, MATCH(1, ($B$1:$B$5="Stock A")*($C$1:$C$5="BUY"), 0))
Or you can do it dynamically with INDEX and COUNTA:
=INDEX($E$1:INDEX($E:$E, COUNTA($A:$A)), MATCH(1, ($B$1:INDEX($B:$B, COUNTA($A:$A)), ="Stock A")*($C$1:INDEX($C:$C, COUNTA($A:$A)), ="BUY"), 0))
Final note: the MATCH function will start at the top of the list, and work down until the first match. If these are in Date Order, and you want to find the last match, then Office356 users have the XMATCH function available, which has an extra argument used to decide if the search is First-to-Last (XMATCH(.., .., .., 1)), Last-to-First (XMATCH(.., .., .., -1)), Ascending Binary Search (XMATCH(.., .., .., 2)), or Descending Binary Search (XMATCH(.., .., .., -2))
Otherwise, you may have to use SUMPRODUCT to get the MAX date that matches your criteria, and include that in your MATCH conditions (SUMPRODUCT(MAX(A:A*(B:B="Stock A")*(C:C="BUY")))
Is there a way to look up a value with two conditions?
There is a very simple solution - you can concatenate (using &) the two columns holding conditions for your search into one and then base your VLOOKUP search on that column. Remember to put this concatenated column at beginning of your table.
If you can ensure that the combination Stock A - BUY and Stock A - SELL occur only once like in
and never twice like in
Then you can use the SUMIFS function to "lookup" the value. Because the sum of only one value will always be the value itself:
=SUMIFS(E:E,B:B,"Stock A",C:C,"BUY")/SUMIFS(E:E,B:B,"Stock A",C:C,"SELL") -1
To be sure there is only one entry per combination you can use the COUNTIFS to check it and return a warning in case of multiple entries (which would result in a false value):
=IF(AND(COUNTIFS(B:B,"Stock A",C:C,"BUY")=1,COUNTIFS(B:B,"Stock A",C:C,"SELL")=1), SUMIFS(E:E,B:B,"Stock A",C:C,"BUY")/SUMIFS(E:E,B:B,"Stock A",C:C,"SELL") -1,"multiple entries found")
Related
I have a table like following:
And I want to count for each store, how many bills purchased single Item ? How many purchased 2 and full set of items ? The result should look like below
Where in store 'm3', bill 300 bought 2 item C but only count for 1 in the final result. This is where I struggle with, since I tried
COUNTIFS(A:A, "=m1", B:B, "=A") (then add with count for B and C) to get single item for store m1 but unable to figure out how to distinguish with the unique bill numbers.
Please don't mind asking if needed more clarifications, and I do prefer excel in-built functions rather than VBA.
Using Office 365 formula we start with the basic formula to find the singles:
=BYROW(E2:E4,
LAMBDA(z,SUM(--(
BYROW(--(COUNTIFS(A:A,z,C:C,UNIQUE($C$2:$C$8),B:B,{"A","B","C"})>0),
LAMBDA(a,SUM(a)))=1))))
This will give you the single results:
The others are all variations of that formula:
A,B
=BYROW(E2:E4,
LAMBDA(z,SUM(--(
BYROW(--(COUNTIFS(A:A,z,C:C,UNIQUE($C$2:$C$8),B:B,{"A","B"})>0),
LAMBDA(a,SUM(a)))=2))))-J2#
B,C
=BYROW(E2:E4,
LAMBDA(z,SUM(--(
BYROW(--(COUNTIFS(A:A,z,C:C,UNIQUE($C$2:$C$8),B:B,{"C","B"})>0),
LAMBDA(a,SUM(a)))=2))))-J2#
A,C
=BYROW(E2:E4,
LAMBDA(z,SUM(--(
BYROW(--(COUNTIFS(A:A,z,C:C,UNIQUE($C$2:$C$8),B:B,{"C","A"})>0),
LAMBDA(a,SUM(a)))=2))))-J2#
All
=BYROW(E2:E4,
LAMBDA(z,SUM(--(
BYROW(--(COUNTIFS(A:A,z,C:C,UNIQUE($C$2:$C$8),B:B,{"A","B","C"})>0),
LAMBDA(a,SUM(a)))=3))))
We have to add the -J2# to the end of the doubles to remove when it has all three. It will count in both places.
With Older Versions of Excel we need to do some gymnastics with SUMPRODUCT, MMULT, INDEX, MODE.MULT, etc
=SUMPRODUCT(
--(MMULT(
--(COUNTIFS(A:A,E2,C:C,INDEX(C:C,N(IF({1},MODE.MULT(IF(MATCH($C$2:$C$8,C:C,0)=ROW($C$2:$C$8),ROW($C$2:$C$8)*{1,1}))))),B:B,{"A","B","C"})>0),
{1;1;1})=1))
=SUMPRODUCT(
--(MMULT(
--(COUNTIFS(A:A,E2,C:C,INDEX(C:C,N(IF({1},MODE.MULT(IF(MATCH($C$2:$C$8,C:C,0)=ROW($C$2:$C$8),ROW($C$2:$C$8)*{1,1}))))),B:B,{"A","B"})>0),
{1;1})=2))-J2
=SUMPRODUCT(
--(MMULT(
--(COUNTIFS(A:A,E2,C:C,INDEX(C:C,N(IF({1},MODE.MULT(IF(MATCH($C$2:$C$8,C:C,0)=ROW($C$2:$C$8),ROW($C$2:$C$8)*{1,1}))))),B:B,{"B","C"})>0),
{1;1})=2))-J2
=SUMPRODUCT(
--(MMULT(
--(COUNTIFS(A:A,E2,C:C,INDEX(C:C,N(IF({1},MODE.MULT(IF(MATCH($C$2:$C$8,C:C,0)=ROW($C$2:$C$8),ROW($C$2:$C$8)*{1,1}))))),B:B,{"A","C"})>0),
{1;1})=2))-J2
=SUMPRODUCT(
--(MMULT(
--(COUNTIFS(A:A,E2,C:C,INDEX(C:C,N(IF({1},MODE.MULT(IF(MATCH($C$2:$C$8,C:C,0)=ROW($C$2:$C$8),ROW($C$2:$C$8)*{1,1}))))),B:B,{"A","B","C"})>0),
{1;1;1})=3))
Each of these would be placed in the first row of their respective columns and confirmed as array formula by using Ctrl-Shift-Enter instead of Enter when exiting edit mode.
They they would be dragged/copied down the columns.
Here, another alternative that generates the entire output for all cases with one formula. Use the following formula in cell E2:
=LET(ms, UNIQUE(A2:A8), ux, UNIQUE(A2:C8), CALC, LAMBDA(arr,cnt, BYROW(ms,
LAMBDA(m, LET(subset, CHOOSECOLS(FILTER(ux, INDEX(ux,,1)=m),2,3),
C, INDEX(subset,,2), SUM(BYROW(MMULT(IF(TOROW(C)=UNIQUE(C),1,0),
TRANSPOSE(IF(TOROW(INDEX(subset,,1))=arr,1,0))),
LAMBDA(r, N(SUM(r)=cnt)))))))),
HSTACK(ms, CALC({"A";"B";"C"}, 1), CALC({"A";"B"},2), CALC({"B";"C"},2),
CALC({"C";"A"},2), CALC({"A";"B";"C"}, 3)))
Here is the output:
We create a user LAMBDA function CALC with input argument arr (items values) and cnt (count condition to check), so we can generate all possible output changing the input parameters via HSTACK function.
CALC uses ux name, that represents the input removing duplicated row, such as the combination: {m3,C,300}. Now we cannot use RACON functions, because we need to work with an array, therefore in order to do the count for Item and Bill column values, we use MMULT function combined with IF statement as follow:
MMULT(IF(TOROW(C)=UNIQUE(C),1,0),TRANSPOSE(IF(TOROW(INDEX(subset,,1))=arr,1,0))
The output of MMULT, on each row (unique bill numbers) has the occurrences of each arr value. Therefore if we do the sum by row (inner BYROW) and check against the number of counts we are looking for (cnt), we get the expected counts we are looking for.
The rest is just to invoke CALC for all cases and append by column via HSTACK.
I want the code value in column B if the handset matches and the date is between the date from and the date to.
How the results should look:
Since you problem involve Date Range, therefore Vlookup & Index match is not possible to solve, I will use If + AND formula to solve your problem:
=IF(AND(E9>$G$3,E9<$H$3,F9=$E$3),$F$3,
IF(AND(E9>$G$4,E9<$H$4,F9=$E$4),$F$4,
IF(AND(E9>$G$5,E9<$H$5,F9=$E$5),$F$5,
IF(AND(E9>$G$6,E9<$H$6,F9=$E$6),$F$6,""))))
If you are able to add a column, one possible solution without using VBA would be to use a combination of SUMIFS and XLOOKUP formulas.
In column E, add a unique identification number (e.g., 1, 2, 3, 4, 5, etc.). You could reference the row number or manually fill down the numbers or whatever. It wouldn't matter as long as the values are unique and all numbers (no text).
Then, use a SUMIFS formula to sum Column E if (1) the handset number matches, (2) the date is greater than or equal to the start date, and (3) the date is less than or equal to the finish date. As long as there are no overlaps in the date ranges for each handset, this will return the unique ID number. Depending on the dataset, you might have to play with the less/greater than or equal to vs just less/greater than.
=SUMIFS(E:E,A:A,I2,C:C,"<="&H2,D:D,">="&H2)
Then, use the XLOOKUP to return the code based on the unique ID. In this example, I combined the SUMIFS and XLOOKUP in one formula, but you could also do it in two columns to provide better visibility.
=XLOOKUP(SUMIFS(E:E,A:A,I2,C:C,"<="&H2,D:D,">="&H2),E:E,B:B,"NOT FOUND",0,1)
Here's an example.
screenshot
I have this:
My issue:
in column F2 I want R2 -> IF -> B2+C2+D2 exists in O:O;P:P;Q:Q
but I do not know how to use VLOOKUP with multiple columns
my attemp was '=VLOOKUP(B2;O:O;4;FALSE)' and I do not know why I used 4... cuz I count R2 as index 4 from O2...
This is just a multiple column lookup. While two column lookups are more common, three column lookups are not that rare.
=index(r:r, aggregate(15, 6, row($2:$999)/((o$2:o$999=b2)*(p$2:p$999=c2)*(q$2:q$999=d2)), 1))
That will return the value from column R for the first matching set of columns O:Q. In the case of multiple matches, you could return the last match by changing 15 to 14.
Since your returned results are expected to be numeric, a sumifs could also be used.
=sumifs(r:r, o:o, b2, p:p, c2, q:q, d2)
However this would return skewed results is more than a single match was found.
In your own vlookup, the 4 represents the fourth column of your lookup range. Since you were only providing a single column (e.g. O:O), you would never return the value from column R without changing the lookup range to O:R.
Another approach (since they look like dates DD/MM/YYYY) would be to convert each group of three columns to dates
=INDEX(R:R,MATCH(DATE(D2,C2,B2),INDEX(DATE(Q:Q,P:P,O:O),0),0))
#Jeeped is right to point out that this is slow on full columns, so plz use a formula like
=INDEX(R$1:R$100,MATCH(DATE(D2,C2,B2),INDEX(DATE(Q$1:Q$100,P$1:P$100,O$1:O$100),0),0))
and adjust the ranges to include your data.
If O:O;P:P;Q:Q is unique, you can use:
=lookup(1,0/((O:O=B2)*(P:P=C2)*(Q:Q=D2), R:R)
I have an excel file like the following:
and I would like to replace the value of votes and avgsocre of those rows with Print = 1 with the rows I have in another file, which looks like the following:
The index number in the second file is exactly off by 1 and since there are also \N's with values from rows with Print = 0, so I cannot use replace then vlookup.
Would appreciate any help on this.
It looks like the variables that I'll call Design and Author uniquely identify your observations, so it would probably make more sense to look up Votes and AvgScore based on those values rather than trying to use the row-1 value in col A of the second file.
In your main file, make a column Votes_new with the formula (in this example, for row 2707):
{=INDEX(SecondFile!$A$1:$H$11, MATCH(1, (SecondFile!$B$1:$B$11=$A2707)*(SecondFile!$C$1:$C$11=$B2707)*(SecondFile!$G$1:$G$11=1), 0), 4)}
Use the same formula with 5 instead of 4 in the last argument in a column for AvgScore_new.
This formula matches on three criteria: Design (equal to the value in the current row), Author (equal to the value in the current row), and Print (equal to 1). It gets the value from the 4th column of the data in the second file, which is Votes (or the 5th column, which is AvgScore).
Note that you have to enter as an array formula using Ctrl+Shift+Enter.
Also note that this will return a #N/A error if there is no match (either because the Design-Author combination is not present or because Print is not equal to 1) so you may want to enclose this in an IFERROR() formula.
Alternatively, if you really want to look up by row reference, you can use =VLOOKUP(ROW()-1, ...) to look up the current row (e.g., 2707) minus 1 (2706) from the second file, and then find the value of print: =VLOOKUP(ROW()-1, SecondFile!$A1$H11, 7, FALSE). You can use this in an IF() function to look up a different column if the value is one: e.g., =IF(VLOOKUP(ROW()-1, SecondFile!$A1$H11, 7, FALSE)=1, VLOOKUP(ROW()-1, SecondFile!$A1$H11, 4, FALSE), "Value not found")
I am trying to use an INDEX MATCH to return the optimal choice from a list of items associated to values that change based on various criteria. My current sheet setup and formula is this:
A B C
Item1 Bad 27
Item2 Good 15
Item3 Good 27
Item4 Bad 44
Column A is a named range Item.
Column B is a named range ItemType.
Column C is a named range ItemValue
=INDEX(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")),MATCH(MAX(IF(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Type")="Good",INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Value"))),INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Value"),0))
(entered as an array formula)
In its current iteration, this will return Item1. I would like it to return Item3.
In this example, 'Sheet'!B3 contains the category to search (in this example Item). I use a variety of named ranges based on the data in that cell and suffixes with the use of INDIRECT/SUBSTITUTE. My current formula finds the highest value that also is Good, but then returns the first value that matches it, regardless of it being Good or Bad. I have tried adding an additional IF statement at different points of the formula but the cell would either return FALSE or a formula syntax error. How can I maintain my ="Good" IF statement throughout the formula?
Thank you!
You will need to double up the Good condition. Once for determining the max number and then again with the max number to determine the Item.
This non-array equivalent is the syntax I favor over the array method.
=INDEX(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")), MAX(INDEX(ROW(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")))*(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Type")="Good")*(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Value")=MAX(INDEX(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Value")*(INDIRECT(SUBSTITUTE('Sheet'!B3," ","")&"Type")="Good"), , ))), , )))
This is your array method.
=INDEX(INDIRECT('Sheet'!B3), MATCH(MAX(IF(INDIRECT('Sheet'!B3&"Type")="Good",INDIRECT('Sheet'!B3&"Value"))), IF(INDIRECT('Sheet'!B3&"Type")="Good", INDIRECT('Sheet'!B3&"Value")), 0))
If the ___Type and ___Value are always in the same position relative to the primary, you might want to abandon the three named ranges and just use a single named range with OFFSET.
=INDEX(INDIRECT('Sheet'!B3), MATCH(MAX(IF(OFFSET(INDIRECT('Sheet'!B3), 0, 1)="Good",OFFSET(INDIRECT('Sheet'!B3), 0, 2))), IF(OFFSET(INDIRECT('Sheet'!B3), 0, 1)="Good", OFFSET(INDIRECT('Sheet'!B3), 0, 2)), 0))
The OFFSET function is considered a volatile function. These recalculate whenever anything in the workbook changes and should be avoided for that reason. However, you are already using the INDIRECT function and that is considered volatile as well.