|
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