Skip to content

Instantly share code, notes, and snippets.

@ncalm
Last active June 15, 2023 09:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ncalm/0b9ed37625152a45a5118ee789921854 to your computer and use it in GitHub Desktop.
Save ncalm/0b9ed37625152a45a5118ee789921854 to your computer and use it in GitHub Desktop.
This Excel lambda namespace provides functionality for creating arrays of functions and sequential vector transforms
/*****************************************************************************************
******************************************************************************************
Array of functions
******************************************************************************************
Allows for creation of an array of functions which can be passed as a parameter to another function
Original credit to: Travis Boulden
https://www.mrexcel.com/board/threads/ifanyof.1184234/
Function named "either" on that page
In the code below, I have simplified slightly to use VSTACK instead of CHOOSE
and SUM instead of REDUCE to calculate the count of not-omitted functions
e.g. Apply the SQRT, LN and LOG_10 transformations to the wine vector:
=LAMB.TRANSFORM(wine, LAMB.FUNCS(LAMB.SQRT, LAMB.LN, LAMB.LOG_10))
Issue here is if we provide fn_1, don't provide fn_2, then provide fn_3, it will try to return
an array containing fn_1 and fn_2
*/
FUNCS =LAMBDA(
fn_1,[fn_2],[fn_3],[fn_4],[fn_5],
[fn_6],[fn_7],[fn_8],[fn_9],[fn_10],
LET(
//An array indicating which functions are omitted
omitted_fns,
VSTACK(
ISOMITTED(fn_1),ISOMITTED(fn_2),
ISOMITTED(fn_3),ISOMITTED(fn_4),
ISOMITTED(fn_5),ISOMITTED(fn_6),
ISOMITTED(fn_7),ISOMITTED(fn_8),
ISOMITTED(fn_9),ISOMITTED(fn_10)
),
//count of the not omitted functions
fn_ct,SUM(--NOT(omitted_fns)),
//return the first fn_ct functions in an array
fns,
CHOOSE(SEQUENCE(fn_ct),
fn_1,fn_2,fn_3,fn_4,fn_5,
fn_6,fn_7,fn_8,fn_9,fn_10
),
fns
)
);
/*
Author: OWEN PRICE
Date: 2022-08-27
Apply a single function to each column in an array.
The function may produce multiple output columns per data-column
This approach uses iteration. Other approaches are available :)
*/
BYCOL =LAMBDA(data, fn, [output],
LET(
//get the first column to apply the function to
_v, TAKE(data,,1),
//get the remaining columns
_rem, DROP(data,,1),
//apply the function to the first column and get the results
_applied, fn(_v),
//stack the new results with the results of the previous columns (or nothing if this is first iteration)
_stacked, IF(ISOMITTED(output), _applied, HSTACK(output, _applied)),
//if data currently only has one column, then just return the stack, otherwise iterate using _rem=data
_output, IF(COLUMNS(data)=1,_stacked,LAMB.BYCOL(_rem, fn, _stacked)),
_output
)
);
/*****************************************************************************************
******************************************************************************************
Library of transformation lambdas
******************************************************************************************
Author: OWEN PRICE
Date: 2022-08-27
Examples of simple vector transforms that can be applied sequentially using LAMB.TRANSFORM
*/
//Wraps the SQRT function as a lambda so it can be passed around other functions
SQRT = LAMBDA(vector, SQRT(vector));
//Wraps the LN function as a lambda so it can be passed around other functions
LN = LAMBDA(vector, LN(vector));
/*
Returns a lambda of the LOG at the specified base
The returned lambda can then be passed to other functions
To create a "log base 10" function:
=outlier.log(10)
To use that function with a vector v:
=outlier.log(10)(v)
*/
LOG = LAMBDA(base, LAMBDA(vector, LOG(vector, base)));
//For simplicity, create a lambda function for applying the log10 transform to a vector
LOG_10 = LAMBDA(vector, LAMB.LOG(10)(vector));
//Returns a lambda function that raises a vector to the given power
POWER = LAMBDA(exponent, LAMBDA(vector, POWER(vector, exponent)));
RECIPROCAL = LAMBDA(vector, LAMB.POWER(-1)(vector));
RECIPROCAL_SQ = LAMBDA(vector, LAMB.POWER(-2)(vector));
CUBEROOT = LAMBDA(vector, LAMB.POWER(1/3)(vector));
/*
Author: OWEN PRICE
Date: 2022-08-27
Used to transform a vector once for each transformation function in transform_fns
e.g. transform the 'wine' vector using SQRT, LN and LOG10
=LAMB.TRANSFORM(vector, LAMB.FUNCS(LAMB.SQRT, LAMB.LN, LAMB.LOG_10))
*/
TRANSFORM =LAMBDA(vector,transform_fns,
REDUCE(vector,transform_fns,LAMBDA(a,b,HSTACK(a,b(vector))))
);
/*
BETWEEN
Author: Owen Price
Date: 2022-09-10
Returns a lambda that itself returns TRUE if the vector value is >=gteq (the lower boundary)
or the vector value is <=lteq (the upper boundary)
*/
BETWEEN =LAMBDA(gteq,lteq, LAMBDA(vector, IFERROR(( (vector>=gteq) * (vector<=lteq) ) > 0, FALSE)));
/*
ROTATE
Author: Owen Price
Date: 2022-09-10
Rotates a 2-dimensional array anti-clockwise by 90 degrees.
This is not the same behavior as TRANSPOSE, which reflects an array on the main diagonal
from top-left to bottom-right.
Inputs:
- arr - the input array
- times - the number of times to rotate by 90 degrees
- [iter] - optional - used as a counter by the recursion
Note: iter should not be used when calling this function from a spreadsheet
*/
ROTATE = LAMBDA(arr,times,[iter],
LET(
_times,MOD(times,4),
IF(_times=0,arr,
LET(
_iter,IF(ISOMITTED(iter),1,iter),
_cols,COLUMNS(arr),
_rotated,INDEX(arr,SEQUENCE(1,ROWS(arr)),_cols-SEQUENCE(_cols)+1),
IF(_iter=_times,_rotated,ROTATE(_rotated,_times,_iter+1))
)
)
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment