Forum Discussion
A statement of truth or a wasted opportunity. Lambda helper functions reluctant to return 2D arrays.
Lost the file!
PeterBartholomew1 I did think of a MAP solution just now to test out, and its really just a glorified INDEX solution, please try the below. I've changes MEMBERS lambda to TRANSPOSE rather than CONCAT, then MAP across 2 arrays, the row indices and column indices.
= LET(
\0, "Extract source data",
pos, --LEFT(rawdata,7),
name, (MID(rawdata,8,22)),
team, TRIM(MID(rawdata,31,26)),
time,--("00:"&RIGHT(rawdata,5)),
teamList, UNIQUE(team),
\1, "Build Lambda functions",
SCOREλ, LAMBDA(t,
LET(
pts, FILTER(pos, team=t),
SUM(INDEX(pts,{1,2,3,4}))
)),
MEMBERSλ, LAMBDA(t,
LET(
m, FILTER(name, team=t),
TRANSPOSE(m)
)),
\2, "Identify scoring members of teams in order",
teamScore, MAP(teamList, SCOREλ),
teamOrder, SORTBY(teamList, teamScore),
row_,MAKEARRAY(ROWS(teamOrder),4,LAMBDA(i,j,i)),
col_,MAKEARRAY(ROWS(teamOrder),4,LAMBDA(i,j,j)),
scoring, MAP(teamOrder, MEMBERSλ),
MAP(row_,col_,LAMBDA(r_,c_,INDEX(MEMBERSλ(INDEX(teamOrder,r_)),c_)))
)
- PeterBartholomew1Sep 18, 2021Silver Contributor
I got some advice concerning the original 'array of arrays' problem. Essentially the advice was 'rather than trying to create arrays of arrays, reduce the array to 'thunks', which can be handled as scalars and evaluated at the end using MAKEARRAY. In the event, I didn't even need to do that because I was already working with Lambda functions (team name as the parameter) which evaluated correctly.
= LET( teamList, UNIQUE(team), teamScore, MAP(teamList, SCOREλ), teamOrder, SORTBY(teamList, teamScore), result, MAKEARRAY(COUNTA(teamList), 4, LAMBDA(t,p,INDEX(MEMBERSλ(INDEX(teamOrder,t)), p)) ), IFERROR(result, ""))
- tbouldenSep 18, 2021Iron ContributorThat's a nice clean construction that encapsulates our journey from your original post!!
- PeterBartholomew1Sep 03, 2021Silver Contributor
I got a surprise playing with SCAN in response to a question on this forum. Basically the OP wanted to decrease an amount by 10% per day, thus giving rise to a geometric sequence. Besides calculating the sequence directly, I also used SCAN to give the result array in a manner that could accommodate a table of variable rates.
Then I turned the percentage into an array {10%,12%} to see what happens, fully expecting SCAN to moan about nested arrays. What happened was that the direct calculation of geometric sequences gave two sequences, one for 10% and the other 12%, but scan did something entirely different.
What it did was to apply an alternating pattern of 10% and 12% as a single calculation! The challenge might be to think up a meaningful use case for such a calculation.
- JoeMcDaidSep 07, 2021
Microsoft
SCAN iterates through the provided array argument in row-major order and returns its output in the same shape as the array argument. So this behavior is expected, array-of-array are not encountered as each iteration of SCAN only returns a scalar.
- PeterBartholomew1Sep 08, 2021Silver Contributor
I was getting there slowly by trial and error, but it is good to have the behaviour confirmed. It has a logical basis for understanding though I can think of few cases where I would be likely to use the row-major order. Maybe an escalation calculation where the days are organised by weekday; continuity as one moves down a week could make sense.
Maybe scanning an array with multiple initial values would require separate functions of VSCAN and HSCAN? In business-based calculation, I suspect that most scanned accumulations would normally be conducted in parallel, but in engineering dynamics there are likely to be multiple position and velocity values required to capture the state of a system and there will be coupling between the system coordinates from step to step. I wanted to check the simulation of a container port crane moving a load that is free to swing as a pendulum.
I gave up on that (it requires 4 values), but have attached a worksheet following the motion of a single object. That only required one position x(t) and the corresponding velocity v(t) to be tracked. Because SCAN only works the way I need over a single column, I combined the two values as a complex number for the purpose of the calculation. The job of the Lambda function is to split the string into two numbers, to calculate the two in a way that takes account of the current velocity forces acting on the system (restoring force, control force and damping) and then uses the COMPLEX function to recombine the two forces into one.
= SCAN("2i", F#, LAMBDA(coord,F₀, LET( x₀, IMREAL(coord), v₀, IMAGINARY(coord), x₁, x₀ + Δt*v₀, v₁, v₀ + Δt*F₀/Mass - Δt*Coef*v₀-κ*x₀, COMPLEX(x₁,v₁) ) ) )
The result is generated as a complex number which is absolutely unintelligible, so I split real and imaginary parts (position and velocity) for presentation. I then hit the problem that IMREAL and IMAGINARY do not accept array arguments, so I have to use MAP. Map will only transform one column at a time so there are two distinct applications of MAP, which I then combine into an array.
= MAP(p#, LAMBDA(z, IMREAL(z))) = MAP(p#, LAMBDA(z, IMAGINARY(z))) = IF({1,0},x#,v#)
There is a lot of tidying up I could do, but VSCAN (or equivalent solution) would be great sometime in the future.
Meanwhile there is a lot to learn concerning how best to use what we have. For example, is it good to leave the anonymous Lambda functions at the point at which they are used; is it better to use a LET wrapper to define a local function so that it is more meaningful at the point of application. A step further and one hides all the working within Name Manager.
- tbouldenSep 04, 2021Iron Contributor
PeterBartholomew1 Apologies, took some time to digest your post, but I'm not sure I am understanding. In cell L4, I've modified it to the following:
= LET( rateByDay, Rate*SEQUENCE(Days-1,,,0), result,SCAN(initial, rateByDay, LAMBDA(acc,r, acc*(1-r))), result )
So Rate = {10%,12%}, however SCAN is using rateByDay, which is a 9x2 array, 9 rows of {10%,12%}. MAP/REDUCE/SCAN perform at the smallest unit of the 2nd parameter array, so value-by-value, moving from top-left to bottom-right. What you're describing seems expected? Am I misunderstanding?
- PeterBartholomew1Sep 04, 2021Silver Contributor
What I wanted to achieve was an array of two SCANs, one down each of two columns, with a row of two initial values, so that I could compare the effect of 10% depreciation against 20%. I had picked up the message that arrays of arrays were not allowed, so expected an error message. The surprise was that I got numbers at all, then that I couldn't immediately recognise the values returned.
I have used MAP to return each of a 2D array as independent calculations, but order hardly matters there. What I hadn't realised was that SCAN and REDUCE would also accept 2D arrays in exactly the same way but, given they are accumulating, order is important. It seems to work like the Persian story of one grain of rice being placed on the first square of a chess board and two on the next. 2⁶⁴ grains are unaffordable. The modern-day equivalent might be 'on the second week there will be two Covid cases'. Some of our politicians were noticeably unable to grasp the consequences.