or, an ordinal boxplot with no q1 or q3 data. A boxplot without the box.
A visualization of monthly-attached-hmds
Road to VR ran the figures against the most recent Hardware & Software Survey (September 2018), and found the following counts for headsets (head-mounted displays, hmds) connected to users’ computers over the course of the month.
It’s worth noting that the Steam Hardware & Software Survey counts how many users have headsets attached to their computers; even if the user didn’t actually use the headset to play a game in the given month, it would still be counted. So it would be more accurate to call the figures above ‘Monthly Attached VR Headsets on Steam’ rather than ‘Monthly Active VR Users on Steam’, though this is the closest indicator we’ve got to the latter. Actual unit sales are likely significantly higher than the ‘headsets attached’ figures that we have here, but none of the above manufacturers have released official figures at this point.
https://www.roadtovr.com/new-valve-data-suggests-648000-active-vr-headsets-on-steam/amp/
d3.csv('./monthly-attached-hmds.csv').then(data => boxplot(data))
function boxplot(data) {
// setup the plot area
const h = 500
const w = 500
const margin = {
top: 20,
bottom: 40,
left: 20,
right: 30
}
const innerHeight = h - margin.bottom
const innerWidth = w - margin.right
const outerHeight = 500
const outerWidth = 960
d3.select('body')
.append('svg')
.attr('height', outerHeight)
.attr('width', outerWidth)
//
// map variables in the data to types
//
// categorical variable
const catVariable = 'VR HMD'
const catValues = data.map(d => d[catVariable])
console.log('catValues', catValues)
// numeric variable statistics
const minVariable = 'min user count estimate'
const maxVariable = 'max user count estimate'
const medianVariable = 'median user count estimate'
const q1Variable = undefined
const q3Variable = undefined
// ensure numeric values read from csv data are numbers
data.forEach(d => {
if (minVariable) d[minVariable] = Number(d[minVariable])
if (maxVariable) d[maxVariable] = Number(d[maxVariable])
if (medianVariable) d[medianVariable] = Number(d[medianVariable])
if (q1Variable) d[q1Variable] = Number(d[q1Variable])
if (q3Variable) d[q3Variable] = Number(d[q3Variable])
})
// define the scales
xScale = d3
.scaleLinear()
.domain([
d3.min(data.map(d => d[minVariable])),
d3.max(data.map(d => d[maxVariable])) * 1.1
])
.rangeRound([margin.left, innerWidth])
yScale = d3
.scaleBand()
.domain(catValues)
.rangeRound([0, innerHeight])
console.log(data)
//
// setup the axes
//
// x-axis
const xTranslate = innerHeight
xAxis = d3
.axisBottom()
.scale(xScale)
.ticks(7)
.tickFormat(d3.format('~s'))
.tickSize(-xTranslate)
d3.select('svg')
.append('g')
.attr('transform', `translate(0,${xTranslate})`)
.attr('id', 'xAxisG')
.call(xAxis)
// text label for the x axis
d3.select('svg')
.append('text')
.attr('transform', `translate(${w / 2},${h - 5})`)
.style('text-anchor', 'middle')
.text('Steam Monthly Attached Head-Mounted Displays')
// y-axis
const yTranslate = innerWidth
yAxis = d3
.axisRight()
.scale(yScale)
.tickSize(-yTranslate)
.tickValues(catValues)
d3.select('svg')
.append('g')
.attr('transform', `translate(${yTranslate},0)`)
.attr('id', 'yAxisG')
.call(yAxis)
d3.select('svg')
.selectAll('g.box')
.data(data)
.enter()
.append('g')
.attr('class', 'box')
.attr('transform', d => {
const yValue = d[catVariable]
const yValueScaled = yScale(yValue)
const yOffset = margin.top * 2
// console.log('d', d)
console.log('yValue', yValue)
console.log('yValueScaled', yValueScaled)
return `translate(${xScale(d[medianVariable])},${yValueScaled + yOffset})`
})
.each(function(d, i) {
const minScreen = xScale(d[minVariable])
const medianScreen = xScale(d[medianVariable])
const maxScreen = xScale(d[maxVariable])
console.log('d', d)
console.log('minScreen', minScreen)
console.log('medianScreen', medianScreen)
console.log('maxScreen', maxScreen)
d3.select(this)
.append('line')
.attr('class', 'range')
.attr('x1', xScale(d[maxVariable]) - xScale(d[medianVariable]))
.attr('x2', xScale(d[minVariable]) - xScale(d[medianVariable]))
.attr('y1', 0)
.attr('y2', 0)
.style('stroke', 'black')
.style('stroke-width', '3px')
d3.select(this)
.append('line')
.attr('class', 'max')
.attr('x1', xScale(d[maxVariable]) - xScale(d[medianVariable]))
.attr('x2', xScale(d[maxVariable]) - xScale(d[medianVariable]))
.attr('y1', -10)
.attr('y2', 10)
.style('stroke', 'black')
.style('stroke-width', '3px')
d3.select(this)
.append('line')
.attr('class', 'min')
.attr('x1', xScale(d[minVariable]) - xScale(d[medianVariable]))
.attr('x2', xScale(d[minVariable]) - xScale(d[medianVariable]))
.attr('y1', -10)
.attr('y2', 10)
.style('stroke', 'black')
.style('stroke-width', '3px')
// only draw the box if q1 and q3 variables are defined
if (q1Variable && q3Variable) {
d3.select(this)
.append('rect')
.attr('class', 'range')
.attr('x', xScale(d[q1Variable]) - xScale(d[medianVariable]))
.attr('y', -10)
.attr('height', 20)
.attr('width', xScale(d[q3Variable]) - xScale(d[q1Variable]))
.style('fill', 'white')
.style('stroke', 'black')
.style('stroke-width', '3px')
}
// median
d3.select(this)
.append('line')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', -5)
.attr('y2', 5)
.style('stroke', 'darkgray')
.style('stroke-width', '3px')
})
}
<DOCTYPE !html>
<html>
<head>
<title>Tie Fighter Marks</title>
<meta charset="utf-8" />
<style>
/* svg {
border: 1px solid gray;
}*/
line {
shape-rendering: crispEdges;
stroke: lightgray;
}
line.minor {
stroke: #777777;
stroke-dasharray: 2,2;
}
path.domain {
fill: none;
stroke: lightgray;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js" type="text/JavaScript"></script>
<script src='./index.js'></script>
</body>
</html>
Steam Monthly Active Users,min % ,% that have,max %,VR HMD,min user count estimate,median user count estimate,max user count estimate,YYYYMM,reporter,source
90000000,0.335,0.34,0.3449,Oculus Rift,301500,306000,310410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined
90000000,0.305,0.31,0.3149,HTC Vive,274500,279000,283410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined
90000000,0.045,0.05,0.0549,Windows Mixed Reality,40500,45000,49410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined
90000000,0.005,0.01,0.0149,HTC Vive Pro,4500,9000,13410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined
90000000,0.005,0.01,0.0149,Oculus Rift DK2,4500,9000,13410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined
90000000,0,0,0.0049,Oculus Rift DK1,0,0,4410,201809,@micahstubbs,https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam?platform=combined