Does Excel have any map or select functions? - excel

I have a row of string values, which I'd like to do a vlookup on each and then compute the average of the results. If this were C#, I'd do a Select( str => VLookup(str,dict)).Average(), is there a way to do this in a single excel function?
I'm using version 2010

In general, no. For your specific case, kind of.
Say you have a table like this
a 42
b 2
c 3
in E8:F10. If you call VLOOKUP with an array like this:
=VLOOKUP({"a","c"},E8:F10,2,FALSE)
you'll get back an array of values: {42,3}. (Enter the formula as an array function...Ctrl+Shift+Enter.) So the map part you can do in this case.
Unfortunately, AVERAGE doesn't seem to work with the array that VLOOKUP returns if you put it all in one formula and put the result in one cell. Worse, it appears to work, but just uses the first element, even if you enter the whole thing as an array formula. That is, if you put:
=AVERAGE(VLOOKUP({"a","c"},E8:F10,2,FALSE))
in cell H12, even if you enter it as an array formula, you'll get back 42 and not 22.5.
Oddly, putting the same formula in two cells, say H16:I16, and entering it as an array formula gives you back an array with two elements: {22.5, 22.5}. (It's probbaly just a one-element array getting expanded into two cells.) So you can get what you need without having to have a whole large intermediate array of results. (Using arrays in place of non-array arguments to worksheet functions can be wierd. See: Is there any documentation of the behavior of built-in Excel functions called with array arguments? )
Of course, the more Excel-like way to do this is to use an intermediate array and not try to compress it into a fancy array-formula. You'd have a list of the strings you want to look up, then drag a plain VLOOKUP (with an absolute reference to your lookup table) down/across a parallel row/column, and then average the results.

Related

Evaluate a range of strings based on a shorter list of strings (Excel 365)

While working on a method of filtering dynamic ranges, I came across an unexpected behavior of COUNTIF().
I wanted to evaluate a range (A2:A9) to see which lines match a shorter list in B2:B3. Useful for further use in FILTER(), SUMPRODUCT() and similar functions. Example below: go through a list on 9 stocks; mark the two stocks that are on the shorter list of 2; return a 9-item dynamic array of 1 and 0 in the same order as on the list of original 9 stocks.
An equivalent expression in Python would be plain vanilla iteration
if criteria_str in evaluated_range:
newlist.append(1)
else:
newlist.append(0)
I got what I wanted by using COUNTIF().
However I am completely confused about the order of arguments. Based on my past experience before Excel 365 enabled common functions to return dynamic ranges, I expected that [range] is A2:A9 and [criteria] is B2:B3. However, Excel assumes the opposite. "All items" are "criteria" (red) and "some items" are "range" (blue)?
Official Microsoft documentation for COUNTIF does not describe at all how dynamic arrays are handled. And for static operations, the order is the same it has always been - all items are "range", expression is "criteria".
Is this behavior (for this function, or all functions that return a dynamic array) documented anywhere?
It has always been that way.
COUNTIF(RangeToBeSearched,WhatToSearchFor)
COUNTIFS has always returned an array, it just did not spill like you see. For example =SUMPRODUCT(COUNTIF(B2:B3,A2:A9)) would have returned 2 regardless of the version you are using.
The second is the array of items to pass through the COUNTIFS. If you reversed them:
=COUNTIFS(A2:A9,B2:B3)
It would only "Spill" two lines because that is all there is in the second argument.
The first argument is the range to search and the second is what to search for. If there is more than one the COUNTIFS returns an array the size of the second argument.
To put it another way, your formula is the same as doing:
=COUNTIF(B$2:B$3,A2)
And dragging the formula down, but it just iterates for you. It has always been this way, you just did not "See" the results.
In older versions you could have highlighted C2:C9. Then put your formula in the formula bar and hit Ctrl-Shift-Enter and the results would spill like you see in Office 365.
Everything works in your formula as expected. You can use both =COUNTIF(B2:B3,A2:A9) and =COUNTIF(A2:A9,B2:B3) expressions depended on wanted result. With first you get your wanted result, simply you check every element in list A2:A9 if it appears in list B2:B3 and if it does, count it as 1. With second formula you would check how many times every element in list B2:B3 does appear in list A2:A9.
I have modified your list (added UBER to A10), maybe it will be easier to understand:

Excel apply functions on FILTER. ie COUNTIF(FILTER(RANGE,FilterCondition),IfCondition)

Excel doesn't seem to let you use function such as RANK/COUNTIF on a FILTER(...) formula without first having the output of the filter somewhere in the spreadsheet. Ie, functions take range as argument but not arrays.
Let's assume in Column A, I have number 1 to 10 on the ten first rows
and in column B a filter on even numbers, =FILTER(A1:A10,MOD(A1:A10,2)=0)
If I want to count the number of rows of array returned by the filter function, I can use =COUNT(B1#)
If I want to count only the values above 5 using COUNTIF, this will also work =COUNTIF(B1#, ">5")
Now, I would like to get rid of the intermediary B Column.
For the COUNT function, it works. =COUNT(FILTER(A1:A10,MOD(A1:A10,2)=0)) returns 5.
For the COUNTIF function, it doesn't work anymore. I assume this because it takes a range as a parameter and not an array.
Excel warns in a popup: "There's a problem with this formula".
Is there a workaround this without using a temporary column to store the array? It seems I can only use functions that takes an array as parameter on top of the filter function.
EDIT:
I have used COUNTIF to build a simple example but I am more interested in other functions such has RANK.
COUNTIF was not the best example because you can obviously move the IF conditions in the FILTER conditions. Thank you #JvdV

Return array from INDEX function in Excel?

I'm trying to use INDEX in array format in Excel but I'm running into problems.
From this question: Return array from INDEX function?, it seems that "INDEX (like VLOOKUP) doesn't return an array of values (except in some convoluted circumstances)"
So I'm wondering what alternatives there are.
I'm trying to do this:
=qlTimeSeries( ,
INDEX({39618,39619,39638,39639},{2,3,4}),
INDEX({18,19,38,39},{2,3,4})
)
and Index is not returning an array.
Any alternative idea how to get the result I am trying to get with INDEX({39618,39619,39638,39639},{2,3,4})?
i.e. from {39618,39619,39638,39639} get {39619,39638,39639} back?
NOTE: Instead of INDEX({39618,39619,39638,39639},{2,3,4}) my spreadsheet in fact uses a dynamic array of dates and the objective is to drop the first element of the array {39618,39619,39638,39639} which is why I am using INDEX.
EDIT: Looking for a solution that avoids using VBA
Try this array formula:
= INDEX({39618,39619,39638,39639},N(IF({1},{2,3,4})))
This will return what you desire: {39619,39638,39639}.
Note this is an array formula, so you must press Ctrl+Shift+Enter on the keyboard after typing this formula instead of just pressing Enter.
(Also note this works with dynamic ranges, not just hard-coded arrays.)

Which built-in excel functions treat ranges as arrays?

For example =SUMPRODUCT(A2:A10-10) does, but =SUM(A2:A10-10) does not. Specifically in my situation I wish =MEDIAN(ABS(A2:A10-MEDIAN(A2:A10))) did; however sadly it does not, and if I knew which built in functions convert the ranges into arrays before they evaluate then I might be able to reformulate something that gives me what I'm looking for...
there are some natural array type formulas like SUMPRODUCT and some of the options in AGGREGATE, as well as some financial functions.
Many can be "forced" into array mode by simply using Ctrl-Shift-Enter instead of Enter when exiting edit mode.
For example your formula:
=MEDIAN(ABS(A2:A10-MEDIAN(A2:A10)))
If it is confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode will "Force" the Array. If done correctly the Excel will put {} around the formula.
The INDEX function has an Array Form.
=SUM(INDEX((A2:A10)-10, , ))
In the above, INDEX is returning an array of values but a wrapping SUM is required to collect a total. The blank parameters represent all rows and all columns in the range (e.g. A2:A10) specified. All rows and all columns in the range can also be represented with zeroes in place of the blank parameters.
With 2 to 10 in A2:A10, your MEDIAN example would return 14 from,
=MEDIAN(INDEX(ABS((A2:A10)-MEDIAN(A2-A10)), , ))
(without CSE)
Use the Evaluate Formula command to see more of the inner workings of this style of INDEX use.

Excel 2003/2007 named formula appears to store only single value not range/array

Similar to a question by Stephen Roy April 26, 2013, answered by Barry Houdini. I have a named range per this generic formula
{=IFERROR(INDEX(Range,SMALL(IF(
MATCH(Range,Range,0)=ROW(INDIRECT("1:"&ROWS(Range))),
MATCH(Range,Range,0)),ROW(INDIRECT("1:"&ROWS(Range))))),"")}
[obviously in 2003 I don't use IFERROR()]
"Range" itself is a named array formula.
The formula is used to pull unique entries from a range and arrange them at the 'top' of another 'range'. However, INDEX() appears to 'store' only a single value, not the array expected. Barry talked about wrapping the ROW() in another function to lose the {array}. It looks as though the first ROW() returns multiple values and works fine, it's the last ROW() which seems to be reduced to the first array entry only, thereby causing INDEX() to return only a single value. However, instead of trying to store this in memory, I select a multi-cell range for output, and use CSE, it works perfectly well. But I don't want to have to do that.
I've tried messing around with INDIRECT(), but can't get that to work at all.
Grateful for your thoughts folks, ian taylor
This might work for you. To return a list of unique values given your range x for example:
=LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)),
ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))),
ROW(INDIRECT("1:"&ROWS(x))),x)
You could define this as a name called Unique and use it in other formulas eg =COUNTIF(x,Unique) returns an array of frequencies.
Addendum
Regarding INDIRECT/INDEX/ROW etc. here's my 2c (see also here). Function inputs and outputs can essentially be divided into three cases: single values, arrays or references. If a function input argument takes single values by default and an array is specified, then the result depends on the type of output of the function. If the output type of the function is:
A) a single value - an array of values is returned, one for each input value. This is the case for most functions eg =LOOKUP({1;2},{1;2;3},{"a";"b";"c"}) returns {"a";"b"}.
B) a reference - an array of references is returned. This is the case for INDIRECT/OFFSET. eg =INDIRECT("A1:A"&ROW()) returns a single element array containing a reference but this can raise errors in many functions as the references are not converted to values by default.
C) an array - a value or array is returned based on the first input of the function if evaluated normally and entered into a single cell. Or else an array is returned based on the first result for each input if entered into multiple cells. This is the case for INDEX which may return an array if one of the arguments is zero. For example =INDEX({1,2;3,4},0,{1;2}) returns {1;3} when evaluated in the formula bar, but {1;2} when array-entered into cells.
In fact Excel uses a variant data type that can take on any of these types so the details are a little more complex than this. Also there is the ability to modify arguments in place which may be why for example VLOOKUP behave like case C with arrays in the first argument but case A with the third argument.
i tried this as a defined name say, "Unique_List" so that i can use it as a Data validation list, but when i add this defined name to the data validation list, it gives an error.
=LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)), ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))), ROW(INDIRECT("1:"&ROWS(x))),x)
where x is my range.
i tried transposing this formula to get the values comma-separated rather than semicolon-separated, but data validation list doesnt seem to work.
=TRANSPOSE(LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)), ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))), ROW(INDIRECT("1:"&ROWS(x))),x))
Kindly advice.

Resources