going over the labor optimizer issues with shearer/spinner.
Identified that roles that are based on skill only, or maybe a preference, have really funky distributions that even when running through an ecdf function, throw off the normalization for that role. It takes a really low mean from a raw avg and flips it after converting to ecdf. Giving it a high mean vs 50%~ (the average of each ecdf conversion should be between 75% to 50% (depending on sample size from 2 to any higher number, and mean will aproach 50%), what this means is there was a successful split of ordering the distributions around each other from their median ranked value.
However, with shearer and spinner, I went from an average of .1 to like .8 after ecdf conversion. It was because of a large number of values that were the same, for example shearer role that is based on shearer skill. Out of like 90 dwarves, only a few had any skill, and some had a great deal of skill in, and majority had no skill (funny, we discovered a bug with negative raw role values and correct it. So the distribution was lopsided (biased), but after ecdf conversion it turned all those low values into a high 84%, and the rest of the values were above these.
We figured the way the original raw distributions are, are infinitely different based on the variables involved with a role. So direct comparison between two roles is impossible without some normalization, because each distribution has it's own unique shape (thanks statistics) and signature. So we learn that the raw values don't have direct comparison with each other (unless you have two similar roles, see how this is [possibly] achievable below, this is essentially how the old optimizer worked, which is a fringe case use and is not recommended using all roles, instead, it's recommended you make a custom optimization job with just those two roles, and run it separate, or just sort the roles next to each other using their raw values in a grid view).
The inverted skewed distributions of Shearer, and Spinner clearly was unacceptable, what would be acceptable is scaling it using min max method, because it's raw median value of 0 (most likely, or whatever value, was artificially bloated to above 50%). It's not a direct representation of the population, due to a large # (i.e. the median skill or value of that role is probably 0) being at the low amount, 0 is counted as 84% of the values. So just starting, I had 84% of 90 dwarf's that had a high rating. Clearly this had to be fixed.
But how?
Well, we decided on error correction. We decided that if a ecdf role (after conversion from raw to ecdf) had a mean - .5 (ie -50%) that was off by more than 27.5%. In reality, means are around 50%, but as the sample size goes
down, the error % can
go up to as high as an average of .75; so we decided to manipulate the ecdf rating on the lowest possible sample. 2 variables average is 50% + 100% ecdf rating. So it comes out to be 150% / 2 = 75%. If Min=Max, then both values are set to 100%
So I tested what the mean of the highest avg of a skewed distribution by running a few samples through ecdf, I ran a 1,1,2 and a 1,1,1,2, I measured their ecdf mean's, and noticed the smaller one 1,1,2 was like 77.777% average, difference of 27.777%
I believe the 1,1,1,2 was an average ~85%, which has a difference from 50% as 35%
So I set our threshold to 27.5%.
Which means even if I have 2 dwarf's that I run an optimization plan on, it will still be able to correctly identify skewed distributions.
We also stated that if min=max (then all values are set to 100%).
hrmm, just realized something...
NOTE TO SELF: If Min = Max && Min = 0; all %'s equal 0%
We identified skewed distributions by one more step, we checked if the frequency of of each value within the distribution. We can compare it's ecdf rating to the role and identify the frequency that # appears in the distribution.
By a rule, if a distribution has such a number, an ecdf conversion will boost that value by whatever % of the distribution it takes up (i.e. count of that # compared to the set dwarves [being selected by the labor optimization plan] within the role, example. 5 dwarf's, and 3/5 dwarve's have 0 labor in the skill. However 2 have some value. Automatically, 0 is going to start at 3/5% 60%. This is a problem when you have even more with no skill in it.
So, we also check the % that a value is repeated within a role (i.e. dataset of # dwarf's). If that value
exceeds 50%, then we run a simple (x-min)/(max-min) conversion instead, which preserves the low value of the 0% as well as the value of the highest %'s (i.e. 100%).
So now, every role in the optimizer is equally distributed next to each other. This means that almost each value directly relates to another correlated value within the grid you see. From highest to lowest. There percent's are back adjusted so they are all within ~.0001%. They are offset by their ranking in highest to lowest raw outputted average (i.e. whatever the role editor spits out).
In other words:
The pre-adustment set's all priorities next to each other and sorts them by order from highest to lowest average. Then a formula is ran to derive a straight line slope that runs through the ordinal ranking of the of the order of the distributions that will serve as priority. How steep this line is is Infinitesimally small. It's sole purpose is to be as minimal as possible but to set the ordinal ranking of the distributions. Because that will give that distribution a clear advantage over all the other distributions on a line by line basis. It will give that distribution the opportunity to directly compare to all values left and right of itself (by the priorities set by the means of each role compared to each other).
The beauty is, it's dynamic to every selection you make within the optimizer, and now can detect skewed distributions and compare them appropriately. Some people might not like this, as a value of 1, 2, 5, 100, 1200 really translate to a straight line % increase from 20% 40% 60% 80% and 100% respectively, another role might be a tighter distribution like 1, 2, 3, 4, 5.
You also have control from one role to the other in adjusting priorities. Which means you can move a distribution up against another distribution when going from top value to lowest value in comparing distributions . Think of it as altering the median location of one flat distribution against another flash distribution, it affectively factors the distribution values higher or lower than the other values, so small values are recommended; however really strong values won't hurt anything. The beauty is, the auto recommended values work best JUST IN CASE raw distribution average does matter when compared to another distribution (such as when you had all your melee_dwarf's at a lower raw average rating than say farming. However, maybe now you have awesome fighters who far exceed your farmer's. So what is the difference between a run using equal priorities or those adjusted by the distributions raw mean? It slightly offsets one role next to another, but nothing to mess up the direct comparison between all roles. So each distribution is aligned next to each other when this optimizer runs. Before, roles that produced high raw % values (which are arbitrary) could potentially always be selected first through no fault of their own. I think we really hit on equal representation of the data.
It will assign the role with the highest ordinal value first, but compared to each other left to right, down the distribution as long as it has roles it can fill up. More or less, actually all the values are merged together in a superlist, and compared from highest to lowest, the Infinitesimally small priority adjustments applies a signature to the distribution to inform it that it has a lower mean than another distribution by just a little, and therefore and equally adjusted rating between each value (aka 90 dwarf's = 1/90% difference between dwarf's, * Infinitesimally small priority rating). Then it starts from the highest rating and starts assigning downwards
Some would argue that the optimizer ignores the differences in range.
Well, I would argue that it preserves the ordinal ranking between roles, and is truly representative of the population you have at hand. For one to say it promotes one over the other role by too little when it needs to be more, I argue it's truly representative of the dataset you have available. I.e. it's an empirical test of each distribution's density function, so it can be argued it's truly representative of your population. It treats every value as equally attainable, so it truly matches the curve density function of the current distribution. So yes, it does recognize differences between roles, a different understanding needs to be made that it measures the density function of each distribution curve of your dataset (which is based on role editor constructed multivariable.
Therefore you flatten the distribution, account for skewed distributions, and preserve their ordinal values, and not promote 0%, and compare them left to right like reading a newspaper.
I believe Splinterz is also going to include the ability to compare two roles within the grid view by their raw role rating. I thought this was a good idea so a player can retain the ability to compare two very similar roles next to each other side by side. Examples are: armorsmith, weaponsmith, speardwarf, sworddwarf, etc. Where maybe the only difference between two roles is maybe one additional variable (such as a skill, or preferences). This will allow a player a little more control over proper comparisons between two roles.
There's also a log that can be outputted (maybe only in beta?) that shows the outputted values of all these calculations.
You can check source of how it operates here.
https://github.com/splintermind/Dwarf-Therapist/blob/master/src/laboroptimizer.cpp#L147https://raw.githubusercontent.com/splintermind/Dwarf-Therapist/master/src/rolestats.cpp