aequitas icon indicating copy to clipboard operation
aequitas copied to clipboard

[Solved] meaning of get_crosstabs() param "score_thresholds", "score"

Open LiFaytheGoblin opened this issue 4 years ago • 4 comments

Hi everyone,

I am analyzing whether an API performs equally well for all groups. So I have a data set where I have continuous "score" values between 0 and 1. They are predictions made by the API for it's result being correct. Then there is the "label_value" which is either 0 (meaning that the API result was incorrect) or 1 (meaning that the API result was correct).

Now if I calculate the true positives for instance for women by hand I do: f_tp = df[((df['sex'] == 'Female') & (df['label_value'] == 1) & (df['score'] >= t))] where t is my treshold of 0.8. So I consider a score of 0.8 to be a prediction of being correct.

I do similar stuff to calculate tn, fp, fn:

f_tn = df[((df['sex'] == 'Female') & (df['label_value'] == 0) & (df['score'] < t))]
f_fp = df[((df['sex'] == 'Female') & (df['label_value'] == 0) & (df['score'] >= t))]
f_fn = df[((df['sex'] == 'Female') & (df['label_value'] == 1) & (df['score'] < t))]

And I obtain:

  • tp = 1185
  • tn = 43
  • fp = 63
  • fn = 104

Now I would like to make these calculations automatically using Aequitas.

g = Group()
xtab, _ = g.get_crosstabs(df, attr_cols=["sex"], score_thresholds= {'score': [0.8]})

In the result, I get values similar to those I got by hand but they are switched around :

  • tp = 104
  • tn = 63
  • fp = 43
  • fn = 1185

Here, tp is what I considered to be fn and fn is what I considered to be tp. What I considered to be tn is fp and what I considered to be fp is tn.

I can't quite wrap my mind around this. What am I missing? I think I might be confusing something? I'd be so happy if you could give me a hint.

Thanks!

LiFaytheGoblin avatar Jul 06 '20 14:07 LiFaytheGoblin

I also looked here:

  • https://dssg.github.io/aequitas/_modules/src/aequitas/group.html#Group.get_crosstabs to find out how to set the score threshold

And here:

  • https://developers.google.com/machine-learning/crash-course/classification/true-false-positive-negative to make sure my definitions for tp, fp, tn, fn are correct

LiFaytheGoblin avatar Jul 06 '20 14:07 LiFaytheGoblin

The numbers I calculated for predicted positive and predicted negative are also switched in Aequitas:

f_pp = df[((df['sex'] == 'Female') & (df['score'] >= t))] # 1248, is pn in Aequitas
f_pn = df[((df['sex'] == 'Female') & (df['score'] < t))] # 147, is pp in Aequitas

So when I say "all those with a score < t are positive", "all those with a score >= t are negative", I get the same results as Aequitas. Same for the other values. So Aequitas basically thinks that when the score is smaller than a threshold it counts as positive and when it is bigger than a threshold it counts as negative. I find this a bit counterintuitive, at least in my use case. Is there any setting or so to change this interpretation?

LiFaytheGoblin avatar Jul 09 '20 07:07 LiFaytheGoblin

  • I assume label_value == 0 is the negative class ("API's classification was not correct"), label_value == 1 is the positive class ("API's classification was correct")
  • Therefore I thought that score means the probability predicted for the classification being correct (probability for positive class)
  • However, the score in Aequitas is actually a score for the classification being incorrect (probability for negative class)

So the solution was to recalculate my score to 1 - score and set the treshold to 1 - original_treshold. Any score smaller than the treshold means "predicted positive", anything bigger than the treshold means "predicted negative".

Now I am getting the correct values for tp etc. from Aequitas.

LiFaytheGoblin avatar Jul 15 '20 13:07 LiFaytheGoblin

And the part of the code that shows how the treshold is interpreted: https://github.com/dssg/aequitas/blob/bb17ce4f305e8a33af3ae18a2a9000555f2e684e/src/aequitas/bias.py#L729 https://github.com/dssg/aequitas/blob/bb17ce4f305e8a33af3ae18a2a9000555f2e684e/src/aequitas/bias.py#L730

LiFaytheGoblin avatar Jul 17 '20 08:07 LiFaytheGoblin