A good[ish] website
Web development blog, loads of UI and JavaScript topics
How to make simple data plotting with pure CSS. The point I had here was not to mathematically verify anything, but just to map out the curve and visually inspect it for any anomalies.
In a previous post about getting a random item from an array I used a helper that churns out random integers. And I got to thinking, how random it actually is? Is there a way for me to visualize it?
There is a way to visualize it, and it’s this random:
That’s simply 1000 flexboxed divs, not using any graph libs. The divs have a height of: occurrence count
* 10
pixels.
The MDN article on the random method has an example piece of code to get random ints in a range:
const getRandomIntInclusive = (min, max) => {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1) + min)
}
And it says:
It might be tempting to use
Math.round()
to accomplish that, but doing so would cause your random numbers to follow a non-uniform distribution, which may not be acceptable for your needs.
So I made a random number generator that uses the Math.round()
, you can choose it from the dropdown on the above interactive graph. The distribution of the values is not shockingly different (at least on this magnitude), the visual difference isn’t striking.
const getRandomIntRound = (min, max) => {
min = Math.round(min)
max = Math.round(max)
return Math.round(Math.random() * (max - min + 1) + min)
}
The random number functions used here are listed above. But if you don’t need the min value, it can be made a bit simpler looking:
const getRandomInt = max => Math.floor(Math.random() * max)
I’m making 10 000 random numbers with a range from 0 to 1000, that should be enough. Create an array and fill it with randomness:
const randomNumbers = Array.from({ length: 10000 }, () => getRandomInt(1000))
Then let’s find out how many times any given random number appears in the array. The occurrences can be dug out with a simple reduce:
const getOccurrences = array =>
array.reduce((acc, randomNumber) => {
acc[randomNumber] = ++acc[randomNumber] || 1
return acc
}, {})
That returns a object like this one below (truncated), where key is the random number, and the value is how many times it occurs in the array:
{
"0": 8,
"1": 13,
"2": 12,
"3": 9,
"4": 7,
"5": 9,
"6": 9,
"7": 11,
"8": 13,
"9": 11,
"10": 14,
"11": 11,
"12": 10,
"13": 7,
"14": 9,
"15": 3,
"16": 12,
"17": 9,
...
}
From it, you can already see that the distribution looks pretty random.
Then we need a helper function to sort the data, let’s use getOccurrences()
helper here, and remember the generate the random data inside the function, or the refresh won’t actually get fresh data:
const getRandomData = (isSorted, randomMethod) => {
// Generate these inside this function so the refresh works.
const randomNumbers = Array.from({ length: 10000 }, () =>
randomMethod === 'round'
? getRandomIntRound(0, 1000)
: getRandomIntInclusive(0, 1000)
)
const occurrences = getOccurrences(randomNumbers)
const arr = Object.keys(occurrences).map(x => occurrences[x])
return isSorted
? arr.sort((a, b) => {
if (a < b) return -1
if (a > b) return 1
return 0
})
: arr
}
I’m using styled-components here, but you-do-you.
The refreshing is kind of interesting: React re-renders a component if you update its state, but in this case there’s not really a change to be made, so I’m updating a state that uses itself. Also, the random data needs to be generated inside the getRandomData
helper.
The other click listeners is for the sort and the select:
const Bar = styled.div(props => ({
alignItems: 'flex-end',
backgroundColor: 'Gray',
height: props.elementHeight * 10 + 'px',
width: '0.1%'
}))
const Wrap = styled.div({
border: '1px solid var(--gray)',
marginBottom: '20px',
padding: '10px'
})
const Container = styled.div({
alignItems: 'flex-end',
display: 'flex',
height: '240px',
marginBottom: '10px',
width: '100%'
})
const Button = styled.button({
marginRight: '10px'
})
const Chart = ({ shouldSort }) => {
const [isSorted, setIsSorted] = React.useState(shouldSort)
const [remountCount, setRemountCount] = React.useState(0)
const [randomMethod, setRandomMethod] = React.useState('normal') const refresh = () => setRemountCount(remountCount + 1)
const handleSort = () => {
setIsSorted(prev => !prev)
}
const handleChange = event => {
setRandomMethod(event.target.value)
}
return (
<Wrap>
<Container>
{getRandomData(isSorted, randomMethod).map((key, index) => (
<Bar key={index} elementHeight={key} />
))}
</Container>
<Button onClick={refresh}>Refresh</Button> <Button onClick={handleSort}>{isSorted ? 'Unsort' : 'Sort'}</Button> <select onChange={handleChange}>
<option value="ceil-floor">With ceil/floor</option>
<option value="round">With round</option>
</select>
</Wrap>
)
}
That's it pretty much.
Could totally render the graph onto a canvas, would be faster and better.
The results here weren’t that interesting, the Math.round()
randomness doesn’t look that different (or maybe I’m just mangling the data in a wrong way..). This was a bit pointless, but kind of fun.
Comments would go here, but the commenting system isn’t ready yet, sorry.