|
9 | 9 | },
|
10 | 10 | "source": [
|
11 | 11 | "---\n",
|
12 |
| - "title: \"Getting Started\"\n", |
13 |
| - "subtitle: \"Packaging and distributing your Python code\"\n", |
| 12 | + "title: \"Packaging and distributing your Python code\"\n", |
14 | 13 | "author: \"Kyle Niemeyer\"\n",
|
15 | 14 | "date: \"29 January 2025\"\n",
|
16 | 15 | "institute: \"Oregon State University\"\n",
|
|
209 | 208 | ":::"
|
210 | 209 | ]
|
211 | 210 | },
|
212 |
| - { |
213 |
| - "cell_type": "markdown", |
214 |
| - "metadata": {}, |
215 |
| - "source": [ |
216 |
| - "# Notebook to installable package\n", |
217 |
| - "\n", |
218 |
| - "## Hypothetical workflow for a researcher {.smaller}\n", |
219 |
| - "\n", |
220 |
| - "1. Work on idea for paper with collaborators\n", |
221 |
| - "2. Do exploratory analysis in scripts and Jupyter ecosystem\n", |
222 |
| - "3. As research progresses, need to write more-complicated functions and workflows\n", |
223 |
| - "3. Code begins to sprawl across multiple directories\n", |
224 |
| - "4. Software dependencies begin to become more complicated\n", |
225 |
| - "4. The code \"works on my machine\", but what about your collaborators?\n", |
226 |
| - "\n", |
227 |
| - "::: {.r-fit-text .fragment}\n", |
228 |
| - "**People heroically press forward, but this is painful, and not reusable**\n", |
229 |
| - ":::\n", |
230 |
| - "\n", |
231 |
| - "## {.smaller}\n", |
232 |
| - "\n", |
233 |
| - "Imagine you start with a Jupyter notebook that looks like this:\n", |
234 |
| - "\n", |
235 |
| - "```{.python .fragment}\n", |
236 |
| - "import numpy as np\n", |
237 |
| - "from scipy.optimize import minimize\n", |
238 |
| - "\n", |
239 |
| - "# Rosenbrock function\n", |
240 |
| - "def rosen(x):\n", |
241 |
| - " \"\"\"The Rosenbrock function\"\"\"\n", |
242 |
| - " return sum(100.0 * (x[1:] - x[:-1] ** 2.0) ** 2.0 + (1 - x[:-1]) ** 2.0)\n", |
243 |
| - "\n", |
244 |
| - "def rosen_der(x):\n", |
245 |
| - " \"\"\"Gradient of the Rosenbrock function\"\"\"\n", |
246 |
| - " xm = x[1:-1]\n", |
247 |
| - " xm_m1 = x[:-2]\n", |
248 |
| - " xm_p1 = x[2:]\n", |
249 |
| - " der = np.zeros_like(x)\n", |
250 |
| - " der[1:-1] = 200 * (xm - xm_m1**2) - 400 * (xm_p1 - xm**2) * xm - 2 * (1 - xm)\n", |
251 |
| - " der[0] = -400 * x[0] * (x[1] - x[0] ** 2) - 2 * (1 - x[0])\n", |
252 |
| - " der[-1] = 200 * (x[-1] - x[-2] ** 2)\n", |
253 |
| - " return der\n", |
254 |
| - "\n", |
255 |
| - "# Minimization of the Rosenbrock function with some initial guess\n", |
256 |
| - "x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])\n", |
257 |
| - "result = minimize(rosen, x0, method=\"BFGS\", jac=rosen_der, options={\"disp\": True})\n", |
258 |
| - "optimized_params = result.x\n", |
259 |
| - "print(optimized_params)\n", |
260 |
| - "```\n", |
261 |
| - "\n", |
262 |
| - "## Reusable science, step by step\n", |
263 |
| - "\n", |
264 |
| - "We can convert our notebook code into a simple importable module an and example calling it:\n", |
265 |
| - "\n", |
266 |
| - "``` bash\n", |
267 |
| - "$ tree edit-sys-path \n", |
268 |
| - "edit-sys-path\n", |
269 |
| - "├── code\n", |
270 |
| - "│ └── utils.py\n", |
271 |
| - "└── example.py\n", |
272 |
| - "\n", |
273 |
| - "2 directories, 2 files\n", |
274 |
| - "```\n", |
275 |
| - "\n", |
276 |
| - "## Reusable science, step by step\n", |
277 |
| - "\n", |
278 |
| - "``` python\n", |
279 |
| - "# example.py\n", |
280 |
| - "import sys\n", |
281 |
| - "from pathlib import Path\n", |
282 |
| - "\n", |
283 |
| - "import numpy as np\n", |
284 |
| - "from scipy.optimize import minimize\n", |
285 |
| - "\n", |
286 |
| - "# Make ./code/utils.py visible to sys.path\n", |
287 |
| - "# sys.path position 1 should be after cwd and before activated virtual environment\n", |
288 |
| - "sys.path.insert(1, str(Path().cwd() / \"code\"))\n", |
289 |
| - "from utils import rosen, rosen_der\n", |
290 |
| - "\n", |
291 |
| - "x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])\n", |
292 |
| - "result = minimize(rosen, x0, method=\"BFGS\", jac=rosen_der, options={\"disp\": True})\n", |
293 |
| - "optimized_params = result.x\n", |
294 |
| - "print(optimized_params)\n", |
295 |
| - "```\n", |
296 |
| - "\n", |
297 |
| - "## Reusable science, step by step\n", |
298 |
| - "\n", |
299 |
| - "- This is **already better** than having everything in a single massive file!\n", |
300 |
| - "- However, now things are tied to this relative path on your computer:\n", |
301 |
| - "\n", |
302 |
| - "``` {.python .fragment}\n", |
303 |
| - "# Make ./code/utils.py visible to sys.path\n", |
304 |
| - "sys.path.insert(1, str(Path(__file__).parent / \"code\"))\n", |
305 |
| - "from utils import rosen, rosen_der\n", |
306 |
| - "```\n", |
307 |
| - "\n", |
308 |
| - "::: fragment\n", |
309 |
| - "and are brittle to refactoring and change; plus, not very portable to others.\n", |
310 |
| - "::: \n", |
311 |
| - "\n", |
312 |
| - "- But we can do better!" |
313 |
| - ] |
314 |
| - }, |
315 | 211 | {
|
316 | 212 | "cell_type": "markdown",
|
317 | 213 | "metadata": {},
|
|
0 commit comments