class Record { String condition; int age; float personalCost; float insurerCost; int count; Record(String condition, int age, float personalCost, float insurerCost, int count) { this.condition = condition; this.age = age; this.personalCost = personalCost; this.insurerCost = insurerCost; this.count = count; } } Record[][] records; String[] conditions; PFont font, titleFont; int TOTAL_INSURER = 0; int TOTAL_PERSONAL = 1; int TOTAL_COUNT = 2; int AVG_INSURER = 3; int AVG_PERSONAL = 4; int[] metrics = { TOTAL_INSURER, TOTAL_PERSONAL, TOTAL_COUNT, AVG_INSURER, AVG_PERSONAL }; int chosenMetric = TOTAL_INSURER; float maxTotalInsurerCost = 0; float maxTotalPersonalCost = 0; float maxTotalCount = 0; float maxAvgInsurerCost = 0; float maxAvgPersonalCost = 0; color[] colors; float maxCost; void setup() { size(500, 375); smooth(); // qualitative colors courtesy Cythia Brewer under an Apache style license // borrowed from http://soliton.vm.bytemark.co.uk/pub/cpt-city/cb/qual/tn/Paired_12.png.index.html colors = new color[12]; colors[0] = color(166, 206, 227); colors[1] = color(31, 120, 180); colors[2] = color(178, 223, 138); colors[3] = color(51, 160, 44); colors[4] = color(251, 154, 153); colors[5] = color(227, 26, 28); colors[6] = color(253, 191, 111); colors[7] = color(255, 127, 0); colors[8] = color(202, 178, 214); colors[9] = color(106, 61, 154); colors[10] = color(255, 255, 153); colors[11] = color(177, 89, 40); conditions = loadStrings("subset.csv"); for (int i = 0; i < conditions.length; i++) { conditions[i] = split(conditions[i], ",")[1]; } records = new Record[80][12]; String[] lines = loadStrings("cond-age-cost.csv"); for (int i = 0; i < 80; i++) { String[] words = split(lines[i], "\t"); for (int j = 0; j < 36; j+=3) { String condition = conditions[int(j/3)]; records[i][int(j/3)] = new Record(condition, i, float(words[j]), float(words[j+1]), int(words[j+2])); //println(condition + ", " + i + ", " + float(words[j]) + ", " + float(words[j+1]) + ", " + int(words[j+2])); } } float maxPersonalCost = 0; float maxInsurerCost = 0; for (int i = 0; i < 80; i++) { for (int j = 0; j < 12; j++) { maxPersonalCost = max(maxPersonalCost, records[i][j].personalCost); maxInsurerCost = max(maxInsurerCost, records[i][j].insurerCost); } } maxCost = max(maxPersonalCost, maxInsurerCost); for (int age = 0; age < 80; age++) { float totalInsurerCost = 0; float totalPersonalCost = 0; float totalCount = 0; float avgInsurerCost = 0; float avgPersonalCost = 0; for (int cond = 0; cond < 12; cond++) { totalInsurerCost += records[age][cond].insurerCost; totalPersonalCost += records[age][cond].personalCost; totalCount += records[age][cond].count; if (records[age][cond].count > 0) { avgInsurerCost += records[age][cond].insurerCost / records[age][cond].count; avgPersonalCost += records[age][cond].personalCost / records[age][cond].count; } } maxTotalInsurerCost = max(totalInsurerCost, maxTotalInsurerCost); maxTotalPersonalCost = max(totalPersonalCost, maxTotalPersonalCost); maxTotalCount = max(totalCount, maxTotalCount); maxAvgInsurerCost = max(avgInsurerCost, maxAvgInsurerCost); maxAvgPersonalCost = max(avgPersonalCost, maxAvgPersonalCost); } font = loadFont("HelveticaNeue-11.vlw"); titleFont = loadFont("HelveticaNeue-16.vlw"); } void draw() { background(250); int numYears = 80; float chartX = 80.0; float chartWidth = width - 120.0; chartWidth = floor(chartWidth/80.0) * 80.0; float chartY = 40.0; float chartHeight = height * 0.666; noStroke(); float bw = chartWidth / float(numYears); float[] currentBase = new float[numYears]; for (int i = 0; i < numYears; i++) { currentBase[i] = chartY + chartHeight; } for (int c = 0; c < conditions.length; c++) { float bx = chartX; for (int age = 0; age < numYears; age++) { float bh = chartHeight; if (chosenMetric == TOTAL_INSURER) { if (maxTotalInsurerCost > 0) { bh *= records[age][c].insurerCost / maxTotalInsurerCost; } else { bh = 0; } } else if (chosenMetric == TOTAL_PERSONAL) { if (maxTotalPersonalCost > 0) { bh *= records[age][c].personalCost / maxTotalPersonalCost; } else { bh = 0; } } else if (chosenMetric == TOTAL_COUNT) { if (maxTotalCount > 0) { bh *= records[age][c].count / maxTotalCount; } else { bh = 0; } } else if (chosenMetric == AVG_INSURER) { if (maxAvgInsurerCost > 0 && records[age][c].count > 0) { bh *= (records[age][c].insurerCost / records[age][c].count) / maxAvgInsurerCost; } else { bh = 0; } } else if (chosenMetric == AVG_PERSONAL) { if (maxAvgPersonalCost > 0 && records[age][c].count > 0) { bh *= (records[age][c].personalCost / records[age][c].count) / maxAvgPersonalCost; } else { bh = 0; } } fill(colors[c]); rect(bx, currentBase[age] - bh, bw, bh); currentBase[age] -= bh; bx += bw; } } // axes... stroke(100); line(chartX, chartY+chartHeight, chartX+chartWidth, chartY+chartHeight); line(chartX, chartY, chartX, chartY+chartHeight); float hlx = chartX; float hlw = chartWidth/floor(numYears/10.0); fill(0); textAlign(CENTER, TOP); textFont(font,11); for (int i = 0; i <= numYears; i+=10) { float hly = chartY+chartHeight; line(hlx, hly, hlx, 2+hly); text(i+"",hlx,8+hly); hlx += hlw; } float vly = chartY+chartHeight; float vlh = chartHeight/10.0; fill(0); textAlign(RIGHT, CENTER); float currentMax = 0; if (chosenMetric == TOTAL_INSURER) { currentMax = maxTotalInsurerCost; } else if (chosenMetric == TOTAL_PERSONAL) { currentMax = maxTotalPersonalCost; } else if (chosenMetric == TOTAL_COUNT) { currentMax = maxTotalCount; } else if (chosenMetric == AVG_INSURER) { currentMax = maxAvgInsurerCost; } else if (chosenMetric == AVG_PERSONAL) { currentMax = maxAvgPersonalCost; } textFont(font,11); for (int i = 0; i <= 10; i++) { float vlx = chartX; line(vlx, vly, vlx-2, vly); float value = i * currentMax / 10.0; String label = ""; if (chosenMetric == TOTAL_INSURER || chosenMetric == TOTAL_PERSONAL) { label = "$"+nfc(value/1000000.0,1)+"m"; } else if (chosenMetric == TOTAL_COUNT) { label = nfc(int(value)); } else if (chosenMetric == AVG_INSURER || chosenMetric == AVG_PERSONAL) { label = "$"+nfc(value/1000.0,1)+"k"; } text(label,vlx-4,vly); vly -= vlh; } // legend... float okx = 40.0; float kx = okx; float oky = chartY+chartHeight + 30.0; float ky = oky; float maxKy = height - 20.0; float kh = 12; float kw = 12; textFont(font, 11); textAlign(LEFT, TOP); noStroke(); float maxTextWidth = 0; for (int i = conditions.length-1; i >= 0; i--) { fill(colors[i]); rect(kx, ky, kw, kh); fill(0); text(conditions[i], kx + kw + 5, ky); maxTextWidth = max(maxTextWidth, textWidth(conditions[i])); ky += kh + 5; if (ky > maxKy) { kx = kx + kw + 5 + maxTextWidth + 10; ky = oky; maxTextWidth = 0; } } // title... fill(0); textFont(titleFont, 16); textAlign(CENTER, CENTER); float tx = width * 0.5; float ty = chartY/2.0; if (chosenMetric == TOTAL_INSURER) { text("Total Insurer Cost ($) by Age", tx, ty); } else if (chosenMetric == TOTAL_PERSONAL) { text("Total Personal Cost ($) by Age", tx, ty); } else if (chosenMetric == TOTAL_COUNT) { text("Total Number of Patients by Age", tx, ty); } else if (chosenMetric == AVG_INSURER) { text("Average Insurer Cost per Person ($) by Age", tx, ty); } else if (chosenMetric == AVG_PERSONAL) { text("Average Personal Cost per Person ($) by Age", tx, ty); } } void mouseMoved() { chosenMetric = metrics[int(constrain(metrics.length * mouseX / width, 0, metrics.length-1))]; } color darker(color c) { float r = red(c); float g = green(c); float b = blue(c); return color(r/2, g/2, b/2); } void keyPressed() { if (online) return; if (key == 's' || key == 'S') { if (chosenMetric == TOTAL_INSURER) { save("TotalInsurerCost.png"); } else if (chosenMetric == TOTAL_PERSONAL) { save("TotalPersonalCost.png"); } else if (chosenMetric == TOTAL_COUNT) { save("TotalNumberofPatients.png"); } else if (chosenMetric == AVG_INSURER) { save("AverageInsurerCostperPerson.png"); } else if (chosenMetric == AVG_PERSONAL) { save("AveragePersonalCostperPerson.png"); } } }