Published
Edited
May 1, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`

## Confidence Checks for Our Napkin Estimates
We can do some confidence checks using public stats about YouTube!

YouTube reports having two billion users and "billions of views" per day (https://blog.youtube/press). Assuming "billions of views" refers to somewhere between 2 billion and 9 billion, we can guess each user averages out to somewhere between 1-9 views per day, or 30 to 270 views per month.

YouTube takes 45% of ad revenue (https://www.tubefilter.com/2020/02/03/youtube-ad-revenue-how-much-does-youtube-make-from-ads-2017-2018-2019/). Charging $5 for a thousand ad impressions, YouTube would earn $2.25 per 1000 views.

Let's also assume 25% of desktop users use ad block (https://www.statista.com/statistics/804008/ad-blocking-reach-usage-us/). Since 70% of traffic comes from mobile (where ad blocking requires much more effort), we have 2e9 * (0.7) + 2e9 * 0.3 * 0.75 = 1.85e9 non-ad-blocking users.

Putting these together, we'd expect, within a month, YouTube to be making about

30 views / user x 1.5e9 users x 2.25 dollars / 1000 views = x dollars

Putting it into Javascript below, we can check if this gets us somewhere close to YouTube's actual 2019 revenue estimate: 15B USD (https://www.tubefilter.com/2020/02/03/youtube-ad-revenue-how-much-does-youtube-make-from-ads-2017-2018-2019/), or 1.5B USD per month.
`
Insert cell
[30 * 1.85e9 * 2.25 / 1000, 270 * 1.85e9 * 2.25 / 1000].map(x=> x /1e9)
Insert cell
Insert cell
Insert cell
numberOfUsers = numberOfUsersInThousands * 1000
Insert cell
lostHr = maxHr - curHr
Insert cell
lostMoneyPerUser = lostHr * subCostPerYear
Insert cell
subEstimate = lostMoneyPerUser * numberOfUsersInThousands
Insert cell
Insert cell
revPerView = adRevPerThousandViews * 0.45 / 1000
Insert cell
Insert cell
recFrac = 0.7
Insert cell
freeViews = viewsPerUser * (1-recFrac)
Insert cell
recViews = viewsPerUser * (recFrac)
Insert cell
maxViews = freeViews + recViews
Insert cell
topPopHitRate = 0.46
Insert cell
md`
"Lost Personalization" is what fraction of the way towards "unpersonalized" baseline the strike brings the system.
`
Insert cell
lostPersonalization = (maxHr - curHr) / (maxHr - topPopHitRate)
Insert cell
//hrRatio = curHr / maxHr
Insert cell
Insert cell
recViewsAtBaseline = 0.5
Insert cell
lostViewsPerUser = recViews * (lostPersonalization)
Insert cell
adEstimate = lostViewsPerUser * numberOfUsersInThousands * revPerView
Insert cell
Insert cell
barData
Insert cell
Insert cell
md`
## Data
`
Insert cell
raw = FileAttachment("stacked@3.csv").csv()
Insert cell
barData = raw.filter(d => d.lever_size == lever_size && d.lever_genre == genre && d.lever_type == lever_type)
Insert cell
lineDataTmp = raw.filter(d => d.lever_type == lever_type && d.variable == "hr")
Insert cell
function foo(a){
return {
name: a,
values: lineDataTmp.filter(b=> b.lever_genre == a).map(x=>x.value)
};
};
Insert cell
linesData = {
const y = "Hit Rate";
const series = genres.map(foo);
const sizes = lineDataTmp.filter(x=> x.lever_genre == "All").map(x=>x.lever_size);
return {y, series, sizes};
}
Insert cell
index = linesData.sizes.indexOf(String(lever_size));
Insert cell
curHr = singlePoint.series.values[0]
Insert cell
Insert cell
maxHr = d3.max(linesData.series, d => d3.max(d.values))
Insert cell
Insert cell
Insert cell
xLine = d3.scaleLinear()
.domain(d3.extent(linesData.sizes))
.range([margin.left, width - margin.right])
Insert cell
yLine = d3.scaleLinear()
.domain([0, maxHr]).nice()
.range([heightOfLinesPlot - margin.bottom, margin.top])
Insert cell
xAxisLine = g => g
.attr("transform", `translate(0,${heightOfLinesPlot - margin.bottom})`)
.call(d3.axisBottom(xLine).ticks(width / 80).tickSizeOuter(0))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 10)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Lever Size"))
//.text("Lever Size")
Insert cell
yAxisLine = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yLine))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(linesData.y))
Insert cell
line = d3.line()
//.defined(d => !isNaN(d.value))
.x((d, i) => xLine(linesData.sizes[i]))
.y(d => yLine(d))
Insert cell
Insert cell
Insert cell
Insert cell
//colNames = barData.map(x=>x.variable);

Insert cell
Insert cell
format = xBar.tickFormat(20, barData.format)
Insert cell
xBar = d3.scaleLinear()
.domain([0.0, d3.max(raw, d => d[xname])])
.range([margin.left, width - margin.right])
Insert cell
yBar = d3.scaleBand()
.domain(d3.range(barData.length))
.rangeRound([margin.top, heightOfBarPlot - margin.bottom])
.padding(0.1)
Insert cell
xAxisBar = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(xBar).ticks(width / 80, barData.format))
.call(g => g.select(".domain").remove())
Insert cell
yAxisBar = g => g
.attr("transform", `translate(${width},0)`)
.call(d3.axisLeft(yBar).tickFormat(i => barData[i].variable).tickSizeOuter(0))
Insert cell
heightOfBar = 15
Insert cell
heightOfBarPlot = Math.ceil((barData.length + 0.1) * heightOfBar) + margin.top + margin.bottom
Insert cell
Insert cell
xMoney = d3.scaleLinear()
.domain([0.0, d3.max(moneyData, x => x.value)])
.range([margin.left, width - margin.right])
Insert cell
yMoney = d3.scaleBand()
.domain(d3.range(moneyData.length))
.rangeRound([margin.top, heightOfBarPlot - margin.bottom])
.padding(0.1)
Insert cell
xAxisMoney = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(xMoney).ticks(width / 80, moneyData.format))
.call(g => g.select(".domain").remove())
Insert cell
yAxisMoney = g => g
.attr("transform", `translate(${width},0)`)
.call(d3.axisLeft(yMoney).tickFormat(i => moneyData[i].name).tickSizeOuter(0))
Insert cell
Insert cell
supported_sizes = [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7];
Insert cell
margin = ({top: 30, right: 100, bottom: 20, left: 50})
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
import {Button, Checkbox, Toggle, Radio, Range, Select, Text, Textarea, Search, Table} from "@observablehq/inputs"
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more