Skip to content

Instantly share code, notes, and snippets.

@dhoboy
Last active October 17, 2017 07:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhoboy/4ef99e7eb2147c95ab211da564e75b6a to your computer and use it in GitHub Desktop.
Save dhoboy/4ef99e7eb2147c95ab211da564e75b6a to your computer and use it in GitHub Desktop.
Pitcher Explorer

Baseball Pitcher Explorer (open in new tab to see full block)

Brush along one or more dimensions to see a table of Pitchers that match your critera. Output table code from this syntagmatic block.

I scraped the data from Retrosheet with Python's BeautifulSoup. Scraping repo here.

Retrosheet data use statement: 'The information used here was obtained free of charge from and is copyrighted by Retrosheet. Interested parties may contact Retrosheet at "www.retrosheet.org".'

<!doctype html>
<meta charset="utf-8">
<title>Pitchers</title>
<style>
body {
font-family: sans-serif;
}
#main {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
margin: 20px 30px 20px 10px;
}
.title {
margin-left: 9px;
font-size: 14px;
}
.bar, .selection {
fill: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: #666666;
shape-rendering: crispEdges;
}
.axis text {
fill: #666666;
font-size: 14px;
}
#output {
margin: 0 20px 10px 10px;
}
pre {
width: 100%;
height: 300px;
margin: 6px 12px;
tab-size: 30;
font-size: 10px;
overflow: auto;
}
</style>
<body>
<div id="main"></div>
<div id="output"></div>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 10
};
var height = 100 - margin.top - margin.bottom;
var width = 200 - margin.left - margin.right;
d3.csv("http://dhoboy.github.io/baseball/pitchers.csv", function(data) {
var filteredData = data;
var filters = {};
var columnKey = {
'G': 'Games Pitched',
'GS': 'Games Started',
'CG': 'Complete Games',
'SHO': 'Shutouts',
'GF': 'Relief Games Finished',
'SV': 'Saves',
'IP': 'Innings Pitched',
'H': 'Hits Allowed',
'BFP': 'Batters Faced Pitcher',
'HR': 'Home Runs Allowed',
'R': 'Runs Allowed',
'ER': 'Earned Runs Allowed',
'BB': 'Bases On Balls',
'IB': 'Intentional Bases On Balls',
'SO': 'Strikeouts',
'SH': 'Sacrifice Hits Allowed',
'SF': 'Sacrifice Flies Allowed',
'WP': 'Wild Pitches',
'HBP': 'Hit By Pitch',
'BK': 'Balks',
'GDP': 'Grounded in Double Plays',
'W': 'Wins',
'L': 'Losses',
'ERA': 'Earned Run Average',
'RS': 'Run Support',
'PW': 'Pitcher Wins'
};
var output = d3.select("#output").append("pre")
.text("Brush along at least one dimension above to see a table of results");
var scales = d3.keys(columnKey).reduce(function(prev, next) {
prev[next] = d3.scaleLinear()
.domain(d3.extent(data, function(d) {
return +d[next];
}))
.range([0, width]);
return prev;
}, {});
var reverseScales = d3.keys(columnKey).reduce(function(prev, next) {
prev[next] = d3.scaleLinear()
.domain([margin.left, width + margin.left, height])
.range(d3.extent(data, function(d) {
return +d[next];
}));
return prev;
}, {});
var axes = d3.keys(columnKey).reduce(function(prev, next) {
prev[next] = d3.axisBottom(scales[next]).ticks(3)
return prev;
}, {});
var brushes = d3.keys(columnKey).reduce(function(prev, next) {
prev[next] = d3.brushX().extent([[margin.left, -15], [width + margin.left, height]]);
return prev;
}, {});
var graphs = d3.select("#main").selectAll(".graph")
.data(d3.keys(columnKey))
.enter()
.append("div")
.attr("class", "graph");
graphs.append("div")
.attr("class", "title")
.text(function(d) { return columnKey[d] + ": " + d; });
var svgs = graphs.append("svg")
.attr("id", function(d) { return d; })
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.right + margin.left)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svgs.append("rect")
.attr("class", "bar")
.attr("height", 15)
.attr("width", width)
.attr("rx", 5)
.attr("ry", 5);
d3.keys(columnKey).forEach(function(key) {
var s = d3.select("svg#" + key);
s.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(axes[key]);
s.append("g")
.attr("class", "brush")
.call(brushes[key]);
brushes[key].on("brush end", function() {
var brushSection = d3.brushSelection(this);
if (brushSection === null) {
removeFilter(key);
} else {
var filterInput = [
reverseScales[key](brushSection[0]),
reverseScales[key](brushSection[1])
];
addFilter(key, filterInput);
}
});
});
function addFilter(key, filterInput) {
filters[key] = filterInput;
filterData();
}
function removeFilter(key) {
delete filters[key];
filterData();
}
function filterData() {
// reset filteredData
filteredData = data;
// apply each filter
d3.keys(filters).forEach(function(filterKey) {
filteredData = filteredData.filter(function(d) {
return +d[filterKey] >= filters[filterKey][0] &&
+d[filterKey] <= filters[filterKey][1];
});
});
// only draw result table if you've filtered the dataset somewhat
if (d3.keys(filters).length > 0) {
output.text(d3.tsvFormat(filteredData.slice(0)));
} else {
output.text("Brush along at least one dimension above to see a table of results");
}
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment