I have found a more robust solution and it looks intimidating but it breaks into chunks:
=AVERAGE(IFERROR(INDEX(FILTER(SORTBY($B1:P1,COLUMN($B1:P1),-1),SORTBY($B1:P1,COLUMN($B1:P1),-1)<>0),SEQUENCE(12,1,1,1)),""))
Let's break that into pieces, starting with the SORTBY()
function that appears twice:
SORTBY($B1:P1,COLUMN($B1:P1),-1)
Note that the first part of the reference is fixed at $B1
so that won't update as we copy/paste to the right. If we put this formula in P2
and copy/paste to the right, we'll always be looking at the range from B1
to whatever column we're on now. In short, all the data so far. Now, we're going to sort that data in reverse order (that's what the -1
does) by sorting it by the column numbers in descending order. Let's re-write the formula as a shorter version now that this piece has been discussed:
=AVERAGE(IFERROR(INDEX(FILTER(SORTBY(~),SORTBY(~)<>0),SEQUENCE(12,1,1,1)),""))
Next, focus on the INDEX(FILTER(),SEQUENCE())
setup. The FILTER()
function takes that sorted array from above and filters out all the zero values. It'll treat blanks as zero so those are covered, too. Now we have an array of all non-zero values in reverse order. By combining that with INDEX()
and SEQUENCE()
, we can pull out just the first 12 values from that list. Combining all that means we now have the 12 most recent non-zero values from the range B1
to whatever. After you pull out those bits, the rest is pretty simple:
=AVERAGE(IFERROR(INDEX(~,""))
If there aren't at least 12 non-zero values, INDEX()
returns an array with some error values in it. We use IFERROR()
to replace those errors with blanks so AVERAGE()
can work. The only way you should see an error now is if there aren't any non-zero values.

If you prefer, the LET()
functions can save you from having to repeat things by giving them names. This is the same function as above but the SORTBY()
mess has been pulled out:
=LET(sortedRange,SORTBY($B1:P1,COLUMN($B1:P1),-1),AVERAGE(IFERROR(INDEX(FILTER(sortedRange,sortedRange<>0),SEQUENCE(12,1,1,1)),"")))