{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Dask DataFrames\n", "\n", "\"Dask\n", " \n", "Dask Dataframes coordinate many Pandas dataframes, partitioned along an index. They support a large subset of the Pandas API." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Start Dask Client for Dashboard\n", "\n", "Starting the Dask Client is optional. It will provide a dashboard which \n", "is useful to gain insight on the computation. \n", "\n", "The link to the dashboard will become visible when you create the client below. We recommend having it open on one side of your screen while using your notebook on the other side. This can take some effort to arrange your windows, but seeing them both at the same is very useful when learning." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:11.664867Z", "iopub.status.busy": "2022-07-27T19:11:11.664611Z", "iopub.status.idle": "2022-07-27T19:11:13.808579Z", "shell.execute_reply": "2022-07-27T19:11:13.807922Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "
\n", "

Client

\n", "

Client-e0ebc4ca-0ddf-11ed-98b5-000d3a8f7959

\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
Connection method: Cluster objectCluster type: distributed.LocalCluster
\n", " Dashboard: http://127.0.0.1:8787/status\n", "
\n", "\n", " \n", "
\n", "

Cluster Info

\n", "
\n", "
\n", "
\n", "
\n", "

LocalCluster

\n", "

fb3316a9

\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", "
\n", " Dashboard: http://127.0.0.1:8787/status\n", " \n", " Workers: 2\n", "
\n", " Total threads: 4\n", " \n", " Total memory: 1.86 GiB\n", "
Status: runningUsing processes: True
\n", "\n", "
\n", " \n", "

Scheduler Info

\n", "
\n", "\n", "
\n", "
\n", "
\n", "
\n", "

Scheduler

\n", "

Scheduler-44cb79c2-de9d-4ec5-8466-954570037d71

\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Comm: tcp://127.0.0.1:40039\n", " \n", " Workers: 2\n", "
\n", " Dashboard: http://127.0.0.1:8787/status\n", " \n", " Total threads: 4\n", "
\n", " Started: Just now\n", " \n", " Total memory: 1.86 GiB\n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "

Workers

\n", "
\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", "

Worker: 0

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", "\n", " \n", "\n", "
\n", " Comm: tcp://127.0.0.1:38545\n", " \n", " Total threads: 2\n", "
\n", " Dashboard: http://127.0.0.1:38797/status\n", " \n", " Memory: 0.93 GiB\n", "
\n", " Nanny: tcp://127.0.0.1:41967\n", "
\n", " Local directory: /home/runner/work/dask-examples/dask-examples/dask-worker-space/worker-7ivifrfo\n", "
\n", "
\n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", "

Worker: 1

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", "\n", " \n", "\n", "
\n", " Comm: tcp://127.0.0.1:36071\n", " \n", " Total threads: 2\n", "
\n", " Dashboard: http://127.0.0.1:46269/status\n", " \n", " Memory: 0.93 GiB\n", "
\n", " Nanny: tcp://127.0.0.1:37775\n", "
\n", " Local directory: /home/runner/work/dask-examples/dask-examples/dask-worker-space/worker-7ftt40fj\n", "
\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
\n", "\n", "
\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
" ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dask.distributed import Client\n", "\n", "client = Client(n_workers=2, threads_per_worker=2, memory_limit=\"1GB\")\n", "client\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Random Dataframe\n", "\n", "We create a random timeseries of data with the following attributes:\n", "\n", "1. It stores a record for every second in the month of January of the year 2000\n", "2. It splits that month by day, keeping each day as a partitioned dataframe\n", "3. Along with a datetime index it has columns for names, ids, and numeric values\n", "\n", "This is a small dataset of about 240 MB. Increase the number of days or reduce the time interval between data points to practice with a larger dataset by setting some of the [`dask.datasets.timeseries()` arguments](https://docs.dask.org/en/stable/api.html#dask.datasets.timeseries)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:13.812754Z", "iopub.status.busy": "2022-07-27T19:11:13.812204Z", "iopub.status.idle": "2022-07-27T19:11:14.145983Z", "shell.execute_reply": "2022-07-27T19:11:14.144719Z" } }, "outputs": [], "source": [ "import dask\n", "\n", "df = dask.datasets.timeseries()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unlike Pandas, Dask DataFrames are _lazy_, meaning that data is only loaded when it is needed for a computation. No data is printed here, instead it is replaced by ellipses (`...`)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.149561Z", "iopub.status.busy": "2022-07-27T19:11:14.149084Z", "iopub.status.idle": "2022-07-27T19:11:14.166539Z", "shell.execute_reply": "2022-07-27T19:11:14.165923Z" } }, "outputs": [ { "data": { "text/html": [ "
Dask DataFrame Structure:
\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", "
idnamexy
npartitions=30
2000-01-01int64objectfloat64float64
2000-01-02............
...............
2000-01-30............
2000-01-31............
\n", "
\n", "
Dask Name: make-timeseries, 30 tasks
" ], "text/plain": [ "Dask DataFrame Structure:\n", " id name x y\n", "npartitions=30 \n", "2000-01-01 int64 object float64 float64\n", "2000-01-02 ... ... ... ...\n", "... ... ... ... ...\n", "2000-01-30 ... ... ... ...\n", "2000-01-31 ... ... ... ...\n", "Dask Name: make-timeseries, 30 tasks" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nonetheless, the column names and dtypes are known." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.169923Z", "iopub.status.busy": "2022-07-27T19:11:14.169502Z", "iopub.status.idle": "2022-07-27T19:11:14.175120Z", "shell.execute_reply": "2022-07-27T19:11:14.174496Z" } }, "outputs": [ { "data": { "text/plain": [ "id int64\n", "name object\n", "x float64\n", "y float64\n", "dtype: object" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.dtypes\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some operations will automatically display the data." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.177965Z", "iopub.status.busy": "2022-07-27T19:11:14.177754Z", "iopub.status.idle": "2022-07-27T19:11:14.181013Z", "shell.execute_reply": "2022-07-27T19:11:14.180414Z" } }, "outputs": [], "source": [ "# This sets some formatting parameters for displayed data.\n", "import pandas as pd\n", "\n", "pd.options.display.precision = 2\n", "pd.options.display.max_rows = 10\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.184009Z", "iopub.status.busy": "2022-07-27T19:11:14.183815Z", "iopub.status.idle": "2022-07-27T19:11:14.658179Z", "shell.execute_reply": "2022-07-27T19:11:14.657005Z" } }, "outputs": [ { "data": { "text/html": [ "
\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", "
idnamexy
timestamp
2000-01-01 00:00:00999Patricia0.860.50
2000-01-01 00:00:01974Alice-0.040.25
2000-01-01 00:00:02984Ursula-0.05-0.92
\n", "
" ], "text/plain": [ " id name x y\n", "timestamp \n", "2000-01-01 00:00:00 999 Patricia 0.86 0.50\n", "2000-01-01 00:00:01 974 Alice -0.04 0.25\n", "2000-01-01 00:00:02 984 Ursula -0.05 -0.92" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head(3)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use Standard Pandas Operations\n", "\n", "Most common Pandas operations can be used in the same way on Dask dataframes. This example shows how to slice the data based on a mask condition and then determine the standard deviation of the data in the `x` column." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.661483Z", "iopub.status.busy": "2022-07-27T19:11:14.661276Z", "iopub.status.idle": "2022-07-27T19:11:14.689321Z", "shell.execute_reply": "2022-07-27T19:11:14.688694Z" } }, "outputs": [ { "data": { "text/plain": [ "Dask Series Structure:\n", "npartitions=1\n", " float64\n", " ...\n", "Name: x, dtype: float64\n", "Dask Name: sqrt, 157 tasks" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df2 = df[df.y > 0]\n", "df3 = df2.groupby(\"name\").x.std()\n", "df3\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the data in `df3` are still represented by ellipses. All of the operations in the previous cell are lazy operations. You can call `.compute()` when you want your result as a Pandas dataframe or series.\n", "\n", "If you started `Client()` above then you can watch the status page during computation to see the progress." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:14.692387Z", "iopub.status.busy": "2022-07-27T19:11:14.692073Z", "iopub.status.idle": "2022-07-27T19:11:15.741650Z", "shell.execute_reply": "2022-07-27T19:11:15.740555Z" } }, "outputs": [ { "data": { "text/plain": [ "pandas.core.series.Series" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "computed_df = df3.compute()\n", "type(computed_df)\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:15.745384Z", "iopub.status.busy": "2022-07-27T19:11:15.744926Z", "iopub.status.idle": "2022-07-27T19:11:15.753072Z", "shell.execute_reply": "2022-07-27T19:11:15.751533Z" } }, "outputs": [ { "data": { "text/plain": [ "name\n", "Alice 0.58\n", "Bob 0.58\n", "Charlie 0.58\n", "Dan 0.58\n", "Edith 0.58\n", " ... \n", "Victor 0.58\n", "Wendy 0.58\n", "Xavier 0.58\n", "Yvonne 0.58\n", "Zelda 0.58\n", "Name: x, Length: 26, dtype: float64" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "computed_df\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the computed data are now shown in the output.\n", "\n", "Another example calculation is to aggregate multiple columns, as shown below. Once again, the dashboard will show the progress of the computation." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:15.755947Z", "iopub.status.busy": "2022-07-27T19:11:15.755556Z", "iopub.status.idle": "2022-07-27T19:11:16.377459Z", "shell.execute_reply": "2022-07-27T19:11:16.376809Z" } }, "outputs": [ { "data": { "text/html": [ "
\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", "
xy
name
Alice172.301.0
Bob54.791.0
Charlie255.011.0
Dan93.231.0
Edith155.411.0
.........
Victor-303.801.0
Wendy-20.971.0
Xavier112.181.0
Yvonne335.841.0
Zelda205.431.0
\n", "

26 rows × 2 columns

\n", "
" ], "text/plain": [ " x y\n", "name \n", "Alice 172.30 1.0\n", "Bob 54.79 1.0\n", "Charlie 255.01 1.0\n", "Dan 93.23 1.0\n", "Edith 155.41 1.0\n", "... ... ...\n", "Victor -303.80 1.0\n", "Wendy -20.97 1.0\n", "Xavier 112.18 1.0\n", "Yvonne 335.84 1.0\n", "Zelda 205.43 1.0\n", "\n", "[26 rows x 2 columns]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df4 = df.groupby(\"name\").aggregate({\"x\": \"sum\", \"y\": \"max\"})\n", "df4.compute()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dask dataframes can also be joined like Pandas dataframes. In this example we join the aggregated data in `df4` with the original data in `df`. Since the index in `df` is the timeseries and `df4` is indexed by names, we use `left_on=\"name\"` and `right_index=True` to define the merge columns. We also set suffixes for any columns that are common between the two dataframes so that we can distinguish them.\n", "\n", "Finally, since `df4` is small, we also make sure that it is a single partition dataframe." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:16.381151Z", "iopub.status.busy": "2022-07-27T19:11:16.380740Z", "iopub.status.idle": "2022-07-27T19:11:17.123758Z", "shell.execute_reply": "2022-07-27T19:11:17.123250Z" } }, "outputs": [ { "data": { "text/html": [ "
\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", "
idnamex_originaly_originalx_aggregatedy_aggregated
timestamp
2000-01-01 00:00:00999Patricia0.860.50-34.681.0
2000-01-01 00:00:03988Patricia-0.57-0.67-34.681.0
2000-01-01 00:00:121038Patricia-0.480.35-34.681.0
2000-01-01 00:01:16964Patricia-0.250.13-34.681.0
2000-01-01 00:01:331050Patricia-0.58-0.38-34.681.0
\n", "
" ], "text/plain": [ " id name x_original y_original x_aggregated \\\n", "timestamp \n", "2000-01-01 00:00:00 999 Patricia 0.86 0.50 -34.68 \n", "2000-01-01 00:00:03 988 Patricia -0.57 -0.67 -34.68 \n", "2000-01-01 00:00:12 1038 Patricia -0.48 0.35 -34.68 \n", "2000-01-01 00:01:16 964 Patricia -0.25 0.13 -34.68 \n", "2000-01-01 00:01:33 1050 Patricia -0.58 -0.38 -34.68 \n", "\n", " y_aggregated \n", "timestamp \n", "2000-01-01 00:00:00 1.0 \n", "2000-01-01 00:00:03 1.0 \n", "2000-01-01 00:00:12 1.0 \n", "2000-01-01 00:01:16 1.0 \n", "2000-01-01 00:01:33 1.0 " ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df4 = df4.repartition(npartitions=1)\n", "joined = df.merge(\n", " df4, left_on=\"name\", right_index=True, suffixes=(\"_original\", \"_aggregated\")\n", ")\n", "joined.head()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Persist data in memory\n", "\n", "If you have the available RAM for your dataset then you can persist data in memory. \n", "\n", "This allows future computations to be much faster." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:17.127751Z", "iopub.status.busy": "2022-07-27T19:11:17.127337Z", "iopub.status.idle": "2022-07-27T19:11:17.163137Z", "shell.execute_reply": "2022-07-27T19:11:17.147526Z" } }, "outputs": [], "source": [ "df = df.persist()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Time Series Operations\n", "\n", "Because `df` has a datetime index, time-series operations work efficiently.\n", "\n", "The first example below resamples the data at 1 hour intervals to reduce the total size of the dataframe. Then the mean of the `x` and `y` columns are taken." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:17.168212Z", "iopub.status.busy": "2022-07-27T19:11:17.166242Z", "iopub.status.idle": "2022-07-27T19:11:17.483174Z", "shell.execute_reply": "2022-07-27T19:11:17.482516Z" } }, "outputs": [ { "data": { "text/html": [ "
\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", "
xy
timestamp
2000-01-01 00:00:001.96e-031.43e-02
2000-01-01 01:00:005.23e-031.51e-02
2000-01-01 02:00:00-9.91e-04-9.25e-04
2000-01-01 03:00:00-2.57e-03-5.00e-03
2000-01-01 04:00:00-5.71e-031.16e-02
\n", "
" ], "text/plain": [ " x y\n", "timestamp \n", "2000-01-01 00:00:00 1.96e-03 1.43e-02\n", "2000-01-01 01:00:00 5.23e-03 1.51e-02\n", "2000-01-01 02:00:00 -9.91e-04 -9.25e-04\n", "2000-01-01 03:00:00 -2.57e-03 -5.00e-03\n", "2000-01-01 04:00:00 -5.71e-03 1.16e-02" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[[\"x\", \"y\"]].resample(\"1h\").mean().head()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next example resamples the data at 24 hour intervals and plots the mean values. Notice that `plot()` is called after `compute()` because `plot()` will not work until the data are computed." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:17.487780Z", "iopub.status.busy": "2022-07-27T19:11:17.486731Z", "iopub.status.idle": "2022-07-27T19:11:20.251345Z", "shell.execute_reply": "2022-07-27T19:11:20.250015Z" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "df[['x', 'y']].resample('24h').mean().compute().plot();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This final example computes the rolling 24 hour mean of the data." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:20.254643Z", "iopub.status.busy": "2022-07-27T19:11:20.254201Z", "iopub.status.idle": "2022-07-27T19:11:20.316021Z", "shell.execute_reply": "2022-07-27T19:11:20.315475Z" } }, "outputs": [ { "data": { "text/html": [ "
\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", "
xy
timestamp
2000-01-01 00:00:000.860.50
2000-01-01 00:00:010.410.38
2000-01-01 00:00:020.25-0.05
2000-01-01 00:00:030.05-0.21
2000-01-01 00:00:040.18-0.18
\n", "
" ], "text/plain": [ " x y\n", "timestamp \n", "2000-01-01 00:00:00 0.86 0.50\n", "2000-01-01 00:00:01 0.41 0.38\n", "2000-01-01 00:00:02 0.25 -0.05\n", "2000-01-01 00:00:03 0.05 -0.21\n", "2000-01-01 00:00:04 0.18 -0.18" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[[\"x\", \"y\"]].rolling(window=\"24h\").mean().head()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Random access is cheap along the index, but must since the Dask dataframe is lazy, it must be computed to materialize the data." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:20.319057Z", "iopub.status.busy": "2022-07-27T19:11:20.318630Z", "iopub.status.idle": "2022-07-27T19:11:20.336583Z", "shell.execute_reply": "2022-07-27T19:11:20.335897Z" } }, "outputs": [ { "data": { "text/html": [ "
Dask DataFrame Structure:
\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", "
idnamexy
npartitions=1
2000-01-05 00:00:00.000000000int64objectfloat64float64
2000-01-05 23:59:59.999999999............
\n", "
\n", "
Dask Name: loc, 31 tasks
" ], "text/plain": [ "Dask DataFrame Structure:\n", " id name x y\n", "npartitions=1 \n", "2000-01-05 00:00:00.000000000 int64 object float64 float64\n", "2000-01-05 23:59:59.999999999 ... ... ... ...\n", "Dask Name: loc, 31 tasks" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[\"2000-01-05\"]\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:20.339739Z", "iopub.status.busy": "2022-07-27T19:11:20.339405Z", "iopub.status.idle": "2022-07-27T19:11:20.422385Z", "shell.execute_reply": "2022-07-27T19:11:20.421524Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 28.7 ms, sys: 7.62 ms, total: 36.3 ms\n", "Wall time: 64.2 ms\n" ] }, { "data": { "text/html": [ "
\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", "
idnamexy
timestamp
2000-01-05 00:00:001001Hannah0.85-0.23
2000-01-05 00:00:011021Charlie-0.09-0.42
2000-01-05 00:00:02974Zelda0.70-0.81
2000-01-05 00:00:031015Sarah0.35-0.13
2000-01-05 00:00:04989Frank-0.26-0.96
...............
2000-01-05 23:59:551023Alice0.680.21
2000-01-05 23:59:56982Alice0.900.74
2000-01-05 23:59:57941Sarah-0.49-0.39
2000-01-05 23:59:581009Kevin0.89-0.26
2000-01-05 23:59:591031Hannah0.800.53
\n", "

86400 rows × 4 columns

\n", "
" ], "text/plain": [ " id name x y\n", "timestamp \n", "2000-01-05 00:00:00 1001 Hannah 0.85 -0.23\n", "2000-01-05 00:00:01 1021 Charlie -0.09 -0.42\n", "2000-01-05 00:00:02 974 Zelda 0.70 -0.81\n", "2000-01-05 00:00:03 1015 Sarah 0.35 -0.13\n", "2000-01-05 00:00:04 989 Frank -0.26 -0.96\n", "... ... ... ... ...\n", "2000-01-05 23:59:55 1023 Alice 0.68 0.21\n", "2000-01-05 23:59:56 982 Alice 0.90 0.74\n", "2000-01-05 23:59:57 941 Sarah -0.49 -0.39\n", "2000-01-05 23:59:58 1009 Kevin 0.89 -0.26\n", "2000-01-05 23:59:59 1031 Hannah 0.80 0.53\n", "\n", "[86400 rows x 4 columns]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%time df.loc['2000-01-05'].compute()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Set Index\n", "\n", "Data is sorted by the index column. This allows for faster access, joins, groupby-apply operations, and more. However sorting data can be costly to do in parallel, so setting the index is both important to do, but only infrequently. In the next few examples, we will group the data by the `name` column, so we will set that column as the index to improve efficiency." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:20.426038Z", "iopub.status.busy": "2022-07-27T19:11:20.425670Z", "iopub.status.idle": "2022-07-27T19:11:22.364173Z", "shell.execute_reply": "2022-07-27T19:11:22.363565Z" } }, "outputs": [ { "data": { "text/html": [ "
Dask DataFrame Structure:
\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", "
idxy
npartitions=26
Aliceint64float64float64
Bob.........
............
Zelda.........
Zelda.........
\n", "
\n", "
Dask Name: sort_index, 954 tasks
" ], "text/plain": [ "Dask DataFrame Structure:\n", " id x y\n", "npartitions=26 \n", "Alice int64 float64 float64\n", "Bob ... ... ...\n", "... ... ... ...\n", "Zelda ... ... ...\n", "Zelda ... ... ...\n", "Dask Name: sort_index, 954 tasks" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df5 = df.set_index(\"name\")\n", "df5\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because resetting the index for this dataset is expensive and we can fit it in our available RAM, we persist the dataset to memory." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:22.367256Z", "iopub.status.busy": "2022-07-27T19:11:22.367051Z", "iopub.status.idle": "2022-07-27T19:11:22.438671Z", "shell.execute_reply": "2022-07-27T19:11:22.413951Z" } }, "outputs": [ { "data": { "text/html": [ "
Dask DataFrame Structure:
\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", "
idxy
npartitions=26
Aliceint64float64float64
Bob.........
............
Zelda.........
Zelda.........
\n", "
\n", "
Dask Name: sort_index, 26 tasks
" ], "text/plain": [ "Dask DataFrame Structure:\n", " id x y\n", "npartitions=26 \n", "Alice int64 float64 float64\n", "Bob ... ... ...\n", "... ... ... ...\n", "Zelda ... ... ...\n", "Zelda ... ... ...\n", "Dask Name: sort_index, 26 tasks" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df5 = df5.persist()\n", "df5\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dask now knows where all data lives, indexed by name. As a result operations like random access are cheap and efficient." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:22.446073Z", "iopub.status.busy": "2022-07-27T19:11:22.445863Z", "iopub.status.idle": "2022-07-27T19:11:24.831016Z", "shell.execute_reply": "2022-07-27T19:11:24.830452Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 360 ms, sys: 44.7 ms, total: 404 ms\n", "Wall time: 2.35 s\n" ] }, { "data": { "text/html": [ "
\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", "
idxy
name
Alice974-0.040.25
Alice10010.440.55
Alice1039-0.380.88
Alice974-0.710.12
Alice9600.98-0.21
............
Alice9940.560.90
Alice9990.03-0.11
Alice988-0.49-0.80
Alice999-0.35-0.20
Alice1079-0.290.88
\n", "

99801 rows × 3 columns

\n", "
" ], "text/plain": [ " id x y\n", "name \n", "Alice 974 -0.04 0.25\n", "Alice 1001 0.44 0.55\n", "Alice 1039 -0.38 0.88\n", "Alice 974 -0.71 0.12\n", "Alice 960 0.98 -0.21\n", "... ... ... ...\n", "Alice 994 0.56 0.90\n", "Alice 999 0.03 -0.11\n", "Alice 988 -0.49 -0.80\n", "Alice 999 -0.35 -0.20\n", "Alice 1079 -0.29 0.88\n", "\n", "[99801 rows x 3 columns]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%time df5.loc['Alice'].compute()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Groupby Apply with Scikit-Learn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that our data is sorted by name we can inexpensively do operations like random access on name, or groupby-apply with custom functions.\n", "\n", "Here we train a different scikit-learn linear regression model on each name." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:24.838060Z", "iopub.status.busy": "2022-07-27T19:11:24.837859Z", "iopub.status.idle": "2022-07-27T19:11:25.701687Z", "shell.execute_reply": "2022-07-27T19:11:25.700967Z" } }, "outputs": [], "source": [ "from sklearn.linear_model import LinearRegression\n", "\n", "\n", "def train(partition):\n", " if not len(partition):\n", " return\n", " est = LinearRegression()\n", " est.fit(partition[[\"x\"]].values, partition.y.values)\n", " return est\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `partition` argument to `train()` will be one of the group instances from the `DataFrameGroupBy`. If there is no data in the partition, we don't need to proceed. If there is data, we want to fit the linear regression model and return that as the value for this group.\n", "\n", "Now working with `df5`, whose index is the names from `df`, we can group by the `names` column. This also happens to be the index, but that's fine. Then we use `.apply()` to run `train()` on each group in the `DataFrameGroupBy` generated by `.groupby()`.\n", "\n", "The `meta` argument tells Dask how to create the `DataFrame` or `Series` that will hold the result of `.apply()`. In this case, `train()` returns a single value, so `.apply()` will create a `Series`. This means we need to tell Dask what the type of that single column should be and optionally give it a name.\n", "\n", "The easiest way to specify a single column is with a tuple. The name of the column is the first element of the tuple. Since this is a series of linear regressions, we will name the column `\"LinearRegression\"`. The second element of the tuple is the type of the return value of `train`. In this case, Pandas will store the result as a general `object`, which should be the type we pass." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2022-07-27T19:11:25.705144Z", "iopub.status.busy": "2022-07-27T19:11:25.704922Z", "iopub.status.idle": "2022-07-27T19:11:26.886130Z", "shell.execute_reply": "2022-07-27T19:11:26.885615Z" } }, "outputs": [ { "data": { "text/plain": [ "name\n", "Alice LinearRegression()\n", "Bob LinearRegression()\n", "Charlie LinearRegression()\n", "Dan LinearRegression()\n", "Edith LinearRegression()\n", " ... \n", "Victor LinearRegression()\n", "Wendy LinearRegression()\n", "Xavier LinearRegression()\n", "Yvonne LinearRegression()\n", "Zelda LinearRegression()\n", "Name: LinearRegression, Length: 26, dtype: object" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df6 = df5.groupby(\"name\").apply(\n", " train, meta=(\"LinearRegression\", object)\n", ").compute()\n", "df6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further Reading\n", "\n", "For a more in-depth introduction to Dask dataframes, see the [dask tutorial](https://github.com/dask/dask-tutorial), notebooks 04 and 07." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.12" } }, "nbformat": 4, "nbformat_minor": 4 }