How to see the +/- change in rolling average between two cells - google-sheets

As you can see from my picture, I have a list of bowling scores and some running averages. The issue I cannot seem to solve is I would like to be able to see the change in average between a game and the previous game. If the average goes down, it would say -1.2% for example or +2.1% if it goes up. I would really like negative averages to be in red and positive ones in green if that is possible.
Here is a copy of my sheet with the desired output in column G.

first you will need running average:
=ARRAYFORMULA(QUERY(TRANSPOSE(QUERY(TRANSPOSE(IF(ISNUMBER(
ARRAY_CONSTRAIN(SPLIT(SORT(REPT("♦ ", ROW(INDIRECT("A1:A"&COUNTA(A2:A)))-1), 1, 0)&
"♦"&TEXTJOIN("♦", 1, C2:C), "♦"), 999^99, COUNTA(A2:A))),
ARRAY_CONSTRAIN(SPLIT(SORT(REPT("♦ ", ROW(INDIRECT("A1:A"&COUNTA(A2:A)))-1), 1, 0)&
"♦"&TEXTJOIN("♦", 1, C2:C), "♦"), 999^99, COUNTA(A2:A)), )),
"select "&TEXTJOIN(",", 1, IF(LEN(A2:A),
"avg(Col"&ROW(A2:A)-ROW(A2)+1&")", ))&"")),
"select Col2", 0))
then you can do:
=ARRAYFORMULA(IF(A2:A<>"", {0; (INDIRECT("F2:F"&ROWS(F3:F))-F3:F)*-1}, ))
and finally color format it:
UPDATE:
with new functions, there is a new method...
average = sum / count therefore we can run running total / cumultative sum:
=SCAN(, A2:A20, LAMBDA(x, y, (x+y)))
and then just divide by sequence to get the running average:
=INDEX(SCAN(, A2:A20, LAMBDA(x, y, (x+y)))/(ROW(A2:A20)-1))
and to get the difference between x and x+1 cells:
={0; MAP(B2:B19, LAMBDA(z, OFFSET(z, 1, )-z))}
and the final touch:
0[=0][black]; -0.00###[<0][red]; 0.00####[color 50]
or in one go:
={0; INDEX(ARRAY_CONSTRAIN({QUERY(
MAP(SCAN(, A2:A20, LAMBDA(x, y, (x+y))), ROW(A2:A20)-1,
LAMBDA(a, b, a/b)), "offset 1", ); ""}-
MAP(SCAN(, A2:A20, LAMBDA(x, y, (x+y))), ROW(A2:A20)-1,
LAMBDA(a, b, a/b)), ROWS(A2:A20)-1, 1))}

You get the change percentage using: =ROUND((C3-C2)/C2*100,2) in G3:G20.
Check:
For going down from 145 to 123 this returns (with no rounding) -15.17241379.
145 + (145 * -15.17241379 / 100) = 123
For the colouring:
Select the cells G3:G20.
Go to -> Format -> Conditional formatting -> Format rules -> Format cells if… -> Greater than -> 0 -> Formatting style
-> [Down arrow next to letter A] -> [Pick your colour]
-> [Down arrow next to (bucket with) fill colour] -> [None]
PS: For the running average you can use: =AVERAGE(C$2:C2) in F2:F20.
Please comment, if and as this requires adjustment / further detail.

Related

Arrayformula to replace sumifs based on criteria - grouping data into categories/types

In this spreadsheet
Cols G and H give the total of each Account Type, from the data (A1:D)
If sum Dr - sum Cr > 0, then only this is shown in col G.
=if(sumifs(C:C,A:A,F2)-sumifs(D:D,A:A,F2)>0, sumifs(C:C,A:A,F2)-sumifs(D:D,A:A,F2),"")
If sum Cr - sum Dr > 0, then only this is shown in col H
=if(sumifs(D:D,A:A,F2)-sumifs(C:C,A:A,F2)>0, sumifs(D:D,A:A,F2)-sumifs(C:C,A:A,F2),"")
I am looking for an array formula which replaces these summifs formula, so that if either new Account Types (e.g. Type E, Type F etc) are added or new rows of data are added, then the formula would automatically calculate the sum Dr or sum Cr, instead of having to copy the formula down
try this in cell G2:
=BYROW(F2:F,LAMBDA(fx,IF(fx="",,LAMBDA(cx,dx,{IF(cx-dx>0,cx-dx,),IF(dx-cx>0,dx-cx,)})(SUMIF(A:A,fx,C:C),SUMIF(A:A,fx,D:D)))))
-
Use query(), like this:
=arrayformula(
lambda(
aggregated,
lambda(
account, balance,
{
"Account Type", "Dr", "Cr";
account,
if( balance >= 0, balance, iferror(1/0) ),
if( balance < 0, -balance, iferror(1/0) )
}
)(
query(aggregated, "select Col1", 0),
query(aggregated, "select Col2", 0)
)
)(
query(
A3:D,
"select A, sum(C) - sum(D)
where A is not null
group by A
label sum(C) - sum(D) '' ",
0
)
)
)
You can set a QUERY that finds both entire columns like this:
=QUERY(A2:D,"SELECT A,SUM(C)-SUM(D),SUM(D)-SUM(C) where A is not null group by A",1)
And check with LAMBDA if there are negative values and change them to null:
=LAMBDA(a,INDEX(IF(a<0,,a)))(QUERY(A2:D,"SELECT A,SUM(C)-SUM(D),SUM(D)-SUM(C) WHERE A is not null group by A",1))
If the values in your result table are just for display, you can use the QUERY format clause to hide negative values, such that the whole thing can be generated within a single QUERY:
=QUERY(A2:D,"select A,sum(C)-sum(D),sum(D)-sum(C) where A is not null group by A label sum(C)-sum(D) 'Dr',sum(D)-sum(C) 'Cr' format sum(C)-sum(D) '0;',sum(D)-sum(C) '0;'",1)

Formula Help: IF, Countif, Split. Override

I am currently running the following formula
=IF(P202="","",COUNTIF(SPLIT(P202," "),"*XXX*")+COUNTIF(SPLIT(P202," "),"*YYY*"))
This is to count the values XXX & YYY in column P. This returns values 0 - 4 primarily in column AC
However I have another column. Which is column W, in this column I have values such as 'cancelled' and 'postponed'.
What I would like to happen, as an example the above formula returns a value of 2 but if Column W was updated with cancelled. Then that value would alter the return to 0 as the event is now cancelled.
Thanks
try:
=INDEX(IF(P202="",,SUM(IFERROR(REGEXMATCH(""&
IF(W202="canceled", 0, SPLIT(P202, " ")), "(?i)xxx|yyy"), 0)*1
for array try:
=INDEX(IF(P202:P="",,MMULT(IFERROR(REGEXMATCH(""&
IF(W202:W="canceled", 0, SPLIT(P202:P, " ")), "(?i)xxx|yyy"), 0)*1,
SEQUENCE(COLUMNS(SPLIT(P202:P, " ")), 1, 0

Google Sheets: Adjusting an array formula to auto-appear in every row

The formula in question is =IF (ISBLANK(H2),"", ARRAY_CONSTRAIN(ARRAYFORMULA(IF( (MOD(SUM(INT(MID(REPT("0",20-LEN(H2))&H2,ROW($1:$31),1)*(MOD(ROW($1:$31),2)+1)/10)+MOD(MID(REPT("0",20-LEN(H2))&H2,ROW($1:$31),1)*(MOD(ROW($1:$31),2)+1),10)),10)=0), "✔", "❌")), 1, 1))
In English it checks if H2 contains a valid credit card (passing Luhn's algorithm, discussion / sample data here). The expected output is valid = ✔; invalid = ❌; if blank then nothing.
I'm trying to adjust this to appear in every row, but can't seem to nail it down. (Using the trick like for a formula =LEFT(H2,4)&" "&MID(H2,5,6), if it's =arrayformula(LEFT(H2:H100,4)&" "&MID(H2:H100,5,6)) it appears in every row without having to manually refill it when a new row is inserted).
Sample google sheet.
Try this:
=ARRAYFORMULA(
IF(
H2:H = "",
"",
IF(
MOD(
MMULT(
MID(REPT("0", 20 - LEN(H2:H)) & H2:H, SEQUENCE(1, 10, 2, 2), 1)
+ MID(REPT("0", 20 - LEN(H2:H)) & H2:H, SEQUENCE(1, 10, 1, 2), 1) * 2
- (MID(REPT("0", 20 - LEN(H2:H)) & H2:H, SEQUENCE(1, 10, 1, 2), 1) * 2 > 9) * 9,
SEQUENCE(10, 1, 1, 0)
),
10
) = 0,
"✔",
"❌"
)
)
)
If you want a more general solution (for card numbers longer than 20 digits), replace 20 with MAX(LEN(H2:H)) + MOD(MAX(LEN(H2:H)), 2), and 10 with (MAX(LEN(H2:H)) + MOD(MAX(LEN(H2:H)), 2)) / 2.

Is there a better way to replicate a column array n times in Google Sheets

I need to formulaic solution to copy a column range stacking on top of itself a given number of times. I found one ugly solution by incorporating a sequence function (to get 1,2,3...n) into an arrayformula for a text operation (Left). The Left operation does nothing but return the original value, but gives me the opportunity to include the sequence array.
There must be a better way to do this.
Problem: Write a formula that creates a column where a named column range is stacked on top of each other an arbitrary number of times. Must be a single formula as other users will need this to self adjust to a new length automatically.
=flatten( transpose( arrayformula( left( Column_Range,len( Column_Range ) + 0 *
sign( sequence( 1,Number_of_Times_To_Repeat ) ) ) ) ) )
could be written as:
=ARRAYFORMULA(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*SIGN(SEQUENCE(1, C1))))))
or:
=INDEX(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*SIGN(SEQUENCE(1, C1))))))
or:
=INDEX(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*TRANSPOSE(ROW(INDIRECT("A1:A"&C1))^0)))))
or:
=INDEX(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*TRANSPOSE(SIGN(ROW(INDIRECT("A1:A"&C1))))))))
or:
=INDEX(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*SPLIT(REPT(1&"♀", C1), "♀")))))
or:
=INDEX(FLATTEN(TRANSPOSE(LEFT(A1:A5, LEN(A1:A5)*COLUMN(INDIRECT("A1:"&ADDRESS(1, C1)))^0))))
or:
=QUERY(FLATTEN(SPLIT(REPT("♀"&JOIN("♀", A1:A5), C1), "♀",,)), "offset 1")
or:
=FLATTEN(SPLIT(REPT(QUERY(A1:A5,,9^9)&" ", C1), " ",,))
Solution 1
Here is a way without FLATTEN:
=ARRAYFORMULA(
VLOOKUP(
1 + MOD(
SEQUENCE(C1 * MAX(ROW(A:A) * (A:A <> ""))) - 1,
MAX(ROW(A:A) * (A:A <> ""))
),
{ROW(A:A), A:A},
2,
0
)
)
MAX(ROW(A:A) * (A:A <> "") inside ARRAYFORMULA just gives you the row number of the last non-empty cell. The rest is quite straightforward.
Solution 2
And here is a FLATTEN solution without LEFT:
=FLATTEN(
ARRAYFORMULA(
ARRAY_CONSTRAIN(
TRANSPOSE(A:A),
1,
MAX(ROW(A:A) * (A:A <> ""))
) & IF(SEQUENCE(C1), "")
)
)

How can I create a range display from a list of non consecutive numbers?

I made a bash script to display numbers in range and I wonder if it's possible to do the same in Sheets without scripting.
I got in a column (let's say A) a list of numbers, for example :
001
002
004
012
013
014
...
and I have a variable prefix in a cell (let's say B1="PREFIX")
Is there a way to display the result as below :
PREFIX001-PREFIX002
PREFIX004
PREFIX012-PREFIX014
...
Thank you by advance for your help !
Could be done. For example A3:A range has the numbers, B1 will be the prefix and B3:B will have the resulting rows.
The formula is only (as it is an array formula) in B3:
=ARRAYFORMULA(
TRANSPOSE(SPLIT(
REGEXREPLACE(
REGEXREPLACE(
TEXTJOIN(
",",
True,
IF(
NOT(ISNUMBER(A3:A)),
"",
IF(
NOT(ISNUMBER({""; OFFSET(A3:A, 0, 0, ROWS(A3:A) - 1)}))
+ NOT(ISNUMBER({A4:A; ""}))
+ ( ISNUMBER({""; OFFSET(A3:A, 0, 0, ROWS(A3:A) - 1)})
* (A3:A <> {""; OFFSET(A3:A, 0, 0, ROWS(A3:A) - 1)} + 1))
+ ( ISNUMBER({A4:A; ""})
* (A3:A <> {A4:A; ""} - 1)),
TEXT(A3:A, "00#"),
""
)
)
& IF(
ISNUMBER({A4:A; ""})
* (A3:A = {A4:A; ""} - 1),
"-",
""
)
),
"(?:-,)+",
"-"
),
"\d+",
B1 & "$0"
),
","
))
)
Here is a sample sheet with step by step description of the solution: link.
#kishkin
Thank you so much for taking the time to do that ! I made a copy to check it, that's very kind of you !
I made mine with multiple columns but in the nasty way (I'm quite new with Sheets but I love it so far) :
to group consecutive numbers :
=IF(CNUM(V2)=CNUM(V1)+1,IF(REGEXMATCH(W1,"-"),LEFT(W1,CHERCHE("-",W1)-1)&"-"&V2,V1&"-"&V2),V2)
to replace lonelies that are part of a range :
=IF(LEFT(W3,2)=LEFT(W2,2),0,W2)
Split the range to calculate range value later :
=SPLIT(X2,"-")
To calculate the range except for 0 values :
=IF(Z2-Y2+1<0,1,Z2-Y2+1)
To remove all the 0 so only the ranges remain :
=filter(X2:X,X2:X>0)
To align the totals with the ranges (useful for group reservations for a project)
=filter(AA2:AA,X2:X>0)
After that, it's just adding the prefix and add some text (sorry for the french).
As I told you it's not as sexy as your solution ^^

Resources