Skip to content

Instantly share code, notes, and snippets.

@mcallaghan
Last active October 19, 2015 17:18
Show Gist options
  • Save mcallaghan/bbbd2e6c7d6b684d47a6 to your computer and use it in GitHub Desktop.
Save mcallaghan/bbbd2e6c7d6b684d47a6 to your computer and use it in GitHub Desktop.
Labour supply responses in an economy of one individual

#Labour supply responses in an economy of one individual This gist uses d3 to visualise labour supply responses to changes in wages, taxes, transfers or preferences (or their concavity) given a choice of working any whole number of hours between 0 and 80

The utility function is given by ((consumption*consumption preference)^consumption concavity)+((leisure*leisure preference)^leisure concavity) where consumption preference + leisure preference = 1.

The indifference curve is drawn through all combinations of consumption and leisure where utility is equal to the maximum utility obtainable on the budget line.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="theme.css"></link>
</head>
<body>
<div id="main">
<p>
Click the plus and minus buttons to adjust the variables
</p>
<div class="variables">
<div id="wages_par" class="variable">
Wages
<div id="wage_div">
</div>
<button onclick="increase('wage')">+</button>
<button onclick="decrease('wage')">-</button>
</div>
<div id="tax_par" class="variable">
Tax
<div id="tax_div">
</div>
<button onclick="increase('tax')">+</button>
<button onclick="decrease('tax')">-</button>
</div>
<div id="c_pref_par" class="variable">
Consumption Preference
<div id="c_pref_div">
</div>
<button onclick="increase('c_pref')">+</button>
<button onclick="decrease('c_pref')">-</button>
</div>
<div id="l_pref_par" class="variable">
Leisure Preference
<div id="l_pref_div">
</div>
<button onclick="increase('l_pref')">+</button>
<button onclick="decrease('l_pref')">-</button>
</div>
<div id="c_con_par" class="variable">
Consumption Concavity
<div id="c_con_div">
</div>
<button onclick="increase('c_con')">+</button>
<button onclick="decrease('c_con')">-</button>
</div>
<div id="l_con_par" class="variable">
Leisure Concavity
<div id="l_con_div">
</div>
<button onclick="increase('l_con')">+</button>
<button onclick="decrease('l_con')">-</button>
</div>
</div>
<div class="variables">
<div id="transfer_cut_par" class="variable">
Transfer Cutoff
<div id="transfer_cut_div">
</div>
<button onclick="increase('transfer_cut')">+</button>
<button onclick="decrease('transfer_cut')">-</button>
</div>
<div id="transfer_par" class="variable">
Transfer Amount
<div id="transfer_div">
</div>
<button onclick="increase('transfer')">+</button>
<button onclick="decrease('transfer')">-</button>
</div>
<div id="transfer_taper_par" class="variable">
Transfer Taper
<div id="transfer_taper_div">
</div>
<button onclick="increase('transfer_taper')">+</button>
<button onclick="decrease('transfer_taper')">-</button>
</div>
</div>
<div id="graphbox">
</div>
<p>
<button onclick="reversex()">Reverse X Axis Scale</button>
</p>
<div id="hours_change_par" class="result">
Change in hours
<div id="hours_change">
</div>
</div>
</div>
<script>
width = 800;
height = 600;
var max_hours = 80
var wage = 100;
$("#wage_div").html(wage)
var tax = 0.5;
$("#tax_div").html(tax)
var c_pref = 0.5;
$("#c_pref_div").html(c_pref)
var l_pref = 0.5;
$("#l_pref_div").html(l_pref)
var c_con = 0.5;
$("#c_con_div").html(c_con)
var l_con = 0.5;
$("#l_con_div").html(l_con)
var transfer_cut = 1000;
$("#transfer_cut_div").html(transfer_cut)
var transfer = 500;
$("#transfer_div").html(transfer)
var transfer_taper = 0.5;
$("#transfer_taper_div").html(transfer_taper)
var hours = [{h:0, y:0, c: 0, l: 1, g: transfer}];
for (i = 0; i < max_hours + 1; i++) {
h = i
y = h*wage
c = y*(1-tax)
if (c > transfer_cut) {
g = transfer-((c-transfer_cut)*transfer_taper)
if (g < 0) {
g = 0
}
} else {
g = transfer
}
c = c + g
l = 1-i/max_hours
u = Math.pow((l*4000)*l_pref,l_con)+Math.pow(c*c_pref,c_con)
if (i == 0) {
hours[i].h = h
hours[i].y = y
hours[i].c = c
hours[i].l = l
hours[i].u = u
} else {
hours.push({h: i, y: y, c: c, l: l, u: u, g: g});
}
}
max_consumption = d3.max(hours, function(d) { return d.c; }) * 2;
max_utility = d3.max(hours, function(d) { return d.u; });
console.log(max_utility)
for (i = 0; i < max_hours + 1; i++) {
l = hours[i].l;
cc = max_utility - Math.pow((l*4000)*l_pref,l_con)
ci = Math.pow(cc, (1.0/c_con))/c_pref
hours[i].ci = ci;
}
yticks = [0]
x = 0;
for (i = 1; x < max_consumption - 1; i++) {
x = i * 500
yticks.push(x)
}
poshourscale = d3.scale.linear()
.domain([0, max_hours])
.range([width*0.1, width*0.9]);
neghourscale = d3.scale.linear()
.domain([max_hours, 0])
.range([width*0.1, width*0.9]);
hourscale = neghourscale;
function reversex() {
if (hourscale == neghourscale) {
hourscale = poshourscale;
draw()
xaxis()
} else {
hourscale = neghourscale;
draw()
xaxis()
}
}
incomescale = d3.scale.linear()
.domain([0, max_consumption])
.range([height*0.9, height*0.15]);
utilityscale = d3.scale.linear()
.domain([0, Math.pow(max_consumption,0.5)])
.range([height*0.9, height*0.15]);
console.log(hours)
wage_increment = 10;
tax_increment = 0.01;
c_pref_increment = 0.1;
l_pref_increment = 0.1;
c_con_increment = 0.1;
l_con_increment = 0.1;
transfer_cut_increment = 50;
transfer_increment = 50;
transfer_taper_increment = 0.1;
function increase(v, a) {
if (v == "c_pref" && a == undefined) {
decrease("l_pref", 1)
}
if (v == "l_pref" && a == undefined) {
decrease("c_pref", 1)
}
this[v] += this[v+"_increment"]
$("#"+v+"_div").html(this[v].toFixed(2))
calculate()
}
function decrease(v, a) {
if (v == "c_pref" && a == undefined) {
increase("l_pref", 1)
}
if (v == "l_pref" && a == undefined) {
increase("c_pref", 1)
}
this[v] -= this[v+"_increment"]
$("#"+v+"_div").html(this[v].toFixed(2))
calculate()
}
var graph = d3.select("#graphbox")
.append("svg")
.attr("width", width)
.attr("height", height);
graph.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "white")
.style("stroke", "black")
.style("stroke-width", 2)
graph.append("line")
.attr("class", "axis")
.attr("x1", width*0.1)
.attr("x2", width* 0.9)
.attr("y1", height*0.9)
.attr("y2", height*0.9)
.attr("stroke-width", 1)
.attr("stroke", "black");
graph.append("line")
.attr("class", "axis")
.attr("x1", width*0.1)
.attr("x2", width* 0.1)
.attr("y1", height*0.9)
.attr("y2", height*0.1)
.attr("stroke-width", 1)
.attr("stroke", "black");
xaxis()
yaxis()
draw()
function calculate() {
max_utility = d3.max(hours, function(d, i) { return d.u; });
for (i = 0; i < max_hours + 1; i++) {
if (hours[i].u == max_utility) {
hours_1 = hours[i].h
}
}
for (i = 0; i < max_hours + 1; i++) {
h = i
y = h*wage
c = y*(1-tax)
if (c > transfer_cut) {
g = transfer-((c-transfer_cut)*transfer_taper)
if (g < 0) {
g = 0
}
} else {
g = transfer
}
c = c + g
l = 1-i/max_hours
u = Math.pow((l*4000)*l_pref,l_con)+Math.pow(c*c_pref,c_con)
hours[i].h = h
hours[i].y = y
hours[i].c = c
hours[i].l = l
hours[i].u = u
}
max_utility = d3.max(hours, function(d) { return d.u; });
for (i = 0; i < max_hours + 1; i++) {
if (hours[i].u == max_utility) {
hours_2 = hours[i].h
}
l = hours[i].l;
cc = max_utility - Math.pow((l*4000)*l_pref,l_con)
ci = Math.pow(cc, (1.0/c_con))/c_pref
hours[i].ci = ci;
}
draw()
delt_hours = hours_2 - hours_1
pcnt = (delt_hours/hours_1).toFixed(2)
if (delt_hours > 0) {
delt_hours = "<span style='color:green'>+"+delt_hours + " (+" + pcnt + "%)</span>"
} else if (delt_hours < 0) {
delt_hours = "<span style='color:red'>"+delt_hours + " (" + pcnt + "%)</span>"
} else {
delt_hours = delt_hours + " (" + pcnt + "%)"
}
$("#hours_change").html(delt_hours)
}
function draw() {
graph.selectAll("circle").remove()
graph.selectAll("path").remove()
graph.selectAll("circle")
.data(hours)
.enter()
.append("circle")
.attr("class", "consumption")
.attr("cx", function(d) { return hourscale(d.h)})
.attr("cy", function(d) { return incomescale(d.c)})
.attr("r", function(d) {
if (d.u == max_utility) {
return 3
} else {
return 1
}
});
graph.append("g").selectAll("circle")
.data(hours)
.enter()
.append("circle")
.attr("class", "utility")
.attr("cx", function(d) { return hourscale(d.h)})
.attr("cy", function(d) { return incomescale(d.ci)})
.attr("r", 1)
var indifference = d3.svg.line()
.x(function(d) {
return hourscale(d.h);
})
.y(function(d) {
return incomescale(d.ci);
})
var income = d3.svg.line()
.x(function(d) {
return hourscale(d.h);
})
.y(function(d) {
return incomescale(d.c);
})
graph.append("svg:path").attr("d", indifference(hours));
graph.append("svg:path").attr("d", income(hours))
.attr("class", "consumption");
}
function xaxis() {
d3.selectAll(".x_lab").remove()
xax = graph.append("g").selectAll("text")
.data(hours
.filter(function(d) {
if (d.h % 2 == 0) {
return true;
} else {
return false;
}
})
)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("class", "x_lab")
.style("fill", "black")
.style("font-size", width/75 + "px")
.attr("x", function(d) { return hourscale(d.h) })
.attr("y", height*0.95)
.text(function(d) { return d.h});
}
function yaxis() {
yax = graph.append("g").selectAll("text")
.data(yticks)
.enter()
.append("text")
.attr("text-anchor", "middle")
.style("fill", "black")
.style("font-size", width/75 + "px")
.attr("x", width*0.05)
.attr("y", function(d) { return incomescale(d) })
.text(function(d) { return d});
}
d3.select(self.frameElement).style("height", "950px");
</script>
</body>
</html>
#main {
width: 1000px;
margin: 0 auto;
text-align: center;
}
.variable, .result {
margin: 5px;
display: inline-block;
}
.variables {
display: inline-block;
}
#graphbox {
clear: both;
}
path {
stroke: rgb(144, 180, 210);
stroke-width: 1;
fill: none;
}
path.consumption {
stroke: red;
}
circle {
opacity: 0.6
}
@supermacro
Copy link

Hey I'm an Econ student unfamiliar to D3.js ... would D3 be a good tool to draw things like this or this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment