Blog
Breaking even on a house: Rising prices are an inevitability because of transaction costs
We recently bought a house. Now, with all the hemming and hawing about telework/remote work from the folks who had just recently been touting “Digital first!” and “The new normal!”, I got to thinking about about what it would take to sell this house should we be forced to move for work. Even if I just wanted to breakeven on this transaction (i.e. net cost of 0, just recouping the money spent on the purchase, nevermind the opportunity cost of the money committed to this endeavour), it isn’t as simple as pricing the house for the same price that we bought it for: to reach breakeven, the price needs to go up for every successive transaction, regardless of if the value of the property changed. You can see this in the visualization below. The key takeaways:
- Sale price needs to increase by around 5% transaction-over-transaction to reach breakeven on successive transactions.
- After 3 sales (starting at $785,400 and selling at $915,708), there is a cumulative sale price increase of around 16.6% required to breakeven. After 6 sales (starting at $785,400 and selling at $1,067,636), the cumulative sale price increase is around 35.9%.
Note: Commission rate & property transfer tax rate were simplified to flat percentages for ease of creation, but they are described (and calculated) in greater detail below the fold, so the values might not be exact matches.
PSA: Stop saying 'down deposit'
There is no such thing as a down deposit.
I’ve seen this erroneous phrase used more than a few times and I feel strongly enough about it to write a few lines for anyone who wants/needs the clarification. Most often, I’ve seen this phrase used in the context of large purchases, especially property. Best I can tell, this is a combination of two other money-related things: down payments and deposits.
I’ll allow that it can be a bit confusing since you might deposit (the verb) the money for a down payment somewhere. But the two concepts contained within the phrase aren’t even all that related to each other.
Now that AI can code...: Can it, though?
Whenever I see a phrase like “Now that LLM/GPT/AI can <VERB>…” I think I do what anyone with even a little bit of knowledge about AI (and especially generative models) does and think, “Hold on there a minute. Can it <VERB>?” If I’m wrong, I’m sure someone will correct me, but my understanding is that the transformer-based large language generative models (cough ChatGPT cough) that are all the rage right now don’t really do anything except generate text passages.
So, no, it can’t <VERB>.
But, you might ask, “Isn’t generating text basically the same as having someone type out that same text?” To which I would respond, “That depends on if you think the product is the only thing that matters. Otherwise, no.” Despite (potentially) producing the same product, the underlying understanding that determines that product is very different for humans and AI and I think that ignoring (or not recognizing) this difference leads some folks to believe that AI is more capable than it is.
Data-Driven Disappointment in Pictures: Layoffs × Hierarchy
Sometimes it can be useful to change the way that we’re looking at the same data to be able to present other insights as to what the data could mean. Previously, I had use hierarchy as the inner ring and then drilled down using LAID OFF status. Here, I’ve done the opposite:
Data-Driven Disappointment in Pictures: Hierarchy × Layoffs
RECAP: We saw previously that 25 out of 42 staff, or approximately 59.5% of the entire staff (or, at any rate, of the staff listed on the web page), were laid off from Canada Learning Code in January of 2024. This is neatly visualized in the pie (donut?) chart below 👇:
See the code:
<script type="module"> import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"; const data = await d3.tsv("https://jernwerber.dev/static/clc-laid-off-2024-01-20.tsv"); const svgWidth = 700; const svgHeight = 500; const outerRadius = 150; const innerRadius = 100; const shiftX = 0; const shiftY = 40; const radiusOffset = 50; const baseColor = "crimson"; const backgroundColor = "none"; const pieGap = 0.04; const pieRotate = 0.2 * Math.PI; const CS = { HIERARCHY : { 0 : "non-managers", 1 : "managers", 2 : "directors", 3 : "C-level", 4 : "CEO" }, LAIDOFF: { "TRUE" : "Laid off", "FALSE" : "Not laid off" } } const groupedData = d3.group(data, d => d.LaidOff); const d3svg = d3.create("svg") .attr("aria-labelledby","chart-title") // .attr("width", svgWidth) // .attr("height", svgHeight) .attr("viewBox", [-svgWidth / 2, -svgHeight / 2, svgWidth, svgHeight]) .style("background-color", backgroundColor) .style("margin-inline","auto") ; d3svg.append("text") .attr("id", "chart-title") .attr("text-anchor", "middle") .attr("transform",`translate(0 ${ -svgHeight/2.2 + shiftY })`) .style("font-size","24") .style("font-weight", "light") .style("font-style","italic") .selectAll("tspan") .data(["Canada Learning Code 2024 Layoffs:", "Employees LAID OFF"]) .join("tspan") .text(d => d) .attr("x", 0) .attr("dy", (d,i) => 28 * i) ; d3svg.append("g") .attr("transform",`translate(${shiftX},${shiftY})`) .selectAll("path") .data( d3.pie() .value(d => d[1].length) .startAngle(pieRotate) .endAngle(2*Math.PI + pieRotate) .sort(null)(groupedData) ) .join("path") .attr("fill", (d, i) => d3.quantize( d3.interpolateRgb("lightgrey","crimson"),2 ).reverse()[i] ) .attr("stroke", "none") .attr("d", d3.arc().innerRadius(innerRadius).outerRadius(outerRadius)) .append("title") .text(d => d.value) ; d3svg.append("g") .attr("transform",`translate(${shiftX},${shiftY})`) .selectAll("g") .data( d3.pie() .value(d => d[1].length) .startAngle(pieRotate) .endAngle(2 * Math.PI + pieRotate) .sort(null)(groupedData) ) .join("g") .each(function (d, i) { const [x,y] = d3.arc().innerRadius(outerRadius).outerRadius(outerRadius + radiusOffset).centroid(d); const g = d3.select(this); const labelWidth = 36; const labelHeight = 24; g.append("rect") .attr("x", x - labelWidth/2) .attr("y", y - labelHeight/2) .attr("width",labelWidth) .attr("height",labelHeight) .attr("fill", "none") ; g.append("text") .attr("dy",6) .attr("text-anchor",(d.endAngle + d.startAngle)/2 < Math.PI ? "start" : "end" ) .text(`${CS.LAIDOFF[d.data[0]]} `) .attr("x", x) .attr("y", y) .attr("fill","darkslategrey") .style("font-weight","light") .style("font-style", "italic") .style("font-family","Georgia") .append("tspan") .style("font-weight", "bold") .style("cursor", "help") .text(`${d.value}`) .append("title") .text(`${(d.value * 100 / d3.sum(groupedData, d => d[1].length)).toFixed(2)}%`) ; }) ; d3.select("#d3-container-997").node() .append(d3svg.node()) ; </script> <div id="d3-container-997"> <!-- this will hold whatever D3 generates --> </div>
The overall proportion statistic, while staggering in its magnitude, doesn’t give us all that much more detail as to the impact of the layoffs on the company’s overall structure: We do know that the company didn’t shutdown, so there must be some intention to stick around at least for a little longer, but stick around for what purpose–who’s left and what do they do?
To get a better handle on what happened, we need to ask more questions.
Perhaps the next questions we might ask should be: Who was laid off? Or, how were layoffs distributed across the company?
Data-Driven Disappointment Part 4: Colour and title text
In part 4 of this series on using the D3 JavaScript library (d3.js), we will look at how to use
d3.color()
and some of the built-ind3
color interpolators to specifyfill
colours for our pie slices. We will also be adding a title and labels to help make the data being presented a little clearer.Data-Driven Disappointment Part 3: Dealing with data
In part 3 of this series on using the D3 JavaScript library (d3.js), we will look at using the functionality provided by D3 to group our data, then use that grouped data as the basis for a simple pie chart showing the relative sizes (proportions) of the groups. We will look at a couple common D3 patterns, including select-data-join and creating and calling generated functions.
Data-Driven Disappointment Part 2: Getting set up and getting started
In part 2 of this series on using the D3 Javascript library (d3.js), we will look at one way to setup data to be consumed by D3 via a tab-separated values data string copied directly out of Google Sheets. We will also be introduced to the dataset that we’ll be using throughout, a list of lay off casualties derived from the publicly available team information on the website of my former employer, Canada Learning Code.
Data-Driven Disappointment Part 1: Introduction
This is part 1 of a work-in-progress series that’s a mix of Data-Driven Documents (D3/d3.js) tutorial and reflection on being laid off. I’m using this as a way to cope with job loss and as an excuse to dive much deeper into a JavaScript library that I’ve only ever scratched the surface of, D3. In my (meager) experience, there’s a steep learning curve to being able to use D3, requiring knowledge of more than a few idiosyncratic patterns to be able to get started. Hopefully, through the posts in this series, you’ll be able use my trials and tribulations to hit the ground running and not spend as much time hitting your head against your desk in frustration.
From the Archives: Troubleshooting a y-axis issue on the Flashforge Dreamer
Another one from the archives, hence the date. This one might actually still be relevant, assuming that I can find and reupload the datasheet that was previously linked.
I was having an issue with the y-axis in my 3D printer. It had suddenly started producing intense low frequency vibrations when running:
That… doesn’t sound good.
From the Archives: Logging accelerometer data from the micro:bit
Another one from the archives, hence the date. I’m republishing this one because it has some interesting information about the micro:bit’s accelerometer, though the actual data logging part might be a bit moot given the improvements that have occurred to MakeCode’s functionality.
What would we need to do to have the micro:bit be a useful logger of acceleration data?
- Set a sampling rate
- Store data on the micro:bit’s local storage
- Play the data back or transfer it to a computer for analysis
From the Archives: The micro:bit and motors
Experimenting with controlling my @codemyrobot with a #microbit (on loan from @KidsCoding and @afmcdnl)! Excited for #LNDLCA 2018 where I'll be working with educators to develop coding and computational thinking skills! (cc @mshagerman @Megan_C_K @edukatik) pic.twitter.com/aYGmRuKPwH
— Jonathan (@jernwerber) June 7, 2018From the Archives: Learning a new platform (micro:bit)!
This is a post that’s been imported from an older blog, hence the date. Some of the information might be out of date, e.g. there’s now a new version of the micro:bit with some enhanced functionality. Still, I think the high-level/general concepts hold, as does much of the commentary, so I’m re-posting this with this disclaimer.
From the Archives: Living in interesting times
This is an old post dug up from a previous personal blog, hence the date. I’m older now, and hopefully at least a little bit wiser. I wonder how past-me would react knowing *just how interesting* the world (and times) would become just a short few years later.
subscribe via RSS