A p5.js version of Ben Fry's Salary vs. Performance, originally written in Processing and featured in ch. 5 of Visualizing Data: Exploring and Explaining Data with the Processing Environment (Ben Fry, O'Reilly Press, 2008). Original MLB datasets can be found in Fry's online supplemental material.
Last active
August 29, 2015 14:05
-
-
Save mattbrehmer/4473009667985e1f893b to your computer and use it in GitHub Desktop.
p5.js Salary vs. Performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<script src="//cdn.jsdelivr.net/p5.js/0.3.5/p5.min.js"></script> | |
<script type="text/javascript" src="salaryper.js"></script> | |
<script type="text/javascript" src="RankedList.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function RankedList (count, ascending) { | |
this.count = count; | |
this.values = []; | |
this.titles = []; | |
this.order = []; | |
this.rank = []; | |
this.minValue; | |
this.maxValue; | |
this.ascending = ascending; | |
}; | |
RankedList.prototype.getCount = function() { | |
return this.count; | |
}; | |
RankedList.prototype.getValue = function(index) { | |
return this.values[index]; | |
}; | |
RankedList.prototype.getMinValue = function() { | |
return this.minValue; | |
}; | |
RankedList.prototype.getMaxValue = function() { | |
return this.maxValue; | |
}; | |
RankedList.prototype.getTitle = function(index) { | |
return this.titles[index]; | |
}; | |
RankedList.prototype.getRank = function(index) { | |
return this.rank[index]; | |
}; | |
RankedList.prototype.getInfo = function() { | |
if (this.ascending) | |
var ascFlag = "true"; | |
else | |
var ascFlag = "false"; | |
return "count: " + this.count + "; ascending: " + ascFlag + " "; | |
}; | |
// Sort the data and calculate min/max values | |
RankedList.prototype.update = function() { | |
// Set up an initial order to be sorted | |
for (var i = 0; i < this.count; i++) { | |
this.order[i] = i; | |
} | |
this.sortList(0, this.count-1); | |
// Assign rankings based on the order after sorting | |
for (var i = 0; i < this.count; i++) { | |
this.rank[this.order[i]] = i; | |
} | |
// Calculate minimum and maximum values | |
this.minValue = Math.min.apply(Math,this.values); | |
this.maxValue = Math.max.apply(Math,this.values); | |
}; | |
RankedList.prototype.sortList = function(left, right) { | |
var pivotIndex = Math.floor((left+right)/2); | |
this.swap(pivotIndex, right); | |
var k = this.partition(left-1, right); | |
this.swap(k, right); | |
if((k-left) > 1) | |
this.sortList(left, k-1); | |
if((right-k) > 1) | |
this.sortList(k+1, right); | |
}; | |
RankedList.prototype.partition = function(left, right) { | |
var pivot = right; | |
do { | |
while (this.compare(++left, pivot) < 0); | |
while ((right != 0) && (this.compare(--right, pivot) > 0)); | |
this.swap(left,right); | |
} while (left < right); | |
this.swap(left,right); | |
return left; | |
}; | |
RankedList.prototype.compare = function(a, b) { | |
if (this.ascending) | |
return this.values[this.order[a]] - this.values[this.order[b]]; | |
else | |
return this.values[this.order[b]] - this.values[this.order[a]]; | |
}; | |
RankedList.prototype.swap = function(a, b) { | |
var temp = this.order[a]; | |
this.order[a] = this.order[b]; | |
this.order[b] = temp; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nyy | 189639045 | |
---|---|---|
bos | 143026214 | |
nym | 115231663 | |
ana | 109251333 | |
cws | 108671833 | |
la | 108454524 | |
sea | 106460833 | |
chc | 99670332 | |
det | 95180369 | |
bal | 93554808 | |
stl | 90286823 | |
sf | 90219056 | |
phi | 89428213 | |
hou | 87759000 | |
atl | 87290833 | |
tor | 81942800 | |
oak | 79366940 | |
min | 71439500 | |
mil | 70986500 | |
cin | 68904980 | |
tex | 68318675 | |
kc | 67116500 | |
cle | 61673267 | |
sd | 58110567 | |
col | 54424000 | |
ari | 52067546 | |
pit | 38537833 | |
was | 37347500 | |
fla | 30507000 | |
tb | 24123500 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var teamCount = 30, | |
teams = [], | |
salaries = [], | |
standings = [], | |
teamCodes = [], | |
teamNames = [], | |
teamIndices = [], | |
salaryList, | |
standingsList, | |
logos = [], | |
logoWidth, | |
logoHeight, | |
ROW_HEIGHT = 23, | |
HALF_ROW_HEIGHT = ROW_HEIGHT / 2.0, | |
SIDE_PADDING = 30; | |
function preload() { | |
teams = loadStrings("teams.csv"); | |
salaries = loadStrings("salaries.csv"); | |
standings = loadStrings("standings.csv"); | |
}; | |
function setupTeams() { | |
teamCount = teams.length; | |
for (var i = 0; i < teamCount; i++) { | |
var pieces = split(teams[i],","); | |
teamCodes[i] = pieces[0]; | |
teamNames[i] = pieces[1]; | |
teamIndices.push([teamCodes[i], i]); | |
} | |
}; | |
function setupSalaries() { | |
salaryList = new RankedList(teamCount,false); | |
for (var i = 0; i < teamCount; i++) { | |
var pieces = split(salaries[i],","); | |
//first column is team's 2- or 3-digit team code | |
var index = teamIndex(pieces[0]); | |
//second column is salary as a number | |
salaryList.values[index] = pieces[1] / 1; | |
//make the title in the format of $NN,NNN,NNN | |
var salary = salaryList.values[index]; | |
salaryList.titles[index] = "$" + nfc(salary); | |
} | |
salaryList.update(); | |
}; | |
function setupStandings() { | |
standingsList = new RankedList(teamCount,false); | |
for (var i = 0; i < teamCount; i++) { | |
var pieces = split(standings[i],","); | |
var index = teamIndex(pieces[0]); | |
var wins = +pieces[1]; | |
var losses = +pieces[2]; | |
standingsList.values[index] = wins / (wins + losses); | |
standingsList.titles[index] = wins + "\u2013" + losses; | |
} | |
standingsList.update(); | |
}; | |
function setupLogos() { | |
for (var i = 0; i < teamCount; i++) { | |
logos[i] = loadImage("thumb_" + teamCodes[i] + ".gif"); | |
} | |
}; | |
function setup() { | |
createCanvas(480,750); | |
setupTeams(); | |
setupSalaries(); | |
setupStandings(); | |
setupLogos(); | |
textFont("Georgia"); | |
textSize(11); | |
}; | |
function teamIndex(teamCode) { | |
var index = 0; | |
var found = false; | |
while (!found && index < teamIndices.length) { | |
if (teamIndices[index][0] == teamCode) | |
found = true; | |
else | |
index++; | |
} | |
return index; | |
}; | |
function draw() { | |
background(255); | |
smooth(); | |
translate(SIDE_PADDING, SIDE_PADDING); | |
var leftX = 160, | |
rightX = 335; | |
logoWidth = logos[0].width / 2.0; | |
logoHeight = logos[0].height / 2.0; | |
for (var i = 0; i < teamCount; i++) { | |
var standingsY = standingsList.getRank(i)*ROW_HEIGHT + HALF_ROW_HEIGHT; | |
fill(128); | |
strokeWeight(0); | |
image(logos[i], 0, standingsY - logoHeight / 2, logoWidth / 1.25, logoHeight / 1.25); | |
textAlign(LEFT, CENTER); | |
text(teamNames[i], 28, standingsY); | |
textAlign(RIGHT, CENTER); | |
text(standingsList.getTitle(i), leftX - 10, standingsY) | |
var salaryY = salaryList.getRank(i)*ROW_HEIGHT + HALF_ROW_HEIGHT; | |
if (salaryY >= standingsY) | |
stroke(33,85,156); // blue for positive / equal difference | |
else | |
stroke(206,0,82); | |
var weight = map(salaryList.getValue(i), | |
salaryList.getMinValue(), | |
salaryList.getMaxValue(), | |
0.25, 6); | |
strokeWeight(weight); | |
line(leftX, standingsY, rightX, salaryY); | |
fill(128); | |
textAlign(LEFT, CENTER); | |
strokeWeight(0); | |
text(salaryList.getTitle(i), rightX + 10, salaryY); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bos | 6 | 4 | |
---|---|---|---|
tor | 7 | 5 | |
bal | 6 | 6 | |
nyy | 5 | 6 | |
tb | 5 | 7 | |
sea | 5 | 3 | |
ana | 6 | 6 | |
oak | 6 | 7 | |
tex | 5 | 7 | |
cle | 6 | 3 | |
det | 7 | 5 | |
min | 7 | 5 | |
cws | 5 | 6 | |
kc | 3 | 9 | |
atl | 8 | 3 | |
nym | 7 | 4 | |
fla | 6 | 5 | |
phi | 3 | 8 | |
was | 3 | 9 | |
ari | 9 | 4 | |
la | 8 | 4 | |
sd | 7 | 5 | |
col | 5 | 7 | |
sf | 3 | 7 | |
cin | 7 | 5 | |
mil | 6 | 5 | |
stl | 6 | 5 | |
hou | 4 | 6 | |
pit | 4 | 6 | |
chc | 4 | 7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nyy | NY Yankees | |
---|---|---|
bos | Boston | |
nym | New York Mets | |
ana | LA Angels | |
cws | Chi White Sox | |
la | LA Dodgers | |
sea | Seattle | |
chc | Chi Cubs | |
det | Detroit | |
bal | Baltimore | |
stl | St. Louis | |
sf | San Francisco | |
phi | Philadelphia | |
hou | Houston | |
atl | Atlanta | |
tor | Toronto | |
oak | Oakland | |
min | Minnesota | |
mil | Milwaukee | |
cin | Cincinnati | |
tex | Texas | |
kc | Kansas City | |
cle | Cleveland | |
sd | San Diego | |
col | Colorado | |
ari | Arizona | |
pit | Pittsburgh | |
was | Washington | |
fla | Florida | |
tb | Tampa Bay |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment