Different approaches of creating the test set
It gets a little complicated, I've attached links at the end of the answer to explain as well. 

def test_set_check(identifier, test_ratio, hash=hashlib.md5):
    return bytearray(hash(np.int64(identifier)).digest())[-1] &lt; 256 * test_ratio


The hash(np.int64(identifier)).digest() part returns a hash value of the identifier (which we cast as an int of 8 bytes with np.int64()). 

The bytearray method stores the hash value into an array. The [-1] represents getting 1 byte/the last byte from the array/hash value. This byte will be a # between 0 and 255 (1 byte=11111111, or 255 in decimal).

Assuming our test_ratio is .20, the byte value should be less than or equal to 51 (256*.20=51.2). If the byte is less than or equal to 51, the row/instance will be added to the test set.

Rest is explained well in these links:

https://stackoverflow.com/questions/50646890/how-does-the-crc32-function-work-when-using-sampling-data

https://github.com/ageron/handson-ml/issues/71

https://docs.python.org/3/library/hashlib.html