Skip to content

Instantly share code, notes, and snippets.

@eric-czech
Created November 19, 2020 15:15
Show Gist options
  • Save eric-czech/daae30d54a5c96fd09f13ffa58a3bafe to your computer and use it in GitHub Desktop.
Save eric-czech/daae30d54a5c96fd09f13ffa58a3bafe to your computer and use it in GitHub Desktop.
Representative GWAS workflow
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## GWAS Workflow\n",
"\n",
"This is a representative GWAS operation. It runs regressions across any number of genetic variants (vs a simulated outcome here) where some set of covariates is common to all variants. See [sgkit.gwas_linear_regression](https://github.com/pystatgen/sgkit/blob/50ece8d9422ca6d94526ca402add13e87c12dfac/sgkit/stats/association.py#L119) for more details.\n",
"\n",
"This simulation shows how poorly these dask functions scale as the sample size grows, most likely due to https://stackoverflow.com/questions/64774771/does-blockwise-allow-iteration-over-out-of-core-arrays. \n",
"\n",
"See https://github.com/pystatgen/sgkit/issues/390 for a related discussion."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import dask.array as da\n",
"import numpy as np\n",
"import pandas as pd\n",
"from dask.diagnostics import ResourceProfiler\n",
"from dask.array import stats\n",
"import plotnine as pn"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Representative arguments below would look like this, but smaller arrays are used for the simulation:\n",
"\n",
"```python\n",
"m, n, c, y = 141910, 365941, 12, 1\n",
"XL = da.random.random(size=(n, m), chunks=(5792, 5216))\n",
"XC = da.random.random(size=(n, c), chunks=(5792, -1))\n",
"Y = da.random.random(size=(n, y), chunks=(5792, -1))\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 7min 29s, sys: 4min 29s, total: 11min 58s\n",
"Wall time: 57.4 s\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>m</th>\n",
" <th>n</th>\n",
" <th>mem</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1000</td>\n",
" <td>1000</td>\n",
" <td>287.666176</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1000</td>\n",
" <td>3162</td>\n",
" <td>286.404608</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1000</td>\n",
" <td>10000</td>\n",
" <td>302.993408</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1000</td>\n",
" <td>31622</td>\n",
" <td>320.745472</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1000</td>\n",
" <td>31622</td>\n",
" <td>436.711424</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" m n mem\n",
"0 1000 1000 287.666176\n",
"1 1000 3162 286.404608\n",
"2 1000 10000 302.993408\n",
"3 1000 31622 320.745472\n",
"4 1000 31622 436.711424"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%%time\n",
"\n",
"def linear_regression(XL, XC, Y):\n",
" dof = Y.shape[0] - XC.shape[1] - 1\n",
" XLP = XL - XC @ da.linalg.lstsq(XC, XL)[0]\n",
" YP = Y - XC @ da.linalg.lstsq(XC, Y)[0]\n",
" XLPS = (XLP ** 2).sum(axis=0, keepdims=True).T\n",
" B = (XLP.T @ YP) / XLPS\n",
" YR = YP[:, np.newaxis, :] - XLP[..., np.newaxis] * B[np.newaxis, ...]\n",
" RSS = (YR ** 2).sum(axis=0)\n",
" T = B / np.sqrt(RSS / dof / XLPS)\n",
" return T\n",
"\n",
"results = []\n",
"for n in 10**np.array([3, 3.5, 4, 4.5, 5, 5.5, 5.8]):\n",
" chunks = (1000, 250)\n",
" m = 1000\n",
" n = int(n)\n",
" with ResourceProfiler() as prof:\n",
" XL = da.random.random(size=(n, m), chunks=chunks)\n",
" XC = da.random.random(size=(n, 10), chunks=(chunks[0], -1))\n",
" Y = da.random.random(size=(n, 3), chunks=(chunks[0], -1))\n",
" linear_regression(XL, XC, Y).max().compute()\n",
" results.append((m, n, prof.results))\n",
" \n",
"df = pd.DataFrame([\n",
" dict(m=r[0], n=r[1], mem=e.mem)\n",
" for r in results\n",
" for e in r[2]\n",
"])\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAj4AAAHICAYAAABOEeA1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdfXzNhf//8ec5syuzza5HMcvlFuWq5JPLXIQo8sl1kqiIpKRIX4SUfJRSn6RQzWUfl9UHpQ8pF12opJBiIyIOs7HNZue8f3/47eR0NjY7xzlzHvfbzW0778vX+3U257n3pckwDEMAAAA+wOzpAgAAAK4Ugg8AAPAZBB8AAOAzCD4AAMBnEHwAAIDPIPgAAACfQfABAAA+g+ADAAB8BsEHAAD4DIIP4EEmk0kDBgzwdBkX1apVK1WrVs3TZQClNn/+fJlMJm3cuNHTpcCDCD4oUywWi8aOHasbbrhBYWFhCg0N1XXXXadu3brpnXfe8XR5AAAvV87TBQDF9fvvv6tJkyY6fvy47rnnHg0aNEgBAQHav3+/Pv30U73yyit64IEHPF3mVeeTTz4Rj/QDcLUg+KDMmD59uo4cOaJXX31Vw4cPdxp/6NAhD1R19QsICPB0CXCzs2fPqly5cipXjo8EXP041IUy45dffpEktW7dutDx1157rcPrr7/+WgMHDlTt2rUVEhKikJAQ3XTTTZo3b57TvBMmTJDJZNLu3bs1atQoXXPNNSpfvrz+8Y9/6Ouvv5Ykbd68Wa1atVKFChUUHR2txx57TOfOnXNYTsH5MKmpqbr77rsVERGhChUqqH379vrhhx+Kva0bNmxQx44dFRERocDAQCUlJenFF1+U1Wp1mG737t3q3bu3qlSpooCAAEVHR6tJkyaaO3fuJdeRm5urSZMmKTk5WSEhIQoNDVWNGjU0cOBA5ebmOm1TgYLzJIr6N2HCBIf1LFu2TC1btlRYWJiCg4PVoEEDvf3225esz2azqWrVqqpVq1ah49evXy+TyaQpU6bYhy1YsEBNmzZVZGSkgoKCdO2116pbt27as2fPJddXsJ0HDx7UPffco4iICIWHh6t79+46duyYJGnu3LmqW7eugoKClJiYWOTh1eK+f65c54IFC9SkSRNVqFBBISEhuuWWW7R48eIit/PAgQPq1auXoqOjFRwcrH379tl/fgqzZMkSmUymS753Bb9LaWlpRa77Qtu2bVOXLl1UuXJlBQYGKjY2Vi1bttTq1asdpsvLy9O0adN0ww03KDg4WGFhYWrbtq02bdrktJ5z585p/PjxqlatmoKCgpSUlKR///vfF60bvoN4jzLjuuuukyTNmzdPL7744iX/Ol2xYoV27dqlnj17qkqVKjp16pSWLl2qgQMH6vjx4xo9erTTPP3791dwcLBGjx6trKwszZgxQ+3atdN7772n+++/X4MGDVKvXr20Zs0azZw5UzExMXrmmWcclpGVlaVWrVqpYcOGmjx5sn7//Xe9/vrrat68ubZu3aq6detetO65c+dq0KBBatCggZ5++mlVrFhRmzdv1pgxY/T999/bP8xOnDih1q1by2az6aGHHlJiYqLS09P1448/atOmTRo4cOBF1zNs2DC9/fbb6tu3rx599FFJUmpqqj788EPl5OQoMDCw0PlatGih999/32n4v//9b23ZskXx8fH2YRMmTNDEiRPVunVrjR8/XsHBwVq3bp0GDx6s3377TS+88EKR9ZnNZt177716/vnntWXLFv3jH/9wGP/uu+/KbDarf//+ks5/8Pfr10+33nqrxo8frwoVKujw4cP67LPP9Ouvv6pOnToX7Yf013t36623aurUqdqzZ49mzZqlo0ePqlu3bnr99dc1ePBghYaGas6cORo0aJDq1KmjW2+91b6M4r5/rlzn//3f/2nSpEm64YYb9Oyzz0qSUlJS1Lt3b+3fv19jx451WOeZM2fUvHlzNW7cWBMnTtTp06cVFRWl++67TzNmzNCPP/6oG264wWGet99+WxUqVFCvXr0u2cfi2rt3r9q2bauYmBg98sgjqlSpko4fP65vv/1W27Zt05133ilJys/PV6dOnfT555+rd+/eevjhh5Wdna2UlBTddtttWrlypTp37mxfbv/+/bV48WLddtttevzxx3XixAmNHz9eVatWdVntKMMMoIzYt2+fER4ebkgyYmNjje7duxsvvvii8eWXXxpWq9Vp+jNnzjgNs1qtRvPmzY3w8HAjLy/PPnz8+PGGJKNjx44Oy1q1apUhyfDz8zO2bt3qsKz69esb8fHxDsNatmxpSDIeeeQRh+FfffWVYTKZjDZt2jgMl2Tcd9999tdHjhwxgoKCjK5duxo2m81h2unTpxuSjI0bNzrUtmTJksLadUkRERFGx44dLzldy5YtjYSEhItO8+677xqSjJ49e9rr/u677wyTyWQ8+uijTtMPGzbMMJvNxr59+y663L179xqSjMGDBzsMz8zMNMqXL2+0a9fOPqxbt25GaGioce7cuUtuU2EK3rupU6c6DB85cqQhybjmmmuMU6dO2YcfPXrUCAwMNHr16mUfVpL3z1Xr3Lt3r2E2m42GDRsa2dnZ9uFnzpwx6tata/j5+RmpqalO63zqqaecerB3717DZDIZw4cPdxi+f/9+w2QyOb0PhSn4XbpwnReu+8KfpZkzZxqSjK+++uqiy3z55ZcNScby5csdhufl5RkNGjQwEhMT7cM+++wzQ5LRrVs3h/dg//79RnBwsCHJ2LBhwyW3A1cvDnWhzLjuuuu0Y8cODRs2TEFBQVq2bJmeeuopNWvWTDVq1NAnn3ziMH1ISIj9+5ycHJ04cUInT55Uhw4dlJGRYT90dqGRI0fKbP7r16J58+aSpCZNmuiWW25xmLZFixY6evSozpw547Scv/+FffPNN6tt27b63//+p/T09CK38T//+Y/Onj2rQYMG6cSJE7JYLPZ/BX/Rrlu3TpIUEREhSfr444916tSpIpdZlIiICP3000/asWNHiee90KeffqpBgwapRYsWevfdd2UymSSd3wNjGIYeeOABh+2wWCy68847ZbPZtH79+osuu2bNmvrHP/6hpUuX6uzZs/bhH3zwgbKzsx1uBRAREaGsrCytXr1aNpvtsrbFbDbrsccecxhW8DNw3333KTw83D48Li5OtWvX1q+//mofVpL3z1XrXLlypWw2m0aPHq3g4GD78JCQED355JOyWq1atWqV07Y+9dRTTsNq1qyp1q1bKyUlxaHfb7/9tgzD0IMPPug0T2kU/AyvWLFCOTk5RU6XkpKiatWqqXnz5g49zcjI0J133qnU1FTt3btX0vlDq5I0ZswY+8+iJCUmJqpv374urR9lE8EHZUpCQoJee+01HThwQH/++ac+/PBD9e3bV2lpaerWrZt+++03+7QWi0VDhw5V5cqVVb58eUVHRzscmjp58qTT8gsOpxUo+I/578MvHHfixAmH4RUrVlTlypWdpk9OTpZhGNq/f3+R27d7925JUufOnRUTE+Pwr+BQzZ9//inp/Ifj4MGD9d577ykmJkZNmjTRE088oa1btxa5/Au99tpryszMVP369ZWQkKB+/frp/fffd/jAu5QdO3aoe/fuqlGjhlauXOlweKxgW2688UanbWnfvr3DtlzMgAEDlJGRoZUrV9qHvfvuuwoPD1e3bt3sw8aNG6caNWqoe/fuio6OVpcuXfTyyy8Xax0FKleurKCgIIdhl/oZuPD9L8n756p1Fvw8XX/99U7T1qtXT5K0b98+h+ExMTH2dfzdww8/rPT0dHuAyM/P17x581S/fn01bty40HkuV69evXTHHXfohRdeUEREhFq0aKFx48bpp59+cphu9+7dSktLc+ppTEyMJk6cKOmvvhZsa3JystP6CusRfA/n+KDMio2NVefOndW5c2dVqVJFL7zwghYvXqxx48bJMAy1b99eP/30k4YPH66bbrpJERER8vPz03//+1+9/PLLhe4V8PPzK3RdRQ2XVOJLvS/8K/TvCmp6++23lZCQUOg0F4aqt956S48//rjWrFmjL7/8Uu+8845mzJih4cOH69VXX71oHZ06dVJaWprWrl2rzz//XBs2bNCCBQs0ceJEbd26VTExMRed/+DBg+rUqZNCQkK0Zs0apw/Sgm356KOPijxfqLAP9r/r2bOnRowYofnz56tXr15KTU3VF198ocGDBzvs4UhMTNRPP/2kjRs36rPPPtMXX3yhUaNG6dlnn9WaNWvse1Eu5mLvc1HjLnz/S/r+uWKdBd9f7Ofq78qXL1/kuK5duyo+Pt5+/tfHH3+sI0eO2M8dupSL1ZGfn+/w2t/fXx999JG+++47rVu3Tl9++aVefvllPf/883rppZf0xBNPSDrf19q1a2vWrFlFLvtS585JJf9dxdWJ4IOrQsGJr4cPH5Yk7dy5U99//72effZZPffccw7Tfvrpp26t5dSpU/rjjz+cPuB27dolk8mkxMTEIuctuIIpIiJCbdu2Ldb66tSpozp16mjkyJHKzs5Whw4d9Nprr+nJJ59UlSpVLjpvxYoV1atXL/sJq7NmzdLw4cP15ptvXvSD7tSpU+rYsaMyMzP1+eefF/ohX6tWLa1du1aVKlVSw4YNi7UthQkLC1O3bt20ZMkS/fHHH3r33XdlGEahd7z29/dXu3bt1K5dO0nSDz/8oJtuukkTJkzQZ599dtk1FNflvH+lVaNGDUnSTz/95LRHo2DPSfXq1Yu9PH9/fw0cOFDPP/+8fv31V82ZM0fly5dXnz59ijV/ZGSkpPN7VP9+Bdf+/fsLvT1Cw4YN7T8jJ0+e1C233KJnnnlGI0aMULly5VSrVi39/vvvatWq1SUvaijY1l27dummm25yGLdr165ibQOubhzqQpmxceNGZWdnFzpuxYoVkv7avV3wl/Lf/8I7fPhwsS6lLq3nn3/e4fXXX3+t9evXq3Xr1kUeYpCkHj16KCgoSBMmTCj03KGcnBydPn1a0vkPiL/vtSpfvrySkpLs44titVoLPdeoUaNGl5w3NzdXd911l/bu3aulS5cWGWoKrrYaM2aM02X/kpSRkeFw2fzFDBgwQFarVe+//77ee+891a5dW02bNnWY5vjx407zJScnKzg4+KLb40olef9cpWvXrjKbzZo+fbrDYcrs7Gy99NJL8vPz01133VWiZQ4ePFhms1kTJ07U2rVr1bNnT4dzjS6mdu3akuR0/lZKSoqOHDniMMxisTjNHxkZqcTEROXm5iorK0vS+XOd0tPTHW5dcKELDx/efffdkqSpU6c6/P6npqZqwYIFxdoGXN3Y44My45VXXtGGDRvUuXNnNWrUSBEREbJYLPr444/1+eefq27duvZLuOvUqaO6detq2rRpOnPmjK6//nqlpqZq9uzZql69uls/CKOjo/Xhhx/q8OHDateunQ4ePKjXX39d5cuX14wZMy467zXXXKPZs2fb7z9033336brrrtPJkye1e/durVixQitXrlSrVq303nvvacaMGeratatq1Kih4OBgffvtt3r77bfVqFEj+/kdhTl9+rQqVaqkLl26qEGDBoqPj9fhw4c1Z84c+fv7X/Qk0PHjx2vTpk3q1KmTTpw4oZSUFIfxN9xwg2644QY1atRIkydP1rhx41S3bl317t1b1157rY4dO6Yff/xRq1ev1q5du4r1HLA2bdqoSpUqev7555WZmampU6c6TXP77bcrNDRULVu2VNWqVXXmzBktXrxYp0+f1v3333/JdbhCSd4/V6lRo4aeeeYZTZo0Sbfccov69u0rwzCUkpKinTt3asqUKSV+1lq1atV0++2324PC4MGDiz1v27ZtlZycrGeffVbHjh1TzZo19e2332r16tWqUaOGQwiePHmy1q5dq86dO+u6666T2WzWxo0b9cknn6hbt272sPXoo49q/fr1mjBhgjZt2qT27dsrMjJSv//+uzZv3qzU1FT7uU5t2rTRPffcow8++EDt2rXTXXfdpRMnTuiNN95QcnKytm/fXqJe4CrkkWvJgMuwbds248knnzRuvvlmIy4uzihXrpwRGhpqNGrUyHjuueeMzMxMh+kPHDhg9OrVy4iNjTWCgoKMG2+80XjnnXeMefPmOV3SerFLcPW3S84vNk/B5br79+83unbtaoSHhxvly5c32rZta2zfvr3Yy962bZvxz3/+04iLizP8/f2NuLg4o2nTpsakSZOMEydOGIZhGN9//70xYMAAo2bNmkZISIgREhJiJCUlGePGjTPS09Mv2svc3FxjzJgxRpMmTYyoqCgjICDAuPbaa41//vOfxjfffOMw7d8vQb7vvvsMSUX+Gz9+vMP8a9euNTp16mRERUUZ/v7+RuXKlY3WrVsb//rXv4ycnJyL1nmhZ555xpBkmM1m49ChQ07j58yZY9x+++1GpUqVjICAACMmJsZo2bKlsXTp0mItv6jL9jds2GBIMubNm1fseYrz/rl6ne+//75x8803G8HBwUZwcLDRpEkTY+HChcWe/+8KbpdQt27dS077d7/99pvRqVMnIyQkxKhQoYLRqVMnY/fu3U7r3rBhg9GzZ0+jWrVqRnBwsBEWFmbceOONxrRp05x+NvLz84033njDaNKkiVGhQgUjKCjIqFatmnH33Xc73dIhNzfXGDdunFGlShUjICDAqF27tvH6668X+rsP32MyDM72AlylVatWSktLK/SutUBZsnbtWnXs2FEzZ8603+ASuBpwjg8AwMkrr7yi8uXL28/VAq4WnOMDAJAkHTt2TJ999pm2bdumdevW6amnnlLFihU9XRbgUgQfAICk85d79+nTR2FhYbr//vudHjgLXA04xwcAAPgMzvEBAAA+g+ADAAB8BsEHAAD4DIIPAADwGQQfAADgMwg+AADAZ3Afn0IU9sRgb2IymRQcHKycnBynp4/7KnrijJ44oyfO6IkzeuKsrPQkOjr6ktN4VfDJzMzUkCFDVKlSJU2fPl2SdODAAb322mtKS0tTfHy8hgwZouuvv94+z+bNmzV//nydOnVKSUlJGjFihKKiouzjU1JStGbNGtlsNjVv3lwPPvigypXzqs0uMbPZrPLlyys3N1dWq9XT5XgFeuKMnjijJ87oiTN64uxq6olXHeqaO3euqlWrZn+dn5+vyZMnq2nTplq0aJG6d++u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bDaCDwAAV1hubq5sNpvmzp2r2NhYT5dTKseOHdPAgQPdvvfqsoLPunXrNH78eH3//feyWq36+uuv1bBhQz344INq2bKl+vbt6+o6AQBAEWJjYxUfH+/pMsqEEu+mWbRokTp16qSEhAS9+uqrDrvXqlevrnnz5rm0QAAAAFcpcfCZNGmSHnvsMS1ZskSDBg1yGHf99dfrp59+cllxAAAArlTi4LN//3516tSp0HEhISHKyMgodVEAAADuUOLgEx8frz179hQ67scff1RCQkKpiwIAAHCHEgefPn36aMKECQ7P4zKZTPrpp580bdo09evXz6UFAgAAuEqJr+qaMGGCfv75Z7Vr187+8NKOHTvq+PHj6ty5s55++mmXFwkAAOAKJQ4+AQEBWrVqlTZs2KBPP/1UFotFkZGRatu2rdq2beuOGgEAAFzism9g2Lp1a7Vu3dqVtQAAALhVic/xOXDggHbu3Gl/nZubqylTpqhfv36aP3++K2sDAABwqRIHn8GDB+v999+3v37qqac0ceJE7dmzRw8++KDeeOMNlxYIAADgKiUOPjt27FDz5s0lSfn5+Xr33Xf14osv6ttvv9WECRP073//2+VFAgAAuEKJg09mZqb9qalfffWVMjMz1atXL0lSs2bNtH//ftdWCAAA4CIlDj7XXnuttm3bJklavny5kpOTValSJUlSenq6ypcv79oKAQAAXKTEV3U98MADGjdunD744AN9//33evnll+3jtm3bpqSkJJcWCAAA4ColDj5PP/20KleurG+++UZDhw7VgAED7OPS09OdHlwKAADgLS7rPj79+/dX//79nYa/+eabpS4IAADAXYoVfA4ePFjkOH9/f0VFRSkgIMBlRQEAALhDsYJPtWrVZDKZihxvNpt18803a+LEiTy2AgAAeK1iBZ8PPvigyHFWq1VHjhzRypUr1bFjR61bt0633XabywoEAABwlWIFn+7du19ymhEjRujuu+/WpEmTCD4AAMArlfg+PhfTv39/bd++3ZWLBAAAcBmXBp/g4GCdO3fOlYsEAABwmcu6nL0o//vf/1SrVq0SzXPu3Dm9+eab2rFjh06fPq3o6Gjdc889atWqlaTzT4N/7bXXlJaWpvj4eA0ZMkTXX3+9ff7Nmzdr/vz5OnXqlJKSkjRixAhFRUXZx6ekpGjNmjWy2Wxq3ry5HnzwQZUr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\n",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<ggplot: (438065496)>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(\n",
" pn.ggplot(\n",
" df.assign(n=lambda df: pd.Categorical(df['n'], ordered=True, categories=df['n'].unique())), \n",
" pn.aes(x='n', y='mem')\n",
" ) + \n",
" pn.geom_boxplot() + \n",
" pn.labs(x='N', y='Memory Used (MB)', title='Samples size vs memory used')\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Multiplication Only\n",
"\n",
"Memory usage also grows with one dimension of the array in a multiplication."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 51 s, sys: 1min 8s, total: 1min 59s\n",
"Wall time: 26.5 s\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAj4AAAHICAYAAABOEeA1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdeZyNBf//8fc5M2Y1xjDDIDPGPlNkK1psWYqborlDuOW2tLiTlJTSjZCSmxSVVKp7rGVplYyIRIuistyKsSQahxnLLIaZ6/eH35yv0zXDHHOOMzPX6/l4eJhzrZ/rc65x3q7t2AzDMAQAAGABdl8XAAAAcKUQfAAAgGUQfAAAgGUQfAAAgGUQfAAAgGUQfAAAgGUQfAAAgGUQfAAAgGUQfAAAgGUQfAAfstlsGjhwoK/LuKh27dqpVq1avi4DKLa3335bNptN69at83Up8CGCD0oVh8OhJ598Uo0bN1aFChUUFham2rVrq2fPnnrzzTd9XR4AoITz93UBQFEdPHhQLVu21NGjR3XXXXdpyJAhCggI0N69e7V69Wq9+OKLGjx4sK/LLHM+//xz8ZV+AMoKgg9KjWnTpunw4cN66aWXNHz4cNP433//3QdVlX0BAQG+LgFelp2dLX9/f/n785GAso9TXSg1/ve//0mS2rdvX+D4q666yuX1t99+q0GDBqlBgwYKDQ1VaGiorrvuOs2bN8807/jx42Wz2bRz506NGjVKNWrUUEhIiG688UZ9++23kqSNGzeqXbt2Kl++vCIjI/Xwww/r7NmzLsvJvx4mJSVFd955pyIiIlS+fHl17txZW7duLfK2rl27Vl26dFFERIQCAwMVHx+v559/Xrm5uS7T7dy5U3fffbdq1qypgIAARUZGqmXLlnrrrbcuuY4zZ85o4sSJSkhIUGhoqMLCwlS3bl0NGjRIZ86cMW1TvvzrJAr7M378eJf1LF26VG3btlWFChUUHByspk2b6o033rhkfXl5eYqJiVH9+vULHJ+cnCybzabJkyc7h82fP1833HCDKlWqpKCgIF111VXq2bOndu3adcn15W/ngQMHdNdddykiIkLh4eFKTExUamqqJOmtt97SNddco6CgIMXFxRV6erWo758n1zl//ny1bNlS5cuXV2hoqFq1aqVFixYVup379+9Xnz59FBkZqeDgYO3Zs8e5/xRk8eLFstlsl3zv8n+X9u3bV+i6L7R582Z1795d1atXV2BgoKpUqaK2bdvqww8/dJkuJydHU6dOVePGjRUcHKwKFSqoY8eOWr9+vWk9Z8+e1bhx41SrVi0FBQUpPj5er7766kXrhnUQ71Fq1K5dW5I0b948Pf/885f83+ny5cu1Y8cO9e7dWzVr1lR6erqWLFmiQYMG6ejRoxo9erRpngEDBig4OFijR49WRkaGpk+frk6dOundd9/VP//5Tw0ZMkR9+vTRypUrNXPmTEVFRempp55yWUZGRobatWunZs2aadKkSTp48KBmz56t1q1ba9OmTbrmmmsuWvdbb72lIUOGqGnTpnriiSdUsWJFbdy4UWPGjNGPP/7o/DA7duyY2rdvr7y8PN13332Ki4tTWlqafvrpJ61fv16DBg266HoefPBBvfHGG+rXr58eeughSVJKSoo++ugjZWVlKTAwsMD52rRpo//+97+m4a+++qq+/vprRUdHO4eNHz9eEyZMUPv27TVu3DgFBwdr1apVGjp0qH777Tc999xzhdZnt9v1j3/8Q88++6y+/vpr3XjjjS7j33nnHdntdg0YMEDS+Q/+/v3766abbtK4ceNUvnx5HTp0SGvWrNGvv/6qhg0bXrQf0v+9dzfddJOmTJmiXbt2adasWTpy5Ih69uyp2bNna+jQoQoLC9PcuXM1ZMgQNWzYUDfddJNzGUV9/zy5zn//+9+aOHGiGjdurKefflqSlJSUpLvvvlt79+7Vk08+6bLO06dPq3Xr1mrRooUmTJigU6dOqXLlyrrnnns0ffp0/fTTT2rcuLHLPG+88YbKly+vPn36XLKPRbV792517NhRUVFR+te//qVq1arp6NGj+v7777V582bdfvvtkqRz586pa9eu+vLLL3X33Xfr/vvvV2ZmppKSknTLLbdoxYoV6tatm3O5AwYM0KJFi3TLLbfokUce0bFjxzRu3DjFxMR4rHaUYgZQSuzZs8cIDw83JBlVqlQxEhMTjeeff9746quvjNzcXNP0p0+fNg3Lzc01WrdubYSHhxs5OTnO4ePGjTMkGV26dHFZ1gcffGBIMvz8/IxNmza5LKtJkyZGdHS0y7C2bdsakox//etfLsO/+eYbw2azGR06dHAZLsm45557nK8PHz5sBAUFGT169DDy8vJcpp02bZohyVi3bp1LbYsXLy6oXZcUERFhdOnS5ZLTtW3b1oiNjb3oNO+8844hyejdu7ez7h9++MGw2WzGQw89ZJr+wQcfNOx2u7Fnz56LLnf37t2GJGPo0KEuw0+ePGmEhIQYnTp1cg7r2bOnERYWZpw9e/aS21SQ/PduypQpLsNHjhxpSDJq1KhhpKenO4cfOXLECAwMNPr06eMc5s7756l17t6927Db7UazZs2MzMxM5/DTp08b11xzjeHn52ekpKSY1vn444+berB7927DZrMZw4cPdxm+d+9ew2azmd6HguT/Ll24zgvXfeG+NHPmTEOS8c0331x0mTNmzDAkGcuWLXMZnpOTYzRt2tSIi4tzDluzZo0hyejZs6fLe7B3714jODjYkGSsXbv2ktuBsotTXSg1ateurW3btunBBx9UUFCQli5dqscff1w333yz6tatq88//9xl+tDQUOfPWVlZOnbsmI4fP67bbrtNJ06ccJ46u9DIkSNlt//fr0Xr1q0lSS1btlSrVq1cpm3Tpo2OHDmi06dPm5bz1/9hX3/99erYsaO++OILpaWlFbqN77//vrKzszVkyBAdO3ZMDofD+Sf/f7SrVq2SJEVEREiSPvnkE6Wnpxe6zMJERETol19+0bZt29ye90KrV6/WkCFD1KZNG73zzjuy2WySzh+BMQxDgwcPdtkOh8Oh22+/XXl5eUpOTr7osuvVq6cbb7xRS5YsUXZ2tnP4e++9p8zMTJdHAURERCgjI0Mffvih8vLyLmtb7Ha7Hn74YZdh+fvAPffco/DwcOfwqlWrqkGDBvr111+dw9x5/zy1zhUrVigvL0+jR49WcHCwc3hoaKgee+wx5ebm6oMPPjBt6+OPP24aVq9ePbVv315JSUku/X7jjTdkGIbuvfde0zzFkb8PL1++XFlZWYVOl5SUpFq1aql169YuPT1x4oRuv/12paSkaPfu3ZLOn1qVpDFjxjj3RUmKi4tTv379PFo/SieCD0qV2NhYvfzyy9q/f7/+/PNPffTRR+rXr5/27dunnj176rfffnNO63A4NGzYMFWvXl0hISGKjIx0OTV1/Phx0/LzT6fly/+H+a/DLxx37Ngxl+EVK1ZU9erVTdMnJCTIMAzt3bu30O3buXOnJKlbt26Kiopy+ZN/qubPP/+UdP7DcejQoXr33XcVFRWlli1b6tFHH9WmTZsKXf6FXn75ZZ08eVJNmjRRbGys+vfvr//+978uH3iXsm3bNiUmJqpu3bpasWKFy+mx/G259tprTdvSuXNnl225mIEDB+rEiRNasWKFc9g777yj8PBw9ezZ0zls7Nixqlu3rhITExUZGanu3btrxowZRVpHvurVqysoKMhl2KX2gQvff3feP0+tM39/uvrqq03TNmrUSJK0Z88el+FRUVHOdfzV/fffr7S0NGeAOHfunObNm6cmTZqoRYsWBc5zufr06aO//e1veu655xQREaE2bdpo7Nix+uWXX1ym27lzp/bt22fqaVRUlCZMmCDp//qav60JCQmm9RXUI1gP1/ig1KpSpYq6deumbt26qWbNmnruuee0aNEijR07VoZhqHPnzvrll180fPhwXXfddYqIiJCfn58+/fRTzZgxo8CjAn5+fgWuq7Dhkty+1fvC/4X+VX5Nb7zxhmJjYwuc5sJQ9frrr+uRRx7RypUr9dVXX+nNN9/U9OnTNXz4cL300ksXraNr167at2+fPvvsM3355Zdau3at5s+frwkTJmjTpk2Kioq66PwHDhxQ165dFRoaqpUrV5o+SPO35eOPPy70eqGCPtj/qnfv3hoxYoTefvtt9enTRykpKdqwYYOGDh3qcoQjLi5Ov/zyi9atW6c1a9Zow4YNGjVqlJ5++mmtXLnSeRTlYi72Phc27sL33933zxPrzP/5YvvVX4WEhBQ6rkePHoqOjnZe//XJJ5/o8OHDzmuHLuVidZw7d87ldbly5fTxxx/rhx9+0KpVq/TVV19pxowZevbZZ/XCCy/o0UcflXS+rw0aNNCsWbMKXfalrp2T3P9dRdlE8EGZkH/h66FDhyRJP//8s3788Uc9/fTTeuaZZ1ymXb16tVdrSU9P1x9//GH6gNuxY4dsNpvi4uIKnTf/DqaIiAh17NixSOtr2LChGjZsqJEjRyozM1O33XabXn75ZT322GOqWbPmReetWLGi+vTp47xgddasWRo+fLhee+21i37Qpaenq0uXLjp58qS+/PLLAj/k69evr88++0zVqlVTs2bNirQtBalQoYJ69uypxYsX648//tA777wjwzAKfOJ1uXLl1KlTJ3Xq1EmStHXrVl133XUaP3681qxZc9k1FNXlvH/FVbduXUnSL7/8YjqikX/kpE6dOkVeXrly5TRo0CA9++yz+vXXXzV37lyFhISob9++RZq/UqVKks4fUf3rHVx79+4t8PEIzZo1c+4jx48fV6tWrfTUU09pxIgR8vf3V/369XXw4EG1a9fukjc15G/rjh07dN1117mM27FjR5G2AWUbp7pQaqxbt06ZmZkFjlu+fLmk/zu8nf8/5b/+D+/QoUNFupW6uJ599lmX199++62Sk5PVvn37Qk8xSFKvXr0UFBSk8ePHF3jtUFZWlk6dOiXp/AfEX49ahYSEKD4+3jm+MLm5uQVea9S8efNLznvmzBndcccd2r17t5YsWVJoqMm/22rMmDGm2/4l6cSJEy63zV/MwIEDlZubq//+979699131aBBA91www0u0xw9etQ0X0JCgoKDgy+6PZ7kzvvnKT169JDdbte0adNcTlNmZmbqhRdekJ+fn+644w63ljl06FDZ7XZNmDBBn332mXr37u1yrdHFNGjQQJJM128lJSXp8OHDLsMcDodp/kqVKikuLk5nzpxRRkaGpPPXOqWlpbk8uuBCF54+vPPOOyVJU6ZMcfn9T0lJ0fz584u0DSjbOOKDUuPFF1/U2rVr1a1bNzVv3lwRERFyOBz65JNP9OWXX+qaa65x3sLdsGFDXXPNNZo6dapOnz6tq6++WikpKZozZ47q1Knj1Q/CyMhIffTRRzp06JA6deqkAwcOaPbs2QoJCdH06dMvOm+NGjU0Z84c5/OH7rnnHtWuXVvHjx/Xzp07tXz5cq1YsULt2rXTu+++q+nTp6tHjx6qW7eugoOD9f333+uNN95Q8+bNndd3FOTUqVOqVq2aunfvrqZNmyo6OlqHDh3S3LlzVa5cuYteBDpu3DitX79eXbt21bFjx5SUlOQyvnHjxmrcuLGaN2+uSZMmaezYsbrmmmt0991366qrrlJqaqp++uknffjhh9qxY0eRvgesQ4cOqlmzpp599lmdPHlSU6ZMMU1z6623KiwsTG3btlVMTIxOnz6tRYsW6dSpU/rnP/95yXV4gjvvn6fUrVtXTz31lCZOnKhWrVqpX79+MgxDSUlJ+vnnnzV58mS3v2utVq1auvXWW51BYejQoUWet2PHjkpISNDTTz+t1NRU1atXT99//70+/PBD1a1b1yUET5o0SZ999pm6deum2rVry263a926dfr888/Vs2dPZ9h66KGHlJycrPHjx2v9+vXq3LmzKlWqpIMHD2rjxo1KSUlx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V08dO3bUxo0bvV07AACAW4p0xCcuLk4JCQl6/vnn1bNnT4WHhxc67bfffqtFixapW7dumjx5soYNG+axYgEAAIqjSMFnxYoVuummm4q0wOuvv17XX3+9JkyYoP379xerOAAAAE8q0qmuooaeC4WFhemaa65xez4AAABv8chdXUePHtWZM2c8sSgAAACvKXLw2bZtmyZNmqRx48Zp69atkqQFCxYoOjpa0dHRCg8P1wMPPKBz5855rVgAAIDiKNI1PqtWrdLtt98uPz8/BQQE6LnnntPs2bP1wAMPqEuXLkpISNDPP/+s119/XfXr19fIkSO9XTcAAIDbinTEZ+LEibrtttuUnp6u9PR0jRo1Sg8++KAeeeQRffjhh3ruuef0ySef6KGHHtK8efO8XTMAAMBlKVLw2b59ux544AEFBARIkkaOHKmcnBx17drVZbq//e1v2rt3r+erBAAA8IAineo6ceKEKlWq5HwdEREh6fydWxcKCwtTVlaWB8vzjYCAAAUGBvq6jELZbDZJUmhoqAzD8HE1JQM9MaMnZvTEjJ6Y0ROzzMxMSVJwcLDps7+0KfJXVuTvCJcaVhbk5OQoJyfH12UUKv9aq4yMDOXm5vq6nBKBnpjREzN6YkZPzOiJWf5BjaysLJ06dcrH1RSuKActihx8Hn30UVWsWFGSnAn44YcfdnmKc3p6urs1AgAAXDFFCj5t2rSRzWZzSXlt27aVJJdhfn5+atOmjYdLBAAA8IwiBZ9169Z5uQwAAADv88iTmwEAAEqDIh3x+eGHH9xaaLNmzS6rGAAAAG8qUvBp0aKF8w6u/AubC7qjyzAM2Ww2roIHAAAlUpHv6goNDVXPnj2VmJjocicXAABAaVGk4PPNN99owYIFWrJkid577z116dJF/fr1U7du3ZxPcwYAACjpinRx83XXXacZM2bo999/14cffqiKFStq8ODBqlq1qgYNGqTk5GSebgkAAEo8t+7qstls6tixo9588039+eefevPNN3Xy5El16dJF/fv391aNAAAAHnHZt7OnpKRo27Zt2rZtmyQpNjbWY0UBAAB4Q5EvbpakQ4cOadGiRZo/f762bt2qVq1aacSIEerdu7eioqK8VSMAAIBHFCn4vP7661qwYIG++uorxcfHq0+fPlq6dKni4uK8XR8AAIDHFCn43H///QoLC1Pfvn3VpEkT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I/Xss8/q1VdfdSv4fPXVV2rWrJmCgoK0a9cuffLJJ7r33nslnT8CVK5cOa1YsULdu3fX119/rSNHjuiGG26QJLVr106jRo3Stm3b1LBhQ82fP18NGjRQtWrVJEkdOnTQ8uXL1aJFCwUFBWnx4sWXfR0SAAAlzYkTJ3T33Xdfse/MHDFihFeXb7fbtXDhQq+GH7eDz8mTJ50FffPNNzp58qT69OkjSbr55ps1efJkt5b38ccfa/bs2crLy1NkZKT69eunNm3anC/O319jx47VrFmztGDBAlWtWlVPPvmkwsLCJEk1a9bU8OHDNWvWLKWlpSkhIUGjRo1yLrtz5846evSoRo4cqdzcXLVp00a9evVyd5MBACiRzpw5o7y8PL311luqUqWKr8spltTUVA0aNMjrR6/cDj5XXXWVNm/erDZt2mjZsmVKSEhwHmFJS0szXWx8Kc8999xFx9eqVcvl2Tx/dfPNN+vmm28ucJzNZlP//v0v69lCAACUFlWqVHE+YgYX53bwGTx4sMaOHav33ntPP/74o2bMmOEct3nzZuf1NAAAACWN28HniSeeUPXq1fXdd99p2LBhLndxpaWlmb64FAAAoKS4rOf4DBgwQAMGDDANf+2114pdEAAAgLcUKfjkPym5IOXKlVPlypUVEBDgsaIAAAC8oUjBp1atWrLZbIWOt9vtuv766zVhwgRuFwcAACVWkYLPe++9V+i43NxcHT58WCtWrFCXLl20atUq3XLLLR4rEAAAwFOKFHwSExMvOc2IESN05513auLEiQQfAABQItkvPUnRDRgwQFu2bPHkIgEAADzGo8EnODjYq9/hAQAAUBweDT5ffPGF6tev78lFAgAAeEyRrvE5fvx4oeNyc3N15MgRffDBB5oxY4ZefPFFjxUHAADgSUUKPpGRkRe9nV2SAgICNHr0aLe+mR0AAOBKKlLweeuttwoNPv7+/oqKilLLli29+jXyAAAAxVWk4HPh93EBAACUVh69uBkAAKAkI/gAAADLIPgAAADLIPgAAADLIPgAAADLcDv4tGjRQq+99ppOnDjhjXoAAAC8xu3gU79+fT3yyCOqXr26BgwYoC+//NIbdQEAAHic28FnwYIFOnz4sF544QXt3LlT7du3V926dTVlyhT98ccf3qgRAADAIy7rGp/w8HANGzZM3333nbZu3apu3bpp+vTp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LT2JiIi45DRlOvjUqFFDknTw4EHVrFlTkpSSkqLY2FiX6SIjI12+2MzhcJToNzZfbm5uqajzSqInZvTEjJ6Y0RMzemJWFnpSpi9uDgoK0k033aT58+crMzNTKSkpWrNmjTp06ODr0gAAgA+U6SM+knTfffdp1qxZGjhwoEJCQtSvXz9de+21vi4LAAD4gM0wDMPXRcA9DodDS5cuVWJiosspOiujJ2b0xIyemNETM3piVpZ6UqZPdZVVDodDc+fOdbkF3+roiRk9MaMnZvTEjJ6YlaWeEHwAAIBlEHwAAIBl+I0fP368r4uA+4KDg9WiRQuFhIT4upQSg56Y0RMzemJGT8zoiVlZ6QkXNwMAAMvgVBcAALAMgg8AALAMgg8AALCMMv/k5pLu448/1hdffKF9+/bphhtu0GOPPeYct3//fr388svat2+foqOj9cADD+jqq692jt+4caPefvttpaenKz4+XiNGjFDlypWd45OSkrRy5Url5eWpdevWuvfee+XvXzre8lmzZun7779XVlaWwsLC1LlzZ/Xq1cs5bvv27frjjz90//33q0uXLi7znjp1Sm+88Ya+/fZbGYahunXratKkSZKkZcuWae3atUpNTVVoaKjat2+vvn37ys/P74pv48X4ar84ffq0Zs+erR9++EHBwcHq1auXunbteuU2/BJ8sV+cPXtWr732mrZt26ZTp04pMjJSd911l9q1a3elN7/E7heXWrc3lcR9Yvv27ZowYYLLurKzszVo0CD16NHD4z0orfvFpdbtNQZ8auPGjcamTZuMV1991Zg6dapz+NmzZ40hQ4YY77//vpGTk2N88cUXxt13322cOnXKMAzDOHjwoNGrVy/jxx9/NLKzs41XX33VeOKJJ5zzr1q1yhg6dKhx5MgRIz093Xj00UeN+fPnX/Htu1z79+83srOzDcMwjNTUVGPYsGHGhg0bDMMwjI8//tjYunWr8eijjxqffvqpad4xY8YYc+bMMU6dOmWcO3fO2L17t3Pc+++/b+zevds4e/as8eeffxrDhw833n///SuzUW7w1X4xbdo0Y/LkyUZGRobx22+/GX379jW2bdt25Tb8EnyxX2RlZRlJSUnG4cOHjdzcXGP79u1G7969jZ07d16BLXZVEveLS63b20rDPnHgwAHjjjvuMFJTUz29+YZhlM794lLr9iZOdfnY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\n",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<ggplot: (446888997)>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%%time\n",
"import dask.array as da\n",
"import numpy as np\n",
"import pandas as pd\n",
"from dask.diagnostics import ResourceProfiler\n",
"import plotnine as pn\n",
"\n",
"results = []\n",
"for n in 10**np.array([3, 3.5, 4, 4.5, 5, 5.5, 6]):\n",
" chunks = (1000, 250)\n",
" m = 1000\n",
" n = int(n)\n",
" with ResourceProfiler() as prof:\n",
" X = da.random.random(size=(n, m), chunks=chunks)\n",
" Y = da.random.random(size=(n, 3), chunks=(chunks[0], -1))\n",
" R = X.T @ Y\n",
" R.max().compute()\n",
" results.append((m, n, prof.results))\n",
" \n",
"df = pd.DataFrame([\n",
" dict(m=r[0], n=r[1], mem=e.mem)\n",
" for r in results\n",
" for e in r[2]\n",
"])\n",
"\n",
"(\n",
" pn.ggplot(\n",
" df.assign(n=lambda df: pd.Categorical(df['n'], ordered=True, categories=df['n'].unique())), \n",
" pn.aes(x='n', y='mem')\n",
" ) + \n",
" pn.geom_boxplot() + \n",
" pn.labs(x='N', y='Memory Used (MB)', title='Samples size vs memory used')\n",
")"
]
}
],
"metadata": {
"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.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment