The purpose of this project is to give a unique ranking on NBA Players based on how valuable they are for Fantasy Basketball 9-Cat mode. The value is based on the mean value for each category for the top 200 players in that category, and the z-score of the individual players are used to determine their value. Also, this tool will provide a 8-Cat ranking as well, as most players tend to lack at a specific category, so another option to cancel the lowest z-score will also be provided. This tool will be helpful to determine how much value will be gained or lost through trades, waiver wire pickups, or building the team.
​
​Fantasy Basketball 9-Cat:
​Fantasy Basketball 9-Cat is a specific gamemode of Fantasy Basketball. A league is created between friends and people gather NBA Players in their team based on either a draft or an auction. Then, they have weekly matchups within each other where they try to win the 9 statistical categories of basketball which are fg%, ft%, 3pt, points, rebounds, assists, steals, blocks, and turnovers. Specific NBA players are great at a few categories, some are alright at a bunch, and the best players both specialize at certain categories, and don’t hurt at others. Through this tool, a correct determination of the value of players will be created.
​
There will also be an option to change the season, which will be useful to track who the best Fantasy Basketball players were historically as well.

Step 1: Importing Data

Data will be imported from Basketball Reference. https://www.basketball-reference.com/leagues/NBA_1986_totals.html#totals_stats. Then, each of the 9-Cat related statistics are pulled. This tool starts from 1982 for stat-tracking purposes and the introduction of three pointers.
Adjust Year Here:
year="2009";​​​​nbaYears=TextWords[ToString[Range[1982,2024]]];​​basketballSeason=Import["https://www.basketball-reference.com/leagues/NBA_"<>year<>"_totals.html#totals_stats","Data"];​​playerDataBaseRaw=Delete[Flatten[basketballSeason,2][[-28]][[3]],-1];
Remove Blank 3pt%, 2pt%, and ft% that mess with data, multiple teams players played for, players with less than 1000 minutes played, accolades, and non-Fantasy Basketball 9-Cat related categories so that only the following remains: Player Name, Games Played, FG%, 3P, FT%, TRB, AST, STL, BLK, TOV, PTS:
In[]:=
rangeLenPlayers=(Length@playerDataBaseRaw-1)//Range;​​playerDataBase3pt=Table[​​If[playerDataBaseRaw[[n]][[13]]==0,Insert[playerDataBaseRaw[[n]],0,13],playerDataBaseRaw[[n]]],​​{n,rangeLenPlayers}];​​playerDataBase2pt=Table[​​If[playerDataBase3pt[[n]][[16]]==0,Insert[playerDataBase3pt[[n]],0,16],playerDataBase3pt[[n]]],​​{n,rangeLenPlayers}];​​playerDataBaseEmptyFixed=Table[​​If[playerDataBase2pt[[n]][[20]]==0,Insert[playerDataBase2pt[[n]],0,20],playerDataBase2pt[[n]]],​​{n,rangeLenPlayers}];​​positions=GatherBy[rangeLenPlayers,playerDataBaseEmptyFixed[[#]][[1]]&];​​positionsToDeleteRaw=Select[positions,Length[#]>1&];​​positionsToDelete=Partition[Flatten[Table[Delete[positionsToDeleteRaw[[n]],1],{n,Range[Length[positionsToDeleteRaw]]}]],1];​​playerDataBaseTMFixed=Delete[playerDataBaseEmptyFixed,positionsToDelete];​​rangeLenTMFixed=Range[Length[playerDataBaseTMFixed]];​​playerDataBaseLimited=Table[​​If[playerDataBaseTMFixed[[n]][[8]]<=1000,Nothing,playerDataBaseTMFixed[[n]]],{n,rangeLenTMFixed}];​​rangeLenNew=Length@playerDataBaseLimited//Range;​​playerDataBaseNoAcc=Table[Take[playerDataBaseLimited[[n]],31],{n,rangeLenNew}];​​cleanDataSeason=Table[playerDataBaseNoAcc[[n]][[{2,6,11,12,21,24,25,26,27,28,30}]],{n,rangeLenNew}];​​cleanDataAverages=Table[(cleanDataSeason[[n]][[{4,6,7,8,9,10,11}]]/cleanDataSeason[[n]][[2]]),{n,rangeLenNew}]//N;​​cleanDataComplete=Table[​​Join[cleanDataSeason[[n]][[{1,2,3,5}]],cleanDataAverages[[n]]],{n,rangeLenNew}];​​cleanRangeLen=Length@cleanDataComplete//Range;

Step 2: Calculating the Mean and Standard Deviation for Individual Categories

For the 9 categories, the value of the stat is divided by the games played by the player, and all of the values are stored in a list. Then, the mean and the standard deviation of the specific category is found. This is done for each of the 9 categories. The means and standard deviations for each category is listed out.
In[]:=
fgValues=Table[cleanDataComplete[[n]][[3]],{n,cleanRangeLen}];​​fgValuesMean=Mean[fgValues];​​fgValuesSD=StandardDeviation[fgValues];​​​​ftValues=Table[cleanDataComplete[[n]][[4]],{n,cleanRangeLen}];​​ftValuesMean=Mean[ftValues];​​ftValuesSD=StandardDeviation[ftValues];​​​​threeptValues=Table[cleanDataComplete[[n]][[5]],{n,cleanRangeLen}];​​threeptValuesMean=Mean[threeptValues];​​threeptValuesSD=StandardDeviation[threeptValues];​​​​rbdValues=Table[cleanDataComplete[[n]][[6]],{n,cleanRangeLen}];​​rbdValuesMean=Mean[rbdValues];​​rbdValuesSD=StandardDeviation[rbdValues];​​​​astValues=Table[cleanDataComplete[[n]][[7]],{n,cleanRangeLen}];​​astValuesMean=Mean[astValues];​​astValuesSD=StandardDeviation[astValues];​​​​stlValues=Table[cleanDataComplete[[n]][[8]],{n,cleanRangeLen}];​​stlValuesMean=Mean[stlValues];​​stlValuesSD=StandardDeviation[stlValues];​​​​blkValues=Table[cleanDataComplete[[n]][[9]],{n,cleanRangeLen}];​​blkValuesMean=Mean[blkValues];​​blkValuesSD=StandardDeviation[blkValues];​​​​tovValues=Sort[Table[cleanDataComplete[[n]][[10]],{n,cleanRangeLen}]];​​tovValuesMean=Mean[tovValues];​​tovValuesSD=StandardDeviation[tovValues];​​​​ptsValues=Table[cleanDataComplete[[n]][[11]],{n,cleanRangeLen}];​​ptsValuesMean=Mean[ptsValues];​​ptsValuesSD=StandardDeviation[ptsValues];

Step 3: z-Scores for Individual Players are Calculated

Each player who survived the restrictions will be listed out with their stats in 9 categories, divided by the number of games they have played. Afterwards, using the mean and the standard deviation the z score for each individual category of a player is calculated. Here, as turnover is a negative stat and the less turnovers win in Fantasy Basketball 9-Cat, it will be multiplied by -1 for the calculation. Afterward, as the name of the players and their z-scores for each category is found, the z-scores will be added. This will give us the z-score ranking for relevant players in Fantasy Basketball 9-Cat. The result will be displayed in a list, with the z-scores of each players in different categories, and the total z-scores.
categories=Table[Style[{"Name","GP","FG%","FT%","3PT","RBD","AST","STL","BLK","TOV","PTS","Z-SCORE"}[[n]],Bold],{n,Range[12]}];​​zscores=Table[​​Flatten[Join[{​​cleanDataComplete[[n]][[{1,2}]],​​0.5*((cleanDataComplete[[n]][[3]]-fgValuesMean)/fgValuesSD),​​0.5*((cleanDataComplete[[n]][[4]]-ftValuesMean)/ftValuesSD),​​((cleanDataComplete[[n]][[5]]-threeptValuesMean)/threeptValuesSD),​​((cleanDataComplete[[n]][[6]]-rbdValuesMean)/rbdValuesSD),​​((cleanDataComplete[[n]][[7]]-astValuesMean)/astValuesSD),​​((cleanDataComplete[[n]][[8]]-stlValuesMean)/stlValuesSD),​​((cleanDataComplete[[n]][[9]]-blkValuesMean)/blkValuesSD),​​-0.25*((cleanDataComplete[[n]][[10]]-tovValuesMean)/tovValuesSD),​​((cleanDataComplete[[n]][[11]]-ptsValuesMean)/ptsValuesSD)}]],​​{n,cleanRangeLen}];​​zscoresTotal=Reverse[SortBy[Table[Flatten[Join[{​​zscores[[n]],​​Total[zscores[[n]][[{3,4,5,6,7,8,9,10,11}]]]​​}]],{n,cleanRangeLen}],Last]];​​tableSeason=NumberForm[Grid[Partition[Flatten[Join[{categories,zscoresTotal}]],12],Spacings->{3,0.5},Frame->All],4]
See output table here: https://www.wolframcloud.com/obj/8bc10dd9-76ae-42bb-8bc2-95dc742f001d

Step 4: Greatest and Worst 9-Cat Season of All-Time

Best Player Each Season
bestPlayerEachSeason=Table[​​basketballSeason=Import["https://www.basketball-reference.com/leagues/NBA_"<>year<>"_totals.html#totals_stats","Data"];​​playerDataBaseRaw=Delete[Flatten[basketballSeason,2][[-28]][[3]],-1];​​rangeLenPlayers=(Length@playerDataBaseRaw-1)//Range;​​playerDataBase3pt=Table[​​If[playerDataBaseRaw[[n]][[13]]==0,Insert[playerDataBaseRaw[[n]],0,13],playerDataBaseRaw[[n]]],​​{n,rangeLenPlayers}];​​playerDataBase2pt=Table[​​If[playerDataBase3pt[[n]][[16]]==0,Insert[playerDataBase3pt[[n]],0,16],playerDataBase3pt[[n]]],​​{n,rangeLenPlayers}];​​playerDataBaseEmptyFixed=Table[​​If[playerDataBase2pt[[n]][[20]]==0,Insert[playerDataBase2pt[[n]],0,20],playerDataBase2pt[[n]]],​​{n,rangeLenPlayers}];​​positions=GatherBy[rangeLenPlayers,playerDataBaseEmptyFixed[[#]][[1]]&];​​positionsToDeleteRaw=Select[positions,Length[#]>1&];​​positionsToDelete=Partition[Flatten[Table[Delete[positionsToDeleteRaw[[n]],1],{n,Range[Length[positionsToDeleteRaw]]}]],1];​​playerDataBaseTMFixed=Delete[playerDataBaseEmptyFixed,positionsToDelete];​​rangeLenTMFixed=Range[Length[playerDataBaseTMFixed]];​​playerDataBaseLimited=Table[​​If[playerDataBaseTMFixed[[n]][[8]]<=1000,Nothing,playerDataBaseTMFixed[[n]]],{n,rangeLenTMFixed}];​​rangeLenNew=Length@playerDataBaseLimited//Range;​​playerDataBaseNoAcc=Table[Take[playerDataBaseLimited[[n]],31],{n,rangeLenNew}];​​cleanDataSeason=Table[playerDataBaseNoAcc[[n]][[{2,6,11,12,21,24,25,26,27,28,30}]],{n,rangeLenNew}];​​cleanDataAverages=Table[(cleanDataSeason[[n]][[{4,6,7,8,9,10,11}]]/cleanDataSeason[[n]][[2]]),{n,rangeLenNew}]//N;​​cleanDataComplete=Table[​​Join[cleanDataSeason[[n]][[{1,2,3,5}]],cleanDataAverages[[n]]],{n,rangeLenNew}];​​cleanRangeLen=Length@cleanDataComplete//Range;​​fgValues=Table[cleanDataComplete[[n]][[3]],{n,cleanRangeLen}];​​fgValuesMean=Mean[fgValues];​​fgValuesSD=StandardDeviation[fgValues];​​ftValues=Table[cleanDataComplete[[n]][[4]],{n,cleanRangeLen}];​​ftValuesMean=Mean[ftValues];​​ftValuesSD=StandardDeviation[ftValues];​​threeptValues=Table[cleanDataComplete[[n]][[5]],{n,cleanRangeLen}];​​threeptValuesMean=Mean[threeptValues];​​threeptValuesSD=StandardDeviation[threeptValues];​​rbdValues=Table[cleanDataComplete[[n]][[6]],{n,cleanRangeLen}];​​rbdValuesMean=Mean[rbdValues];​​rbdValuesSD=StandardDeviation[rbdValues];​​astValues=Table[cleanDataComplete[[n]][[7]],{n,cleanRangeLen}];​​astValuesMean=Mean[astValues];​​astValuesSD=StandardDeviation[astValues];​​stlValues=Table[cleanDataComplete[[n]][[8]],{n,cleanRangeLen}];​​stlValuesMean=Mean[stlValues];​​stlValuesSD=StandardDeviation[stlValues];​​blkValues=Table[cleanDataComplete[[n]][[9]],{n,cleanRangeLen}];​​blkValuesMean=Mean[blkValues];​​blkValuesSD=StandardDeviation[blkValues];​​tovValues=Sort[Table[cleanDataComplete[[n]][[10]],{n,cleanRangeLen}]];​​tovValuesMean=Mean[tovValues];​​tovValuesSD=StandardDeviation[tovValues];​​ptsValues=Table[cleanDataComplete[[n]][[11]],{n,cleanRangeLen}];​​ptsValuesMean=Mean[ptsValues];​​ptsValuesSD=StandardDeviation[ptsValues];​​zscores=Table[​​Flatten[Join[{year,​​cleanDataComplete[[n]][[{1,2}]],​​0.50*((cleanDataComplete[[n]][[3]]-fgValuesMean)/fgValuesSD),​​0.50*((cleanDataComplete[[n]][[4]]-ftValuesMean)/ftValuesSD),​​((cleanDataComplete[[n]][[5]]-threeptValuesMean)/threeptValuesSD),​​((cleanDataComplete[[n]][[6]]-rbdValuesMean)/rbdValuesSD),​​((cleanDataComplete[[n]][[7]]-astValuesMean)/astValuesSD),​​((cleanDataComplete[[n]][[8]]-stlValuesMean)/stlValuesSD),​​((cleanDataComplete[[n]][[9]]-blkValuesMean)/blkValuesSD),​​-0.25*((cleanDataComplete[[n]][[10]]-tovValuesMean)/tovValuesSD),​​((cleanDataComplete[[n]][[11]]-ptsValuesMean)/ptsValuesSD)}]],​​{n,cleanRangeLen}];​​zscoresTotal=First[Reverse[SortBy[Table[Flatten[Join[{​​zscores[[n]],​​Total[zscores[[n]][[{4,5,6,7,8,9,10,11,12}]]]​​}]],{n,cleanRangeLen}],Last]]],​​{year,nbaYears}];​​categoriesYear=Table[Style[{"Year","Name","GP","FG%","FT%","3PT","RBD","AST","STL","BLK","TOV","PTS","Z-SCORE"}[[n]],Bold],{n,Range[13]}];​​bestEachSeasonTable=NumberForm[Grid[Partition[Flatten[Join[{categoriesYear,Reverse[SortBy[bestPlayerEachSeason,Last]]}]],13],Spacings->{3,0.5},Frame->All],4]
See the output table here: https://www.wolframcloud.com/obj/a463fe1e-6f88-401e-87cc-de43c5aec73c
Worst Player Each Season (Min 1000 Minutes Played)
See the output table here: https://www.wolframcloud.com/obj/37c6e7fb-01eb-4034-858f-7e870ad35110

Step 5: Highest Z-Score for a Stat

Situation for a Single Case
Highest Z-Score for a Single Stat Each Season:
Lowest Z-Score for a Single Stat Each Season:

Step 6: 8-Cat Rankings

For this, as almost every player has a weakness in a category, we are ignoring the worst category for each player and rank them again. This is used by pulling out the data with the player name and the lowest z-score is eliminated. Then, the same process as Step 3 is repeated to calculate the total z-scores and rank players accordingly. For this process, only the player name and their total z-score is listed out.
See output table here: https://www.wolframcloud.com/obj/8a47a45a-e4ef-4113-b1e6-e16978b6a7cf
Stats Credits goes to Basketball Reference

CITE THIS NOTEBOOK

NBA player ranking in Fantasy Basketball 9-Cat mode: mean category scores and Z-score​
by Hakan Bora Yavuzkara​
Wolfram Community, STAFF PICKS, April 17, 2025
​https://community.wolfram.com/groups/-/m/t/3446307