|
| 1 | +# Millrun |
| 2 | + |
| 3 | +## A Python library and CLI tool for automating the execution of papermill |
| 4 | + |
| 5 | +### Motivation |
| 6 | + |
| 7 | +Papermill is great: it parameterizes a single notebook for you. Ok, so what about this whole directory of notebooks that I would like to execute with this list of different parameters? |
| 8 | + |
| 9 | +**Millrun** Will execute either a single notebook or all of the notebooks in a directory (recursively, if you want) and using either a list of alternative parameter dictionaries or a dictionary with a list of variations. |
| 10 | + |
| 11 | +In short, it iterates both over notebooks in a directory AND over lists of parameters. |
| 12 | + |
| 13 | +_When executed as a CLI tool, notebooks are executed in parallel using multi-processing_. |
| 14 | + |
| 15 | +## Installation |
| 16 | + |
| 17 | +`pip install millrun` |
| 18 | + |
| 19 | +## Usage: Python Library |
| 20 | + |
| 21 | +```python |
| 22 | +import millrun |
| 23 | + |
| 24 | +millrun.execute_run( |
| 25 | + notebook_dir_or_file: pathlib.Path | str, |
| 26 | + bulk_params: list | dict, |
| 27 | + output_dir: Optional[pathlib.Path | str] = None, |
| 28 | + output_prepend_components: Optional[list[str]] = None, |
| 29 | + output_append_components: Optional[list[str]] = None, |
| 30 | + recursive: bool = False, |
| 31 | + exclude_glob_pattern: Optional[str] = None, |
| 32 | + include_glob_pattern: Optional[str] = None, |
| 33 | + use_multiprocessing: bool = False, |
| 34 | + **kwargs, # kwargs are passed through to papermill |
| 35 | +) |
| 36 | +``` |
| 37 | + |
| 38 | +## Usage: CLI tool |
| 39 | + |
| 40 | +``` |
| 41 | +millrun --help |
| 42 | + |
| 43 | + Usage: millrun [OPTIONS] NOTEBOOK_DIR_OR_FILE PARAMS |
| 44 | + |
| 45 | + Executes a notebook or directory of notebooks using the provided bulk parameters JSON file |
| 46 | + |
| 47 | + |
| 48 | +╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────╮ |
| 49 | +│ * notebook_dir_or_file TEXT Path to a notebook file or a directory containing notebooks. │ |
| 50 | +│ [default: None] │ |
| 51 | +│ [required] │ |
| 52 | +│ * params TEXT JSON file that contains parameters for notebook execution. Can │ |
| 53 | +│ either be a 'list of dict' or 'dict of list'. │ |
| 54 | +│ [default: None] │ |
| 55 | +│ [required] │ |
| 56 | +╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ |
| 57 | +╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────╮ |
| 58 | +│ --output-dir TEXT Directory to place output files into. If not │ |
| 59 | +│ provided the file directory will be used. │ |
| 60 | +│ [default: None] │ |
| 61 | +│ --prepend TEXT Prepend components to use on output filename.Can │ |
| 62 | +│ use dict keys from 'params' which will be │ |
| 63 | +│ evaluated.(Comma-separated values). │ |
| 64 | +│ [default: None] │ |
| 65 | +│ --append TEXT Append components to use on output filename.Can │ |
| 66 | +│ use dict keys from 'params' which will be │ |
| 67 | +│ evaluated.(Comma-separated values). │ |
| 68 | +│ [default: None] │ |
| 69 | +│ --recursive --no-recursive [default: no-recursive] │ |
| 70 | +│ --exclude-glob-pattern TEXT [default: None] │ |
| 71 | +│ --include-glob-pattern TEXT [default: None] │ |
| 72 | +│ --help Show this message and exit. │ |
| 73 | +╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ |
| 74 | +
|
| 75 | +``` |
| 76 | + |
| 77 | +### Example |
| 78 | + |
| 79 | +While the `prepend` argument is optional, it is highly recommend you take advantage of it. If not, your output file names will be automatically prepended with an integer index to differentiate the output files. |
| 80 | + |
| 81 | +``` |
| 82 | +millrun ./Notebooks_Dir params.json --prepend id_key_in_params |
| 83 | +``` |
| 84 | + |
| 85 | +Where `id_key_in_params` is one of the keys in your params.json that you can use to uniquely identify each iteration. If you do not have a single unique key, you can provide a list of keys and they will all be prepended: |
| 86 | + |
| 87 | +Lets say my params.json looked like this: |
| 88 | + |
| 89 | +```json |
| 90 | +{ |
| 91 | + "x_values": [0, 1, 2], |
| 92 | + "y_values": [45, 32, 60], |
| 93 | +} |
| 94 | + |
| 95 | +``` |
| 96 | + |
| 97 | +I could execute like this: |
| 98 | + |
| 99 | +``` |
| 100 | +millrun ./Notebooks_Dir params.json --prepend x_values,y_values,results |
| 101 | +``` |
| 102 | + |
| 103 | +And my output files would look like: |
| 104 | + |
| 105 | +``` |
| 106 | +0-45-results-special_calculation.ipynb |
| 107 | +1-32-results-special_calculation.ipynb |
| 108 | +2-60-results-special_calculation.ipynb |
| 109 | +``` |
| 110 | + |
| 111 | +**Notice**: Since "results" was not a key in my params.json, it gets passed through as a string literal. |
| 112 | + |
| 113 | +## Organizing your parameters |
| 114 | + |
| 115 | +You can have your parameters dictionary/JSON in one of two formats: |
| 116 | + |
| 117 | +### Format 1: A list of dicts |
| 118 | + |
| 119 | +```python |
| 120 | +[ |
| 121 | + {"param1": 0, "param2": "hat", "param3": 21.2}, |
| 122 | + {"param1": 1, "param2": "cat", "param3": 34.3}, |
| 123 | + {"param1": 2, "param2": "bat", "param3": 200.0} |
| 124 | +] |
| 125 | +``` |
| 126 | + |
| 127 | +Where each notebook given to millrun will execute against each dictionary in the list. |
| 128 | + |
| 129 | + |
| 130 | +### Format 2: A dict of lists |
| 131 | + |
| 132 | +```python |
| 133 | +{ |
| 134 | + "param1": [0, 1, 2], |
| 135 | + "param2": ["hat", "cat", "bat"], |
| 136 | + "param3": [21.2, 34.3, 200.0] |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +This format is offered as a convenience format. Internally, it is converted into "Format 1" prior to execution. |
| 141 | + |
| 142 | + |
| 143 | +## CLI parallel execution |
| 144 | + |
| 145 | +Since millrun iterates over two dimensions (each notebook and then dict of parameters in the list), there are two ways of parellelizing: |
| 146 | + |
| 147 | +1. Execute each notebook in sequence and parallelize the execution of the different parameter variations |
| 148 | +2. Execute each notebook in parallel and sequentialize the execution of the different parameter variations |
| 149 | + |
| 150 | +Because of my own personal use cases, it is more efficient for me to use **1.** because I have way more parameter variations than I do notebooks. |
| 151 | + |
| 152 | +However, this method becomes inefficient if you have MANY notebooks and only 1-3 variations. In that case, you would probably prefer the method **2.**. It is still faster than single-process execution (like you get ) |
| 153 | + |
| 154 | +If you need this use case then feel free to raise an issue and/or contribute a PR to implement it as an option for execution. |
0 commit comments