Skip to content

Commit

Permalink
Merge pull request #3 from data-creative/data-transformation2
Browse files Browse the repository at this point in the history
Data Transformations
  • Loading branch information
s2t2 authored Mar 15, 2017
2 parents 6fb96a5 + 09c63ba commit 3d39616
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 60 deletions.
26 changes: 21 additions & 5 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# Credits, Notes, and Reference

## Data Dictionary

+ [Defining the size of "big" law firms](http://www.top-law-schools.com/introduction-to-biglaw.html)

## Data Processing

+ [Previous project](https://github.com/dwillis/state_legislatures/) dealing with PDF-to-CSV conversions
+ [JavaScript String Interpolation](http://stackoverflow.com/questions/1408289/how-can-i-do-string-interpolation-in-javascript)

## Data Visualization

+ [Previous project](https://github.com/data-creative/us-gov-greenhouse-gas-emissions/blob/master/emissions.html) involving Highcharts stacked bar graphs

### Charting Libraries

#### NVD3
Expand All @@ -17,12 +20,25 @@

#### Highcharts

+ [Previous project](https://github.com/data-creative/us-gov-greenhouse-gas-emissions/blob/master/emissions.html) involving Highcharts stacked bar graphs
+ http://www.highcharts.com/demo/column-stacked
+ http://www.highcharts.com/demo/column-stacked-percent
+ http://www.highcharts.com/docs/export-module/export-module-overview
+ http://api.highcharts.com/highcharts/plotOptions.column.dataLabels
+ http://stackoverflow.com/questions/13275648/disable-click-on-legend-in-highcharts-column-graph
+ http://api.highcharts.com/highcharts/xAxis.categories
+ http://api.highcharts.com/highcharts/series%3Ccolumn%3E.data.name
+ http://jsfiddle.net/wxxEu/2/
+ http://stackoverflow.com/questions/18035421/highcharts-how-can-i-take-x-axis-value-in-tooltip
+ http://api.highcharts.com/highcharts/subtitle
+ Tooltip Content, Formatting, and Positioning:
+ http://api.highcharts.com/highcharts/series%3Ccolumn%3E.data.name
+ http://stackoverflow.com/questions/18035421/highcharts-how-can-i-take-x-axis-value-in-tooltip
+ http://stackoverflow.com/q/16991335/670433
+ http://api.highcharts.com/highcharts/Highcharts.numberFormat
+ http://api.highcharts.com/highcharts/tooltip.backgroundColor
+ https://github.com/highcharts/highcharts/issues/2961
+ [Specify Background Color and Opacity](http://jsfiddle.net/highcharts/9vrYL/)

### Colors

+ [D3/Colorbrewer Color Scales](https://bl.ocks.org/mbostock/5577023)
+ [Previous Project](https://github.com/data-creative/us-income-and-affordability/blob/master/index.html) using color scales
+ [Another Previous Project](https://github.com/data-creative/us-state-legislature-compositions/blob/master/index.html) using color scales
244 changes: 189 additions & 55 deletions employment-outcomes.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,95 +22,199 @@ <h2>Employment Outcomes for Recent Graduates</h2>
</p>
</footer>

<script src="http://d3js.org/colorbrewer.v1.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">

const employmentStatuses = [
"Employed - Bar Passage Required",
"Employed - J.D. Advantage",
"Employed - Professional Position",
"Employed - Non-Professional Position",
"Employed - Law School/University Funded",
"Employed - Undeterminable",
"Pursuing Graduate Degree Full Time",
"Unemployed - Start Date Deferred",
"Unemployed - Not Seeking",
"Unemployed - Seeking",
"Employment Status Unknown"
]
const employedStatuses = employmentStatuses.filter(function(status){ return status.includes("Employed") })
const unemployedStatuses = employmentStatuses.filter(function(status){ return !status.includes("Employed") })

const employmentTypes = [
{type: "Law Firms (Unknown Size)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (Solo)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (2 - 10)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (11 - 25)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (26 - 50)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (51 - 100)", defaultDisplayGroup: "Law Firm (Other)"},
{type: "Law Firms (101 - 250)", defaultDisplayGroup: "Law Firm (Big)"},
{type: "Law Firms (251 - 500)", defaultDisplayGroup: "Law Firm (Big)"},
{type: "Law Firms (501 +)", defaultDisplayGroup: "Law Firm (Big)"},

{type: "Business & Industry", defaultDisplayGroup: "Business & Industry"},
{type: "Government", defaultDisplayGroup: "Government"},
{type: "Pub. Int.", defaultDisplayGroup: "Public Interest"},
{type: "Clerkships - Federal", defaultDisplayGroup: "Clerkship"},
{type: "Clerkships - State & Local", defaultDisplayGroup: "Clerkship"},
{type: "Clerkships - Other", defaultDisplayGroup: "Clerkship"},
{type: "Education", defaultDisplayGroup: "Employed (Other)"},
{type: "Employer Type Unknown", defaultDisplayGroup: "Employed (Other)"}
]

//
// REQUEST DATA FROM NEARBY JSON FILE
//

var url = "data/2015/employment_summary_reports.json"
var myData
var myChart
const year = 2015
const url = `data/${year}/employment_summary_reports.json`

d3.json(url, function(reports){
myData = reports // assign the raw JSON data to the `myData` variable to access it later via the console for debugging
d3.json(url, function(json){
var reports = json

//
// PROCESS AND TRANSFORM RAW DATA
//

var chartCategories = ['School A', 'School B', 'School C', 'School D', 'School E']

var chartSeries = [
{
name: 'Unemployed',
data: [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},
{
name: 'Public Interest',
data: [2, 2, 3, 2, 1] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},
{
name: 'Big Law',
data: [3, 4, 4, 2, 5] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
}
]























sortedReports = reports.sort(function(a, b){
return d3.ascending(a.school_name, b.school_name )
})

// PROCESS EMPLOYMENT STATUSES

// Returns the sum of an array of counts matching any status included in the given list
// @param [Object] rpt
// @param [Array] statuses
function sumOfStatusCounts(report, statuses) {
var counts = report.employment_outcomes.statuses.filter(function(statusCount){
return statuses.includes(statusCount.status)
}).map(function(statusCount){
return statusCount.count
})

return d3.sum(counts)
}

var sortedEmployedCounts = sortedReports.map(function(rpt){
return sumOfStatusCounts(rpt, employedStatuses)
})

var sortedUnemployedCounts = sortedReports.map(function(rpt){
return sumOfStatusCounts(rpt, unemployedStatuses)
})

// PROCESS EMPLOYMENT TYPES

// Returns a sorted series of numbers, each representing the sum of counts for all employment types belonging to the given display group.
// @param [String] displayGroup e.g. "Law Firm (Big)"
function sortedEmploymentTypeSeries(displayGroup) {
var types = employmentTypes.filter(function(obj){ return obj.defaultDisplayGroup == displayGroup }).map(function(obj){ return obj.type })

return sortedReports.map(function(rpt){ return sumOfTypeCounts(rpt, types) })
}

// Returns the sum of an array of counts matching any type included in the given list
// @param [Object] rpt
// @param [Array] types
function sumOfTypeCounts(report, types) {
var counts = report.employment_outcomes.types.filter(function(typeCount){
return types.includes(typeCount.type)
}).map(function(typeCount){
return typeCount.count
})

return d3.sum(counts)
}

//
// COMPILE CHART
//

var chartCategories = sortedReports.map(function(rpt){ return rpt.school_name.toUpperCase() })

var chartSeries = [
{
name: 'Unemployed',
color: colorbrewer.Reds[9][6],
data: sortedUnemployedCounts // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},
//{
// name: 'Employed',
// color: colorbrewer.Greens[9][6],
// data: sortedEmployedCounts // [2, 2, 3, 2, 1] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
//}
{
name: 'Employed (Other)',
color: colorbrewer.Greys[9][6],
data: sortedEmploymentTypeSeries('Employed (Other)') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},



{
name: "Public Interest",
color: colorbrewer.Oranges[9][4],
data: sortedEmploymentTypeSeries("Public Interest") // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},

{
name: 'Government',
color: colorbrewer.Oranges[9][6],
data: sortedEmploymentTypeSeries('Government') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},




{
name: 'Clerkship',
color: colorbrewer.Purples[9][6],
data: sortedEmploymentTypeSeries('Clerkship') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},


{
name: 'Business & Industry',
color: colorbrewer.Blues[9][6],
data: sortedEmploymentTypeSeries('Business & Industry') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},


{
name: 'Law Firm (Other)',
color: colorbrewer.Greens[9][4],
data: sortedEmploymentTypeSeries('Law Firm (Other)') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},
{
name: 'Law Firm (Big)',
color: colorbrewer.Greens[9][6],
data: sortedEmploymentTypeSeries('Law Firm (Big)') // [5, 3, 4, 7, 2] // must be in same order as ['School A', 'School B', 'School C', 'School D', 'School E']
},
]

function report(schoolShortName) {
return sortedReports.find(function(rpt){ return rpt.school_name.toUpperCase() == schoolShortName })
}

function totalGrads(schoolShortName) {
var rpt = report(schoolShortName)
return rpt.total_grads;
}

function customTooltip(point) {
var tooltip = ''
var headerFormat = `<b>${point.x.toUpperCase()}</b><br/>`
headerFormat += `<b>Total Grads: ${totalGrads(point.x)}</b><br/>`
tooltip += headerFormat

point.points.forEach(function(pt){
var pointFormat = `<span style="color:${pt.series.color}">${pt.series.name}</span>: <b>${pt.y}</b> (${ Highcharts.numberFormat(pt.percentage, 0) }%)<br/>`
tooltip += pointFormat
})

//
// COMPILE CHART
//
return tooltip
}

//var chartSeries = [
// {
Expand Down Expand Up @@ -167,7 +271,10 @@ <h2>Employment Outcomes for Recent Graduates</h2>
// ]

var chartOptions = {
chart: {type: 'column'},
chart: {
type: 'column',
marginBottom:50 // make room to display the subtitle below the chart.
},
exporting:{
enabled:true,
buttons: {contextButton: {text: 'Export'}}
Expand All @@ -178,20 +285,47 @@ <h2>Employment Outcomes for Recent Graduates</h2>
layout:'vertical',
verticalAlign:'middle'
},
title: {text: 'Employment Outcomes by School'},
title: {text: 'Employment Outcomes by Law School'},
subtitle: {
text: 'Copyright 2017 Data Creative (http://data-creative.info). Does not distinguish between short-term/long-term or full-time/part-time employment. Law firm size considered "Big" if greater than or equal to 100 employees.',
align:'left',
verticalAlign:'bottom',
floating:true,
y: -5,
style: { "font-size":10, "font-style":"italic" }
},
xAxis: {
categories: chartCategories,
type: "category"
type: "category",
opposite:true,
labels:{
//formatter:function(){
// return `<b>${this.value}</b>`
//},
style: { "color": "#000", "cursor": "default", "fontSize": "11px" }
}
},
yAxis: {
min: 0,
title: {text: 'Percentage of 2015 Graduates'},
stackLabels: {enabled: true}
title: {
text: 'PERCENTAGE OF 2015 GRADUATES',
style: { "color": "#000", "cursor": "default", "fontSize": "11px" }
},
stackLabels: {enabled: true},
labels:{
enabled:true,
format: '{value}%',
style: { "color": "#000", "cursor": "default", "fontSize": "11px" }
}
},
tooltip: {
headerFormat: '<b>{point.x}</b><br/>', // '<b>{point.key}</b><br/>'
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.percentage:.0f}%)<br/>',
shared: true
//headerFormat: '<b>{point.x}</b><br/>', // '<b>{point.key}</b><br/>'
//pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.percentage:.0f}%)<br/>',
formatter: function(){ return customTooltip(this) },
shared: true,
borderColor: '#000',
followPointer:true,
backgroundColor:'rgba(255,255,255, 1)' // use white background and remove opacity
},
plotOptions: {
column: {
Expand Down

0 comments on commit 3d39616

Please sign in to comment.