Equity formula for the Data Science Toolbox Group Assessments

The equity formula operates on the following principles:

  1. It should be unresponsive around 1, so that small deviations result in little change to marks.
  2. It should prevent the mean of all scores from increasing much, to reduce gaming the system.
  3. It must asymptote to prevent extreme multipliers leading to inappropriate grade classes.
  4. It must operate without complex interplay between marks, and be tolerant of a range of group sizes.

It is based on a model used in the Business School which uses the same basic structure.

The equity share formula uses the following curve to map a claimed proportion of contribution (first variable) into a score multiplier (second variable).

sharecurvedata=matrix(c(0,0,
                0.8,0.7,
                0.88,0.98,
                1.12,1.02,
                1.5,1.2,
                10,1.5),byrow=T,ncol=2)

This is accessed via the interpolated curve formula:

sharecurve=function(s,tmodel=sharecurvedata){ # s as a ratio of the expected work per person
    tmp=c(0,0)
    i=2
    while(s>=tmodel[i-1,1]){
        tmp[1]=min(tmodel[i,1],s)
        slope=(tmodel[i,2]-tmodel[i-1,2])/ (tmodel[i,1]-tmodel[i-1,1])
        distance= (tmp[1]-tmodel[i-1,1])
        tmp[2]=tmp[2] +distance*slope
        i=i+1
    }
    tmp[2]
}

The curve scales a mark by the proportional contribution:

sprop=seq(0,2,by=0.05)
plot(sprop,sapply(sprop,sharecurve),type="b",
     main="Equity formula for Data Science Toolbox",
     xlab="Claimed equity proportion as a fraction of the expected",
     ylab="Mark multiplier")
abline(a=0,b=1)
text(0.95,1.05,"Unresponsive around 1",adj=1)
text(1.5,1.1,"Asymptote",adj=0)
text(0.9,0.7,"Typically worse to unevenly divide",adj=0)

This curve can be accessed using a simple interface called shareformula for the marks x and the share s. Note that marks are thresholded, though in practice it would be unusual to award marks that could exceed these limits. The function is:

shareformula=function(x,s,tmodel=sharecurvedata){
    ss=s/sum(s)*length(s)
    ss=sapply(ss,sharecurve,tmodel=tmodel)
    
    xx=x*ss
    xx[xx<0]=0
    xx[xx>100]=100
    xx
}

Usage

Use the function shareformula to turn a mark into a scaled mark. Input raw scores as the first variable x and the share as the second variable s. Use sharecurve to obtain a proportional scaling without any mark truncation. Here is an example with three members, one of whom contributed 40% whereas the others only 30%:

sharecurve((4/10)/(1/3))
[1] 1.057895
shareformula(1,c(40,30,30))
[1] 1.0578947 0.9833333 0.9833333

Some examples:

Note that because of the limitation to operate on the relative contribution, without knowledge of the number of members in the group, the formula can be sensitive if used in large chunks (e.g. 5% intervals) at large group sizes. This is because a 5% change of contribution is a larger proportion when there are 4 or 5 members than when there are 2 or 3. For example, the difference between a 15% and 25% equity in a 5 person project represents an entire half-person difference in contribution, so is heavily reweighted, but the difference between 45% and 55% in a 2 person project is negligible.

Examples with a varying number of group members and a base mark of 70%.

examples=list(
  c(45,55),
  c(40,60),
  c(40,30,30),
  c(50,25,25),
  c(30,25,25,20),
  c(30,25,23,22),
  c(25,20,20,20,15),
  c(25,20,20,18,17)
  )
printexample=function(ex){
  res=shareformula(70,ex)
  print(paste("Example share: (",
              paste(ex,collapse=","),
              ") of a mark of 70 results in a scaled mark of: (",
              paste(round(res,digits=0),collapse=","),
              ")"))
  invisible(res)
}
invisible(sapply(examples,printexample))
[1] "Example share: ( 45,55 ) of a mark of 70 results in a scaled mark of: ( 69,71 )"
[1] "Example share: ( 40,60 ) of a mark of 70 results in a scaled mark of: ( 49,74 )"
[1] "Example share: ( 40,30,30 ) of a mark of 70 results in a scaled mark of: ( 74,69,69 )"
[1] "Example share: ( 50,25,25 ) of a mark of 70 results in a scaled mark of: ( 84,46,46 )"
[1] "Example share: ( 30,25,25,20 ) of a mark of 70 results in a scaled mark of: ( 74,70,70,49 )"
[1] "Example share: ( 30,25,23,22 ) of a mark of 70 results in a scaled mark of: ( 74,70,69,69 )"
[1] "Example share: ( 25,20,20,20,15 ) of a mark of 70 results in a scaled mark of: ( 76,70,70,70,46 )"
[1] "Example share: ( 25,20,20,18,17 ) of a mark of 70 results in a scaled mark of: ( 76,70,70,69,61 )"
LS0tCnRpdGxlOiAiRGF0YSBTY2llbmNlIFRvb2xib3ggRXF1aXR5IE1vZGVsIgpvdXRwdXQ6CiAgaHRtbF9mcmFnbWVudDoKICAgIHNlbGZfY29udGFpbmVkOiBmYWxzZQotLS0KCiMgRXF1aXR5IGZvcm11bGEgZm9yIHRoZSBEYXRhIFNjaWVuY2UgVG9vbGJveCBHcm91cCBBc3Nlc3NtZW50cwoKVGhlIGVxdWl0eSBmb3JtdWxhIG9wZXJhdGVzIG9uIHRoZSBmb2xsb3dpbmcgcHJpbmNpcGxlczoKCjEuIEl0IHNob3VsZCBiZSB1bnJlc3BvbnNpdmUgYXJvdW5kIDEsIHNvIHRoYXQgc21hbGwgZGV2aWF0aW9ucyByZXN1bHQgaW4gbGl0dGxlIGNoYW5nZSB0byBtYXJrcy4KMi4gSXQgc2hvdWxkIHByZXZlbnQgdGhlIG1lYW4gb2YgYWxsIHNjb3JlcyBmcm9tIGluY3JlYXNpbmcgbXVjaCwgdG8gcmVkdWNlIGdhbWluZyB0aGUgc3lzdGVtLgozLiBJdCBtdXN0IGFzeW1wdG90ZSB0byBwcmV2ZW50IGV4dHJlbWUgbXVsdGlwbGllcnMgbGVhZGluZyB0byBpbmFwcHJvcHJpYXRlIGdyYWRlIGNsYXNzZXMuCjQuIEl0IG11c3Qgb3BlcmF0ZSB3aXRob3V0IGNvbXBsZXggaW50ZXJwbGF5IGJldHdlZW4gbWFya3MsIGFuZCBiZSB0b2xlcmFudCBvZiBhIHJhbmdlIG9mIGdyb3VwIHNpemVzLgoKSXQgaXMgYmFzZWQgb24gYSBtb2RlbCB1c2VkIGluIHRoZSBCdXNpbmVzcyBTY2hvb2wgd2hpY2ggdXNlcyB0aGUgc2FtZSBiYXNpYyBzdHJ1Y3R1cmUuCgpUaGUgZXF1aXR5IHNoYXJlIGZvcm11bGEgdXNlcyB0aGUgZm9sbG93aW5nIGN1cnZlIHRvIG1hcCBhIGNsYWltZWQgcHJvcG9ydGlvbiBvZiBjb250cmlidXRpb24gKGZpcnN0IHZhcmlhYmxlKSBpbnRvIGEgc2NvcmUgbXVsdGlwbGllciAoc2Vjb25kIHZhcmlhYmxlKS4KCmBgYHtyfQpzaGFyZWN1cnZlZGF0YT1tYXRyaXgoYygwLDAsCiAgICAgICAgICAgICAgICAwLjgsMC43LAogICAgICAgICAgICAgICAgMC44OCwwLjk4LAogICAgICAgICAgICAgICAgMS4xMiwxLjAyLAogICAgICAgICAgICAgICAgMS41LDEuMiwKICAgICAgICAgICAgICAgIDEwLDEuNSksYnlyb3c9VCxuY29sPTIpCmBgYAoKVGhpcyBpcyBhY2Nlc3NlZCB2aWEgdGhlIGludGVycG9sYXRlZCBjdXJ2ZSBmb3JtdWxhOgpgYGB7cn0Kc2hhcmVjdXJ2ZT1mdW5jdGlvbihzLHRtb2RlbD1zaGFyZWN1cnZlZGF0YSl7ICMgcyBhcyBhIHJhdGlvIG9mIHRoZSBleHBlY3RlZCB3b3JrIHBlciBwZXJzb24KICAgIHRtcD1jKDAsMCkKICAgIGk9MgogICAgd2hpbGUocz49dG1vZGVsW2ktMSwxXSl7CiAgICAgICAgdG1wWzFdPW1pbih0bW9kZWxbaSwxXSxzKQogICAgICAgIHNsb3BlPSh0bW9kZWxbaSwyXS10bW9kZWxbaS0xLDJdKS8gKHRtb2RlbFtpLDFdLXRtb2RlbFtpLTEsMV0pCiAgICAgICAgZGlzdGFuY2U9ICh0bXBbMV0tdG1vZGVsW2ktMSwxXSkKICAgICAgICB0bXBbMl09dG1wWzJdICtkaXN0YW5jZSpzbG9wZQogICAgICAgIGk9aSsxCiAgICB9CiAgICB0bXBbMl0KfQpgYGAKClRoZSBjdXJ2ZSBzY2FsZXMgYSBtYXJrIGJ5IHRoZSBwcm9wb3J0aW9uYWwgY29udHJpYnV0aW9uOgpgYGB7cn0Kc3Byb3A9c2VxKDAsMixieT0wLjA1KQpwbG90KHNwcm9wLHNhcHBseShzcHJvcCxzaGFyZWN1cnZlKSx0eXBlPSJiIiwKICAgICBtYWluPSJFcXVpdHkgZm9ybXVsYSBmb3IgRGF0YSBTY2llbmNlIFRvb2xib3giLAogICAgIHhsYWI9IkNsYWltZWQgZXF1aXR5IHByb3BvcnRpb24gYXMgYSBmcmFjdGlvbiBvZiB0aGUgZXhwZWN0ZWQiLAogICAgIHlsYWI9Ik1hcmsgbXVsdGlwbGllciIpCmFibGluZShhPTAsYj0xKQp0ZXh0KDAuOTUsMS4wNSwiVW5yZXNwb25zaXZlIGFyb3VuZCAxIixhZGo9MSkKdGV4dCgxLjUsMS4xLCJBc3ltcHRvdGUiLGFkaj0wKQp0ZXh0KDAuOSwwLjcsIlR5cGljYWxseSB3b3JzZSB0byB1bmV2ZW5seSBkaXZpZGUiLGFkaj0wKQpgYGAKClRoaXMgY3VydmUgY2FuIGJlIGFjY2Vzc2VkIHVzaW5nIGEgc2ltcGxlIGludGVyZmFjZSBjYWxsZWQgYHNoYXJlZm9ybXVsYWAgZm9yIHRoZSBtYXJrcyBgeGAgYW5kIHRoZSBzaGFyZSBgc2AuIE5vdGUgdGhhdCBtYXJrcyBhcmUgdGhyZXNob2xkZWQsIHRob3VnaCBpbiBwcmFjdGljZSBpdCB3b3VsZCBiZSB1bnVzdWFsIHRvIGF3YXJkIG1hcmtzIHRoYXQgY291bGQgZXhjZWVkIHRoZXNlIGxpbWl0cy4gVGhlIGZ1bmN0aW9uIGlzOgpgYGB7cn0Kc2hhcmVmb3JtdWxhPWZ1bmN0aW9uKHgscyx0bW9kZWw9c2hhcmVjdXJ2ZWRhdGEpewogICAgc3M9cy9zdW0ocykqbGVuZ3RoKHMpCiAgICBzcz1zYXBwbHkoc3Msc2hhcmVjdXJ2ZSx0bW9kZWw9dG1vZGVsKQogICAgCiAgICB4eD14KnNzCiAgICB4eFt4eDwwXT0wCiAgICB4eFt4eD4xMDBdPTEwMAogICAgeHgKfQpgYGAKCiMjIFVzYWdlCgpVc2UgdGhlIGZ1bmN0aW9uIGBzaGFyZWZvcm11bGFgIHRvIHR1cm4gYSBtYXJrIGludG8gYSBzY2FsZWQgbWFyay4gSW5wdXQgcmF3IHNjb3JlcyBhcyB0aGUgZmlyc3QgdmFyaWFibGUgYHhgIGFuZCB0aGUgc2hhcmUgYXMgdGhlIHNlY29uZCB2YXJpYWJsZSBgc2AuIFVzZSBgc2hhcmVjdXJ2ZWAgdG8gb2J0YWluIGEgcHJvcG9ydGlvbmFsIHNjYWxpbmcgd2l0aG91dCBhbnkgbWFyayB0cnVuY2F0aW9uLiBIZXJlIGlzIGFuIGV4YW1wbGUgd2l0aCB0aHJlZSBtZW1iZXJzLCBvbmUgb2Ygd2hvbSBjb250cmlidXRlZCA0MCUgd2hlcmVhcyB0aGUgb3RoZXJzIG9ubHkgMzAlOgpgYGB7cn0Kc2hhcmVjdXJ2ZSgoNC8xMCkvKDEvMykpCnNoYXJlZm9ybXVsYSgxLGMoNDAsMzAsMzApKQpgYGAKCiMjIFNvbWUgZXhhbXBsZXM6CgpOb3RlIHRoYXQgYmVjYXVzZSBvZiB0aGUgbGltaXRhdGlvbiB0byBvcGVyYXRlIG9uIHRoZSByZWxhdGl2ZSBjb250cmlidXRpb24sIHdpdGhvdXQga25vd2xlZGdlIG9mIHRoZSBudW1iZXIgb2YgbWVtYmVycyBpbiB0aGUgZ3JvdXAsIHRoZSBmb3JtdWxhIGNhbiBiZSBzZW5zaXRpdmUgaWYgdXNlZCBpbiBsYXJnZSBjaHVua3MgKGUuZy4gNSUgaW50ZXJ2YWxzKSBhdCBsYXJnZSBncm91cCBzaXplcy4gVGhpcyBpcyBiZWNhdXNlIGEgNSUgY2hhbmdlIG9mIGNvbnRyaWJ1dGlvbiBpcyBhIGxhcmdlciBwcm9wb3J0aW9uIHdoZW4gdGhlcmUgYXJlIDQgb3IgNSBtZW1iZXJzIHRoYW4gd2hlbiB0aGVyZSBhcmUgMiBvciAzLiBGb3IgZXhhbXBsZSwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIDE1JSBhbmQgMjUlIGVxdWl0eSBpbiBhIDUgcGVyc29uIHByb2plY3QgcmVwcmVzZW50cyBhbiBlbnRpcmUgaGFsZi1wZXJzb24gZGlmZmVyZW5jZSBpbiBjb250cmlidXRpb24sIHNvIGlzIGhlYXZpbHkgcmV3ZWlnaHRlZCwgYnV0IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gNDUlIGFuZCA1NSUgaW4gYSAyIHBlcnNvbiBwcm9qZWN0IGlzIG5lZ2xpZ2libGUuCgpFeGFtcGxlcyB3aXRoIGEgdmFyeWluZyBudW1iZXIgb2YgZ3JvdXAgbWVtYmVycyBhbmQgYSBiYXNlIG1hcmsgb2YgNzAlLgpgYGB7cn0KZXhhbXBsZXM9bGlzdCgKICBjKDQ1LDU1KSwKICBjKDQwLDYwKSwKICBjKDQwLDMwLDMwKSwKICBjKDUwLDI1LDI1KSwKICBjKDMwLDI1LDI1LDIwKSwKICBjKDMwLDI1LDIzLDIyKSwKICBjKDI1LDIwLDIwLDIwLDE1KSwKICBjKDI1LDIwLDIwLDE4LDE3KQogICkKcHJpbnRleGFtcGxlPWZ1bmN0aW9uKGV4KXsKICByZXM9c2hhcmVmb3JtdWxhKDcwLGV4KQogIHByaW50KHBhc3RlKCJFeGFtcGxlIHNoYXJlOiAoIiwKICAgICAgICAgICAgICBwYXN0ZShleCxjb2xsYXBzZT0iLCIpLAogICAgICAgICAgICAgICIpIG9mIGEgbWFyayBvZiA3MCByZXN1bHRzIGluIGEgc2NhbGVkIG1hcmsgb2Y6ICgiLAogICAgICAgICAgICAgIHBhc3RlKHJvdW5kKHJlcyxkaWdpdHM9MCksY29sbGFwc2U9IiwiKSwKICAgICAgICAgICAgICAiKSIpKQogIGludmlzaWJsZShyZXMpCn0KaW52aXNpYmxlKHNhcHBseShleGFtcGxlcyxwcmludGV4YW1wbGUpKQpgYGA=