Find Nth Occurrence of Largest Value in a row - google-sheets-formula

I have some basic data on a rugby game, to be used in a scouting report. I need to:
Show which players have the highest PITA count (sum of all their other actions)
And
Show those players' 3 highest count actions - column headers are the different actions.
Pic of Dataset
The first half of my task was accomplished by the following index formula:
=if(C25=C24,index(C3:C17,small(if(large(B3:B17,2)=B3:B17,row(B3:B17)-2),2)), vlookup(large(B3:B17,2),B3:C17,2,false))
I've gotten the 3 players ranked by highest PITA count, which also includes tied values, as seen in the pink/salmon colored box. I was able to take the "Nth" value, so Player #2 and Player #8 are both shown correctly in 2nd and 3rd place, even though they have the same PITA value. Rather than Player #2 be shown twice since the Large function is looking for the same value.
I'd like to achieve similar to above, but display which type of action (column headers) is the player's 1st, 2nd, and 3rd most completed actions (blue columns). I am having trouble replicating the above formula to work horizontally and choose the correct column. Similar to above, I need it to be able to take the Nth value. The yellow highlighted row is an example of my problem, as I'd like it to show Ruck Disruption and then Tackler in the 1st Highest Action and 2nd Highest Action column, rather than it showing Ruck Disruption twice.
This is where I've gotten with said formula:
=if(large($E4:$L4,1)=0,"",index($E$2:$L$2,,match(large($E4:$L4,2),$E4:$L4,0)))
Link to spreadsheet: https://docs.google.com/spreadsheets/d/1P2msjxofWR8wfwjbfPYQexEzLEMSwVCwX5wNJUACZHI/edit?usp=sharing
Thanks in advance for any help.
Tried:
=if(large($E4:$L4,1)=0,"",index($E$2:$L$2,,match(large($E4:$L4,2),$E4:$L4,0)))
but keeps returning the first instance of "1" and therefore giving me Ruck Disruption instead of Tackler.

The way the get the player place seems may not have considered situation such as when there are two 1st place and two 2nd place, or even three 1st place, etc.
the formula below will take in all combinations and return PITA rank, PITA Count, and PlayerNumber as an arrary, it gives dinamic result, so that when player score the same PITA, they will be named as the same place, and can exceed 3 place if more than 3 same score appears:
=
LAMBDA(PLAYER,PITA,
LAMBDA(UPITA,
LAMBDA(UPITACOUNT,
LAMBDA(UPITACOUNT,
LAMBDA(RESULT_1,RESULT_2,RESULT_3,
LAMBDA(FIRST,SECOND,THIRD,
QUERY({
FIRST;
IF(COUNTA(INDEX(FIRST,,1))<3,SECOND,{"","",""});
IF(COUNTA(INDEX(FIRST,,1))+COUNTA(INDEX(SECOND,,1))<3,THIRD,{"","",""})
},"WHERE Col1 IS NOT NULL LABEL Col1'PITA rank',Col2'PITA Count',Col3'PlayerNumber'",0)
)(
MAKEARRAY(COUNTA(INDEX(RESULT_1,,1)),3,LAMBDA(ROW,COL,IFS(COL=1,"1st",TRUE,INDEX(RESULT_1,ROW,COL-1)))),
MAKEARRAY(COUNTA(INDEX(RESULT_2,,1)),3,LAMBDA(ROW,COL,IFS(COL=1,"2nd",TRUE,INDEX(RESULT_2,ROW,COL-1)))),
MAKEARRAY(COUNTA(INDEX(RESULT_3,,1)),3,LAMBDA(ROW,COL,IFS(COL=1,"3rd",TRUE,INDEX(RESULT_3,ROW,COL-1))))
)
)(
FILTER({PITA,PLAYER},PITA=INDEX(UPITACOUNT,1,1)),
FILTER({PITA,PLAYER},PITA=INDEX(UPITACOUNT,2,1)),
FILTER({PITA,PLAYER},PITA=INDEX(UPITACOUNT,3,1))
)
)(SORT({UPITA,COUNTIF(PITA,INDEX(UPITA,,SEQUENCE(UPITACOUNT)))},1,0))
)(COUNTA(UPITA))
)(UNIQUE(PITA))
)($C$3:$C$17,$D$3:$D$17)
one 1st and three 2nd:
two 1st and one 2nd:
five top:
more than one 3rd:
And this formula will return 3 results of the highest score actions of each player:
=ArrayFormula(
LAMBDA(DATA,
LAMBDA(TITLES,VALUES,
LAMBDA(RESULTS,
RESULTS
)(
BYROW(VALUES,LAMBDA(SCORE,
TRANSPOSE(IFNA(QUERY(SORT(TRANSPOSE({TITLES;SCORE}),2,0),"SELECT Col1 WHERE Col2>0 LIMIT 3",0),""))
))
)
)(INDEX(DATA,1),MAKEARRAY(COUNTA(INDEX(DATA,,1))-1,COUNTA(INDEX(DATA,1)),LAMBDA(ROW,COL,INDEX(DATA,ROW+1,COL))))
)($E$2:$L$17)
)

Related

How to Create a List of Available Times after removing Downtimes from a Period

I have a grid which lists the Period (Start - End), and a list of Downtimes.
The downtimes are then sorted (to ensure chronological order based on the start time of the outage), using the following formula:
=SORT(INDIRECT("B4:C"&SUMPRODUCT(MAX((B4:B12<>"")*ROW(B4:B12)))),1)
After which I am trying to calculate the list of Available times (Uptimes).
Currently i have a mess of inflexible formulas as follows:
B26 =IF(B14<B15,B14,C15)
C26 =IF(C14<C15,C14,B16)
B27 =C16
C27 =B17
I am searching for either a universal single celled arrayformula, or a formula that can be dragged (down/across), that can calculate the list of Available times (Uptimes).
I am seeking formula solutions that will work in both Excel (Mac 2021) and Google Sheets.
See attached image:
EDIT:
Here is a google sheet that has some example data, and explanatory notes: https://docs.google.com/spreadsheets/d/1t0XImtjP4RKeTdg3L97bjPzateUX2waHPhjf3nmSFIk/edit#gid=2100307022
in GS try:
=FILTER(B3:C24; A3:A24="Period")
update
B15:
=SORT(B4:C12)
B25:
=QUERY({C15:C23, B16:B24}, "where Col1 is not null and Col2 is not null")
Here is the solution i created (working in both Excel + Google Sheets) for cutting downtimes from a time period, to leave only the remaining uptimes.
Using the same cells and ranges as per the Question....
The original downtimes does not need to be in order, they are sorted in the intermediate table.
in cell B15:
=IFERROR(SORT(FILTER(B4:C12,(B4:B12<=C3)*(C4:C12>=B3))),"")
Then, create named ranges for the elements to be used in the formulas that will populate the remaining uptimes (this makes the formulas easier to edit as will be noted below):
start is the start time of the original time period (B14)
end is the end time of the original time period (C14)
downstart is the range of the start datetimes of the downtimes (B15:B23)
downend is the range of the end datetimes of the downtimes (C15:C23)
Then, to create/populate the list of the remaining uptimes, for the opening times, enter this formula into the first cell (B25) :
=IFERROR(IF((start>=MIN(downstart))*(end<=MAX(downend)),IF(ROW()=(25+SUMPRODUCT(--(LEN(downstart)>0))-1),"",SMALL(downend,1+ROW()-25)),
IF((start>=MIN(downstart)),SMALL(downend,1+ROW()-25),
IF((end<=MAX(downend)),IF(ROW()=25,start,IF(ROW()=(25+SUMPRODUCT(--(LEN(downstart)>0))),"",SMALL(downend,ROW()-25))),
IF(ROW()=25,start,SMALL(downend,ROW()-25))
))),"")
And for the ending times, enter this formula into the first cell (C25):
=IFERROR(IF((start>=MIN(downstart))*(end<=MAX(downend)),IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),"",SMALL(downstart,2+ROW()-25)),
IF((start>=MIN(downstart)),IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),end,SMALL(downstart,2+ROW()-25)),
IF((end<=MAX(downend)),IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),end,SMALL(downstart,1+ROW()-25)),
IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))),end,SMALL(downstart,1+ROW()-25))
))),"")
Note: In both of these formulas, the number 25 is the row number of the first row where the results are to be populated, so if you have these results starting on a different row, just change the number 25 to your starting row. Due to use of named ranges, no other changes are necessary.
After entering the formulas, drag them both down to fill the remaining results.
For those with Excel 2019 or newer (or Google Sheets), you can use IFS instead. For the opening times, use this (in B25 ):
=IFERROR(IFS(
(start>=MIN(downstart))*(end<=MAX(downend)),(IF(ROW()=(25+SUMPRODUCT(--(LEN(downstart)>0))-1),"",SMALL(downend,1+ROW()-25))),
(start>=MIN(downstart)),SMALL(downend,1+ROW()-25),
(end<=MAX(downend)),(IF(ROW()=25,start,IF(ROW()=(25+SUMPRODUCT(--(LEN(downstart)>0))),"",SMALL(downend,ROW()-25)))),
(start<MIN(downstart))*(end>MAX(downend)),(IF(ROW()=25,start,SMALL(downend,ROW()-25)))
),"")
And, for the ending times use this (in C25):
=IFERROR(IFS(
(start>=MIN(downstart))*(end<=MAX(downend)),(IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),"",SMALL(downstart,2+ROW()-25))),
(start>=MIN(downstart)),(IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),end,SMALL(downstart,2+ROW()-25))),
(end<=MAX(downend)),(IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),end,SMALL(downstart,1+ROW()-25))),
(start<MIN(downstart))*(end>MAX(downend)),(IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))),end,SMALL(downstart,1+ROW()-25)))
),"")
Again, drag them down to populate the remaining results.
Explanation:
(IF(ROW()=(25+SUMPRODUCT(--(LEN(downend)>0))-1),xxxxxx styled sections of the formulas check if on certain row, so that can return specific results
SMALL(named_range,ROW()-(25)) styled sections of the formulas uses the ROW with an offset (25, based on this examples starting row for the results) to increment the SMALL
Both the nested IF and the IFS styled solutions are in the example file linked in the opening Question.

Reset count after a new month starts

I'm adding data to a spreadsheet and I want to do basic tasks programmatically.
Every time I add a date like 03/01/2022 the month cells updates to "March" with
ARRAYFORMULA(IF(E2:E = "","", TEXT(E2:E,"mmmm")))
So, I'm counting the entries per month like this:
I created a formula to make a sequence, but it'll go infinitely as per the number of rows, I'd like to reset the count when the Month cell is different than the previous one.
=SEQUENCE(ROWS(B2:B))
David, I assume "Month" is in column B and you want the sequence in column A under "No."
Try using this formula in A2:
=arrayformula(if(B2:B="",,countifs(B2:B,B2:B,row(B2:B),"<="&row(B2:B))))
Briefly:
uses the arrayformula so you don't have to copy down the formula
if(B2:B="",, takes care of any blanks
countifs() along with row() does the rest of the magic.
to see the role of row(), try using just countif(B2:B, B2:B). This will give the total number of occurrences of "January. "February", etc.
row() combined with "<="&row() makes sure that the formula counts occurrences above the current row only.
Watch out for year change. All "January" values across different years will be added to the sequence.
Good luck.

Power Query, avg value based on the values appearing within a specified date range

Context:
I have a data set for the weights of truck and trailer combinations coming into my site over the span of a few years. I have organized my data by seasons as I am trying to prove that the truck:trailers in winter are noticeably heavier due to ice, snow, and mud. The theory is, if the tare weight is higher in this season (the weight of the truck after it empties its load) than its Avg tare weight (which I need to calculate from the data) it can be deduced that the truck:trailer combinations are coming in with extra weight that we pay for in part as some snow/ice/mud falls off in the trailer emptying process.
What I've done so far:
I've defined a custom date range for my seasons
I've grouped Truck:Trailer by: count to get a duplicates column and, all rows to keep all my details
I've filtered out every combination I've seen less than 50 times, as i want good representation for each truck:trailer combo so that I can better emphasize repeated patterns
I've added an index column to better keep track of the individuals before expanding the details
What I need to do:
I only want to work with truck:trailer combinations which have weighed in for all four seasons at least once
I need to find the average tare weight of the truck:trailer combinations based over the extended range for both summer and autumn (the dry time of the year) while preserving the raw tare data for all seasons, as I need to eventually compare the winter tare values to this average.
example of my data
When I'm finished I'd like the data to look something like this
Pivot Chart
query data
For your first question (all seasons) you can add a column that holds the distinct count of the values in [Season] for each [Driver:Trailer]. Then filter your table on that column, keeping only the 4's. To achieve this, add the following m-code to your script in the Advanced Editor. Change the part after in to #"DistinctCount Season"
#"DistinctCount Season" = Table.Join(#"insert name previous step","Driver:Trailer",
Table.Group(#"insert name previous step", {"Driver:Trailer"},
{{"DistinctCountSeasons", each Table.RowCount(Table.Distinct(_,"Season")),
type number}}),"Driver:Trailer")
Insert the name of your previous step where indicated.
For second question:
You can use a matrix-visual for that in you report. First create a measure:
[AverageTare] = AVERAGE(table'[Tare])
Then put [Season] on Rows and the [AverageTare] on Values. You can create a group (right-click on [Season] in the FIELDS-pain) called [DrySeason], to combine the values for Spring and Summer.
If that doesn't work for you, explore the AVERAGEX function.
EDIT
In excel you can use a pivottable. Put [Season] on Rows and the [AverageTare] on Values. Right-click a value in the pivottable. Select Value Field Setting and choose Average. Then select the Seasons you want to group, right-click and select Group.
EDIT 2
To add a column in the Power Query Editor that holds the average [Tare] for the [Season] in each row, add the following steps to your script in the Avanced Editor:
#"GroupedSeasonAvg" = Table.Group(#"Insert name previous step", {"Season"}, {{"AVG", each List.Average([Tare]), type number}}),
#"JoinOnSeason" = Table.NestedJoin(#"Insert name previous step",{"Season"},GroupedSeasonAvg,{"Season"},"AVGGrouped"),
#"ExtractSeasonAVG" = Table.ExpandTableColumn(JoinOnSeason, "AVGGrouped", {"AVG"}, {"SeasonAVG"})
It works something like this:
"GroupedSeasonAvg" : Creates a table with the avereges for each [Season]
"JoinOnSeason": Creates a new column with tables joining the [Season] value for each row to [Season] in the grouped table.
#"ExtractSeasonAVG": Expand each table and keep only [AVG].

How can I get the values in the Matrix on my SSRS report to repeat?

I know there must be a simple answer to this, but I can't find it.
I have added a couple of textboxes to a Matrix in a BIDS/SSRS report. I've given these textboxes values such as:
=Fields!WEEK1USAGE.Value
It works (after a fashion); when I run the report (either on the Preview tab, or on the Report Server site) I see the first corresponding data value on the report - but only one.
I would think that once a value has been assigned via expressions such as "=Fields!WEEK1USAGE.Value", each value would display (rows would automatically be added).
There must be some property on the Matrix or the textbox that specified this, but I can't see what it might be.
Here is how my report looks (very minimalistic, so far) in the Layout pane:
...and after running, on the Preview tab:
Obviously, I want the report to display as many rows as necessary, not just one. The textboxes do have a "RepeatWith" property, but there description doesn't sound interesting/useful/promising.
I don't see any property on the Matrix control that looks right, either.
I thought maybe the designer was only showing one row of values, and ran the report on the server, too, but there also it just shows the two values.
So what do I need to do to get all the data for a provided field?
Matrices are for display of grouped data and summary information, usually in a horizontally expanding pivot table type of format. Is a matrix really what you are after? Looking at your expression you have =Fields!Week1Usage.Value but in a matrix what I expect to see would be at least =Sum(Fields!Week1Usage.Value) or even better just =Sum(Fields!Usage.Value). Then you would have ProactDescription as your row group and the week as your column group and it would all just work out everything for you, grouping and summing by Proact vertically and expanding the weeks out horizontally.
What seems to be happening is that you have no grouping on rows or columns and no aggregation so it is falling back to the default display which is effectively the First function - it displays the first row of data and as far as the matrix is concerned it has done its job because there is no grouping.
Without knowing your problem or data, I'll make up a scenario that might be what you are doing and discuss how the matrix does the heavy lifting to solve that problem. Let's say you have usage data for multiple Proacts. Each time one is used you record the usage amount and the date and time it is used. It could be used multiple times per day but certainly multiple times in a week. So you might be able to get the times each Proact is used from a table like so:
SELECT ProactDescription, TimeUsed, Usage
FROM ProactUsage
ORDER BY ProactDescription, TimeUsed
In your report you want to show the total weekly usage for each Proact over multiple weeks. Something like this:
Proact Week1 Week2 Week3 ...
Description Usage Usage Usage ...
--------------------------------------------
Anise, Fennel 1 CT 20.00 22.50 16.35 ...
St John's Wort 15.20 33.90 28.25 ...
...
and so on. Using a dataset based on the SQL above we create a matrix and in the row group properties we group on =Fields!ProactDescription.Value and in the column group properties we group on a week expression like =DateDiff(DateInterval.Week, Fields!TimeUsed.Value, Today) and then in the intersection of the row and column we put =Sum(Fields!Usage.Value). To display the header of the column nicely put an expression like
="Week " & DateDiff(DateInterval.Week, Fields!TimeUsed.Value, Today)
The matrix automatically does all the summing by week and product and expands the weeks horizontally for as many as you are reporting. For bonus points you can also put totaling at the end of the columns and the rows to show the total use of that Proact for the period (row total) and total use of all Proacts in that week (column total).

Ordering photos both by timestamp and manually

Context
I'm working on a small web app to store photos. Photos are ordered according to their timestamp (the date they've been taken), and it's working great. Here's a simplified look at the database:
+--------------+-------------------+
| id | timestamp |
+--------------+-------------------+
| 1 | 1000000003 |
| 2 | 1000000000 |
+--------------+-------------------+
Now I'd like to add the possibility to re-order photos. And I can't find a way of doing that without any downsides.
What I did
I first added a column to the table to save a custom order.
+--------------+-------------------+-------------+
| id | timestamp | order |
+--------------+-------------------+-------------+
| 1 | 1000000003 | 1 |
| 2 | 1000000000 | 2 |
+--------------+-------------------+-------------+
First issue: I believe I can't order photos according to two different criteria, because it'd be hard to know which one has to be given precedence.
So I'm ordering them using the order column, and only this one. When I added the order column, I gave each photo a value so that the current order would remain. I now have photos ordered by order, in the same order as when they were ordered by timestamp.
I can now re-order some photos manually, and the other ones will stay where they belong. The first issue has been solved.
But now, I want to add a new photo.
Second issue: I know when the new photo I'm adding has been taken, but my photos aren't ordered by their timestamp anymore. This photo needs to be correctly ordered, thus it needs a correct order value.
This is the issue: a correct order value.
Here are two ways I could handle a new photo:
Give it an order value greater than others. In the previous table, a new photo would be given order = 3. This is obviously a bad idea, since it doesn't take its timestamp into account. A recent photo would still be the last one displayed.
"Insert" it where it belongs, according to its timestamp. Looking at the same table, if the timestamp of the new photo was 1000000002, the new photo would be given order = 2, and the order of every following photo would be increased by 1.
The second solution looks great, except in one case: if the order of the photo #2 had been manually changed to let's say 50, the new photo would have been given order = 50 even though it belongs among the first photos (according to its timestamp).
What I need
What I need is a way of ordering photos according to their timestamp and to their manually-set order.
Maybe you have a solution to the second issue I highlighted, or maybe you're aware of a whole other way to deal with this. Either way, thank you for your help.
At no point in your question do you mention computers or programming languages. This is OK (actually, it's a good approach, get the problem and solution worked out on paper before coding) and here's an answer which also ignores computers and programming languages.
Put all your photos into a shoebox in the order in which you get them.
Now, take three pieces of paper:
On page 1 write the numbers (one to a line) from 1 to N (the number of photos the box can hold). Whenever you put a photo in the box, write its timestamp on the line corresponding to its order in the box.
On page 2 write the timestamp of photo 1 a few lines down. Write a 1 on the same line. For the next photo, write its timestamp in the appropriate place on the paper, leaving as much space above and below as seems necessary for future photo insertions. Write a 2 on the same line. Continue until you run out of space between lines, when you need to copy all the information onto a new version of the page with more space for insertions. The information on this page is the same as the information on page 1, but with the two numbers on each line swapping positions.
On page 3 write the numbers from 1 to N again. As you collect each photo write its number from page 1 (ie its number in the sequence of all photo numbers) in the correct position for your manually-set ordering. You'll probably have to do a lot of rubbing-out and re-writing on this page as you decide that latecomers ought to be inserted high onto this page.
Now you have:
a store for your photos, the shoebox; you should already have realised that you can't store the photos in more than one order at a time;
three indexes (indices if you prefer); the first is fixed and simply assigns a unique sequence number to each photo; it also tells you the timestamp of each photo in the box;
the second index enables you to find the unique sequence number of a photo given its timestamp, and then find the photo in the shoebox;
the third index allows you to order photos as you wish; the first number on each line is the sequence number in the sorted order, the second number is the photo's unique sequence number from the first index.
All of this is an extremely long-winded way of telling you that, since you can't (either in a shoebox or a computerised data store) keep photos in multiple orders simultaneously, you will have to maintain indices for the orderings you wish to use. Those indices point (that's what an index does) from a number to a location in the shoebox, either directly or indirectly.

Resources