{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Dask array rounding\n", "> Dask array rounding float32\n", "- categories: [python, dask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook is an extract from the [Dask array Tutorial notebook](https://github.com/dask/dask-tutorial/blob/master/03_array.ipynb), see also the Youtube SciPy 2020 class at .\n", "\n", "We notice that `dask` is automatically rounding `float32` numbers to machine precision, which I think is the most sensible choice, but surprising difference compared to `numpy`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create the input data\n", "\n", "Needs only to be done once, defaults to ~4GB of data, you can reduce it by setting `blocksize` to a smaller number, e.g. 1000" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import os\n", "import h5py\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%time\n", "\n", "blocksize = 1000000\n", "nblocks = 1000\n", "shape = nblocks * blocksize\n", "\n", "if not os.path.exists('random.hdf5'):\n", "\n", " with h5py.File('random.hdf5', mode='w') as f:\n", " dset = f.create_dataset('/x', shape=(shape,), dtype='f4')\n", " for i in range(0, shape, blocksize):\n", " dset[i: i + blocksize] = np.random.exponential(size=blocksize)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from dask.distributed import Client\n", "\n", "client = Client(n_workers=24, processes=False)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# Load data with h5py\n", "# this creates a pointer to the data, but does not actually load\n", "import h5py\n", "import os\n", "f = h5py.File('random.hdf5', mode='r')\n", "dset = f['/x']" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Array Chunk
Bytes 4.00 GB 40.00 MB
Shape (1000000000,) (10000000,)
Count 101 Tasks 100 Chunks
Type float32 numpy.ndarray
\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", "\n", " \n", " 1000000000\n", " 1\n", "\n", "\n", "\n", "" ], "text/plain": [ "dask.array" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import dask.array as da\n", "import numpy as np\n", "x = da.from_array(dset, chunks=(10000000,))\n", "x" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "x_float64 = x.astype(np.float64)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "999976700.0" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.sum().compute()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The machine resolution of `float32` is `1e-6`, therefore everything after the 7th digit is garbage, so it is reasonable to remove it, otherwise it gives you the impression that the computation is more precise than it actually it.\n", "Still I am surprised `dask` does it, `numpy` above instead doesn't care about that and prints all the digits.\n", "\n", "If we need more precision, we need to increase the precision of the calculation, see below, but we are going to use a lot more memory, also, the input data were `float32`, so it is not very useful anyway, we should generate again the input with higher precision." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.finfo(np.float32)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Array Chunk
Bytes 8 B 8 B
Shape () ()
Count 336 Tasks 1 Chunks
Type float64 numpy.ndarray
\n", "
\n", "\n", "
" ], "text/plain": [ "dask.array" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x_float64.sum()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "999976584.1788422" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x_float64.sum().compute()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "client.shutdown()" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.8" } }, "nbformat": 4, "nbformat_minor": 4 }