├── .gitignore ├── Makefile ├── README.md ├── cameo-01-predict-heterologous-pathways.ipynb ├── cameo-02-generating-gene-knockout-strategies.ipynb ├── cameo-03-gene-expression-modulation-targets.ipynb ├── cameo-schedule.md ├── cobra-schedule.md ├── cobrapy-01-getting-started.ipynb ├── cobrapy-02-genome-scale-metabolic-models.ipynb ├── cobrapy-03-manipulating-the-model.ipynb ├── cobrapy-04-simulating-and-analyzing-metabolic-models.ipynb ├── cobrapy-05-advanced-constraints-objectives.ipynb ├── data ├── S4_McCloskey2013_aerobic_metabolomics.csv ├── S6_RNA-seq_aerobic_to_anaerobic.csv ├── custom_map.json ├── e_coli_core.xml.gz ├── iJO1366.xml.gz ├── iMM904.xml.gz └── iris.csv ├── escher-01.ipynb ├── python-01-crash-course.ipynb ├── requirements.txt ├── slides.pdf └── slides.pptx /.gitignore: -------------------------------------------------------------------------------- 1 | /profile_default/history.sqlite 2 | /profile_default/startup/README 3 | /.ipynb_checkpoints/ 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install jupyter 2 | 3 | ################################################################################# 4 | # COMMANDS # 5 | ################################################################################# 6 | 7 | ## Install Python Dependencies 8 | install: 9 | pip install -U pip setuptools wheel 10 | pip install -r requirements.txt 11 | 12 | ## Install Jupyter notebook with extensions and widgets 13 | jupyter: install 14 | pip install jupyter jupyter_contrib_nbextensions jupyter_nbextensions_configurator ipywidgets 15 | jupyter contrib nbextension install --sys-prefix 16 | jupyter nbextensions_configurator enable 17 | jupyter nbextension enable --py --sys-prefix widgetsnbextension 18 | 19 | ################################################################################# 20 | # Self Documenting Commands # 21 | ################################################################################# 22 | 23 | .DEFAULT_GOAL := show-help 24 | 25 | # Inspired by 26 | # sed script explained: 27 | # /^##/: 28 | # * save line in hold space 29 | # * purge line 30 | # * Loop: 31 | # * append newline + line to hold space 32 | # * go to next line 33 | # * if line starts with doc comment, strip comment character off and loop 34 | # * remove target prerequisites 35 | # * append hold space (+ newline) to line 36 | # * replace newline plus comments by `---` 37 | # * print line 38 | # Separate expressions are necessary because labels cannot be delimited by 39 | # semicolon; see 40 | .PHONY: show-help 41 | show-help: 42 | @echo "$$(tput bold)Available rules:$$(tput sgr0)" 43 | @echo 44 | @sed -n -e "/^## / { \ 45 | h; \ 46 | s/.*//; \ 47 | :doc" \ 48 | -e "H; \ 49 | n; \ 50 | s/^## //; \ 51 | t doc" \ 52 | -e "s/:.*//; \ 53 | G; \ 54 | s/\\n## /---/; \ 55 | s/\\n/ /g; \ 56 | p; \ 57 | }" ${MAKEFILE_LIST} \ 58 | | LC_ALL='C' sort --ignore-case \ 59 | | awk -F '---' \ 60 | -v ncol=$$(tput cols) \ 61 | -v indent=19 \ 62 | -v col_on="$$(tput setaf 6)" \ 63 | -v col_off="$$(tput sgr0)" \ 64 | '{ \ 65 | printf "%s%*s%s ", col_on, -indent, $$1, col_off; \ 66 | n = split($$2, words, " "); \ 67 | line_length = ncol - indent; \ 68 | for (i = 1; i <= n; i++) { \ 69 | line_length -= length(words[i]) + 1; \ 70 | if (line_length <= 0) { \ 71 | line_length = ncol - indent - length(words[i]) - 1; \ 72 | printf "\n%*s ", -indent, " "; \ 73 | } \ 74 | printf "%s ", words[i]; \ 75 | } \ 76 | printf "\n"; \ 77 | }' \ 78 | | more $(shell test $(shell uname) = Darwin && echo '--no-init --raw-control-chars') 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ISCB Tutorial - cobrapy, escher, cameo and DD-DeCaF 2 | 3 | Most modern biotechnology applications depend on efficient algorithms for modeling metabolic flux and predicting viable strain designs. The [packages](http://opencobra.github.io/cobrapy/packages) in the rapidly growing [cobrapy community](http://opencobra.github.io/cobrapy/) provide implementations of numerous popular analysis and simulation methods for constraints-based metabolic flux models, as well as search optimization algorithms for designing novel pathways, all implemented in the widely popular and freely available programming language Python. 4 | 5 | In this tutorial, we will first give an introduction to the micro-service/web-based *data-driven design of cell factories communities platform ([DD-DeCaF](http://app.dd-decaf.eu))*, which aims to bring efficient data management, visualization and strain design methods to a broad audience. 6 | 7 | Following that, we provide a detailed, hands-on session using the cobrapy related packages that power the DD-DeCaF platform directly in a [Jupyter notebook](http://jupyter.org/). We will demonstrate how to use the [cobrapy](http://opencobra.github.io/cobrapy) package itself to create constraints-based metabolic flux models and to manipulate them programmatically. Examples will be given showing how to apply popular algorithms such as parsimonious flux balance analysis, flux variability analysis, flux sampling, and how to analyze the impact of knocking-out genes, finding blocked reactions, and much more. After the demonstration of cobrapy, we will proceed by providing an introduction to using [escher](https://escher.github.io/) to create elegant pathway visualizations. Finally, we will show-case the [cameo](http://cameo.bio) package allowing participants to predict heterologous pathways for selected targets and to optimize these using different genetic manipulation strategies. 8 | 9 | # Schedule and format 10 | 11 | We start each section with a short presentation of the introduced methods and algorithms to provide the necessary background and then interactively, step-by-step reproduce the analysis together with the participants. For the demonstration of DD-DeCaF, participants are encouraged to try the platform online. For the tutorial on cobrapy and cameo, users may either follow the presenter with their own local Python installation, or by using the provided [JupyterHub](https://workshop.dd-decaf.eu). 12 | 13 | Use our [etherpad](https://etherpad.net/p/icsb-tutorial) during the workshop. 14 | 15 | To enable as participants of different backgrounds to join, we will start the second part by giving a gentle introduction to programming in Python and using the Jupyter notebook. 16 | 17 | | Time | Topic | 18 | |------------|---------------------------------------------------------------------------------------------------------------| 19 | | **9:00** | (45 min) Demonstration of the DD-DeCaF platform | 20 | | **15 min** | Break | 21 | | **10:00** | (45 min) [Gentle introduction to programming in Python using Jupyter notebooks](python-01-crash-course.ipynb) | 22 | | **15 min** | Break | 23 | | **11:00** | (1 h) [Modeling metabolic flux using cobrapy](cobra-schedule.md) | 24 | | **1 h** | Lunch | 25 | | **13:00** | (1 h) [Modeling metabolic flux using cobrapy (continued)](cobra-schedule.md) | 26 | | **15 min** | Break | 27 | | **14:15** | (1 h) [Model and data visualization with Escher](escher-01.ipynb) | 28 | | **15 min** | Break | 29 | | **15:30** | (1 h 30 min) [Computational strain design using cameo](cameo-schedule.md) | 30 | | **17:00** | Finish | 31 | 32 | 33 | No prior knowledge of programming is needed only familiarity with fundamental systems biology concepts. Just bring your laptop (installation instructions will be given for Linux, Mac and Windows). 34 | 35 | # Names and affiliations of main organizers 36 | 37 | - Moritz E. Beber (DTU Biosustain) 38 | - Danny Dannaher (DTU Biosustain) 39 | - Svetlana Galkina (DTU Biosustain) 40 | - Zachary King (UC San Diego) 41 | - Henning Redestig (DTU Biosustain) 42 | - Nikolaus Sonnenschein (DTU Biosustain) 43 | -------------------------------------------------------------------------------- /cameo-schedule.md: -------------------------------------------------------------------------------- 1 | # Introduction to cameo 2 | 3 | Rough schedule, we will cover as much as we can and leave the rest as homework. 4 | 5 | - *25 min* [Pathway prediction](cameo-01-predict-heterologous-pathways.ipynb) 6 | - Predict pathways for a given compound 7 | - *35 min* [Generating gene knockout strategies](cameo-02-generating-gene-knockout-strategies.ipynb) 8 | - What's in the model, reactions, metabolites, genes 9 | - Expressions and objectives 10 | - Simulating using FBA, changing the objective 11 | - *30 min* [Get suggestions for expression modulation targets](cameo-03-gene-expression-modulation-targets.ipynb) 12 | - Flux scanning based on enforces objective flux 13 | - Differential FVA 14 | -------------------------------------------------------------------------------- /cobra-schedule.md: -------------------------------------------------------------------------------- 1 | # Introduction to cobrapy 2 | 3 | Rough schedule, we will cover as much as we can and leave the rest as homework. 4 | 5 | - *5 min* [Getting started](cobrapy-01-getting-started.ipynb) 6 | - Load a model and do FBA 7 | - *30 min* [Genome scale metabolic models and simulating](cobrapy-02-genome-scale-metabolic-models.ipynb) 8 | - What's in the model, reactions, metabolites, genes 9 | - Expressions and objectives 10 | - Simulating using FBA, changing the objective 11 | - *30 min* [Manipulating the model](cobrapy-03-manipulating-the-model.ipynb) 12 | - Adding and emoving reactions, metabolites 13 | - Changing and reverting using the context manager 14 | - *45 min* [Simulating and analyzing models](cobrapy-04-simulating-and-analyzing-metabolic-models.ipynb) 15 | - pFBA 16 | - FVA 17 | - Loopless 18 | - Additional constraints 19 | - Production envelope 20 | - Gene and reaction essentiality 21 | - *15 min* If there is time: [constraints and variables](cobrapy-05-advanced-constraints-objectives.ipynb) 22 | - Defining own variables and constraints 23 | -------------------------------------------------------------------------------- /cobrapy-01-getting-started.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Quick start" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Loading a model (1)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "from cobra.io import read_sbml_model" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 2, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "model = read_sbml_model('data/iJO1366.xml.gz')" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 3, 42 | "metadata": {}, 43 | "outputs": [ 44 | { 45 | "data": { 46 | "text/html": [ 47 | "\n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | "
NameiJO1366
Memory address0x07f4c36a88240
Number of metabolites1805
Number of reactions2583
Objective expression-1.0*BIOMASS_Ec_iJO1366_core_53p95M_reverse_5c8b1 + 1.0*BIOMASS_Ec_iJO1366_core_53p95M
Compartmentsperiplasm, cytosol, extracellular space
" 69 | ], 70 | "text/plain": [ 71 | "" 72 | ] 73 | }, 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "model" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "## Simulations (2 + 2)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "" 99 | ] 100 | }, 101 | "execution_count": 4, 102 | "metadata": {}, 103 | "output_type": "execute_result" 104 | } 105 | ], 106 | "source": [ 107 | "model.solver" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 5, 113 | "metadata": { 114 | "collapsed": true 115 | }, 116 | "outputs": [], 117 | "source": [ 118 | "model.solver = \"cplex\"" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 6, 124 | "metadata": { 125 | "collapsed": true 126 | }, 127 | "outputs": [], 128 | "source": [ 129 | "solution = model.optimize()" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "Check the growth rate:" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 7, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "data": { 146 | "text/plain": [ 147 | "[,\n", 148 | " ]" 149 | ] 150 | }, 151 | "execution_count": 7, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "model.reactions.query(\"BIOMASS\")" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 8, 163 | "metadata": {}, 164 | "outputs": [ 165 | { 166 | "data": { 167 | "text/plain": [ 168 | "0.98237181272698204" 169 | ] 170 | }, 171 | "execution_count": 8, 172 | "metadata": {}, 173 | "output_type": "execute_result" 174 | } 175 | ], 176 | "source": [ 177 | "solution.fluxes.BIOMASS_Ec_iJO1366_core_53p95M" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "We can investigate all fluxes by creating a data frame and getting all fluxes that are greater than 0.0001 mmol/g* DWh$^{-1}$" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 9, 190 | "metadata": {}, 191 | "outputs": [ 192 | { 193 | "data": { 194 | "text/html": [ 195 | "
\n", 196 | "\n", 209 | "\n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | "
fluxesreduced_costs
DM_4crsol_c0.00020.0000e+00
DM_5drib_c0.00020.0000e+00
DM_mththf_c0.00040.0000e+00
BIOMASS_Ec_iJO1366_core_53p95M0.98241.8492e-15
EX_ca2_e-0.00510.0000e+00
.........
UPPDC10.00020.0000e+00
USHD0.01910.0000e+00
VALTA-0.41570.0000e+00
ZN2tpp0.00030.0000e+00
Zn2tex0.0003-0.0000e+00
\n", 275 | "

413 rows × 2 columns

\n", 276 | "
" 277 | ], 278 | "text/plain": [ 279 | " fluxes reduced_costs\n", 280 | "DM_4crsol_c 0.0002 0.0000e+00\n", 281 | "DM_5drib_c 0.0002 0.0000e+00\n", 282 | "DM_mththf_c 0.0004 0.0000e+00\n", 283 | "BIOMASS_Ec_iJO1366_core_53p95M 0.9824 1.8492e-15\n", 284 | "EX_ca2_e -0.0051 0.0000e+00\n", 285 | "... ... ...\n", 286 | "UPPDC1 0.0002 0.0000e+00\n", 287 | "USHD 0.0191 0.0000e+00\n", 288 | "VALTA -0.4157 0.0000e+00\n", 289 | "ZN2tpp 0.0003 0.0000e+00\n", 290 | "Zn2tex 0.0003 -0.0000e+00\n", 291 | "\n", 292 | "[413 rows x 2 columns]" 293 | ] 294 | }, 295 | "execution_count": 9, 296 | "metadata": {}, 297 | "output_type": "execute_result" 298 | } 299 | ], 300 | "source": [ 301 | "solution_frame = solution.to_frame()\n", 302 | "solution_frame[solution_frame.fluxes.abs() > 1e-4]" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "metadata": {}, 308 | "source": [ 309 | "### Exercise" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "Convert the cell below to code and fill in the blanks. Read another model and use FBA to figure out the optimal growth rate." 317 | ] 318 | }, 319 | { 320 | "cell_type": "raw", 321 | "metadata": {}, 322 | "source": [ 323 | "from cobra.io import ____\n", 324 | "model = read_sbml_model(____)\n", 325 | "model.____" 326 | ] 327 | } 328 | ], 329 | "metadata": { 330 | "anaconda-cloud": {}, 331 | "kernelspec": { 332 | "display_name": "Python 3", 333 | "language": "python", 334 | "name": "python3" 335 | }, 336 | "language_info": { 337 | "codemirror_mode": { 338 | "name": "ipython", 339 | "version": 3 340 | }, 341 | "file_extension": ".py", 342 | "mimetype": "text/x-python", 343 | "name": "python", 344 | "nbconvert_exporter": "python", 345 | "pygments_lexer": "ipython3", 346 | "version": "3.5.2+" 347 | }, 348 | "toc": { 349 | "colors": { 350 | "hover_highlight": "#DAA520", 351 | "navigate_num": "#000000", 352 | "navigate_text": "#333333", 353 | "running_highlight": "#FF0000", 354 | "selected_highlight": "#FFD700", 355 | "sidebar_border": "#EEEEEE", 356 | "wrapper_background": "#FFFFFF" 357 | }, 358 | "moveMenuLeft": true, 359 | "nav_menu": { 360 | "height": "86px", 361 | "width": "252px" 362 | }, 363 | "navigate_menu": true, 364 | "number_sections": false, 365 | "sideBar": true, 366 | "threshold": "3", 367 | "toc_cell": false, 368 | "toc_section_display": "block", 369 | "toc_window_display": true, 370 | "widenNotebook": false 371 | } 372 | }, 373 | "nbformat": 4, 374 | "nbformat_minor": 1 375 | } 376 | -------------------------------------------------------------------------------- /cobrapy-02-genome-scale-metabolic-models.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Genome-scale metabolic models" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Preparation" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "from cobra.io import read_sbml_model" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 2, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "model = read_sbml_model('data/iJO1366.xml.gz')" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "## What is in a genome-scale metabolic model? (10 + 5)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 3, 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "data": { 53 | "text/html": [ 54 | "\n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | "
NameiJO1366
Memory address0x07f60d9887080
Number of metabolites1805
Number of reactions2583
Objective expression-1.0*BIOMASS_Ec_iJO1366_core_53p95M_reverse_5c8b1 + 1.0*BIOMASS_Ec_iJO1366_core_53p95M
Compartmentsperiplasm, cytosol, extracellular space
" 76 | ], 77 | "text/plain": [ 78 | "" 79 | ] 80 | }, 81 | "execution_count": 3, 82 | "metadata": {}, 83 | "output_type": "execute_result" 84 | } 85 | ], 86 | "source": [ 87 | "model" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "### Metabolites" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "The model contains a list of metabolites. Here are the first ten." 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 4, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "[,\n", 113 | " ,\n", 114 | " ,\n", 115 | " ,\n", 116 | " ,\n", 117 | " ,\n", 118 | " ,\n", 119 | " ,\n", 120 | " ,\n", 121 | " ]" 122 | ] 123 | }, 124 | "execution_count": 4, 125 | "metadata": {}, 126 | "output_type": "execute_result" 127 | } 128 | ], 129 | "source": [ 130 | "model.metabolites[0:10]" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "There are 1805 metabolites in the model." 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 5, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "1805" 149 | ] 150 | }, 151 | "execution_count": 5, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "len(model.metabolites)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "One can access a specific metabolite using dot notation." 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 6, 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/html": [ 175 | "\n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 191 | " \n", 192 | "
Metabolite identifierg3p_c
NameGlyceraldehyde 3-phosphate
Memory address0x07f608ba64b00
FormulaC3H5O6P
Compartmentc
In 14 reaction(s)\n", 190 | " F6PA, TKT2, EDA, TALA, DRPA, DXPS, TPI, TRPS1, TGBPA, GAPD, TKT1, DDPGALA, FBA, TRPS3
" 193 | ], 194 | "text/plain": [ 195 | "" 196 | ] 197 | }, 198 | "execution_count": 6, 199 | "metadata": {}, 200 | "output_type": "execute_result" 201 | } 202 | ], 203 | "source": [ 204 | "model.metabolites.g3p_c" 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": {}, 210 | "source": [ 211 | "
\n", 212 | "\n", 213 | "**Warning:** One cannot use dot notation to access metabolites, reactions, or genes if their identifiers do not resemble proper Python variable names.\n", 214 | "\n", 215 | "
" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 7, 221 | "metadata": { 222 | "collapsed": true 223 | }, 224 | "outputs": [], 225 | "source": [ 226 | "# the line below results in an error, uncomment to try\n", 227 | "# model.metabolites.10fthf_c\n", 228 | "\n", 229 | "\n", 230 | "# File \"\", line 1\n", 231 | "# model.metabolites.10fthf_c\n", 232 | "# ^\n", 233 | "# SyntaxError: invalid syntax" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "
\n", 241 | "\n", 242 | "**Solution:** Use the method `get_by_id` instead!\n", 243 | "\n", 244 | "
" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 8, 250 | "metadata": {}, 251 | "outputs": [ 252 | { 253 | "data": { 254 | "text/html": [ 255 | "\n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 271 | " \n", 272 | "
Metabolite identifier10fthf_c
Name10-Formyltetrahydrofolate
Memory address0x07f608bbae358
FormulaC20H21N7O7
Compartmentc
In 9 reaction(s)\n", 270 | " BIOMASS_Ec_iJO1366_WT_53p95M, FMETTRS, AICART, FTHFD, MTHFC, FTHFLi, GARFT, ULA4NFT, BIOMASS_Ec_iJO1366_core_53p95M
" 273 | ], 274 | "text/plain": [ 275 | "" 276 | ] 277 | }, 278 | "execution_count": 8, 279 | "metadata": {}, 280 | "output_type": "execute_result" 281 | } 282 | ], 283 | "source": [ 284 | "model.metabolites.get_by_id('10fthf_c')" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "Metabolites are associated with compartments in the cell. Glyceraldehyde 3-phosphate (`g3p_c`) is associated with the `c` (Cytosol) compartment." 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 9, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/plain": [ 302 | "'c'" 303 | ] 304 | }, 305 | "execution_count": 9, 306 | "metadata": {}, 307 | "output_type": "execute_result" 308 | } 309 | ], 310 | "source": [ 311 | "model.metabolites.g3p_c.compartment" 312 | ] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "The _E. coli_ model has three compartments." 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 10, 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "{'c': 'cytosol', 'e': 'extracellular space', 'p': 'periplasm'}" 330 | ] 331 | }, 332 | "execution_count": 10, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | } 336 | ], 337 | "source": [ 338 | "model.compartments" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "Some metabolites (like Glucose for example) can be associated with multiple compartments." 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 11, 351 | "metadata": {}, 352 | "outputs": [ 353 | { 354 | "data": { 355 | "text/plain": [ 356 | "'c'" 357 | ] 358 | }, 359 | "execution_count": 11, 360 | "metadata": {}, 361 | "output_type": "execute_result" 362 | } 363 | ], 364 | "source": [ 365 | "model.metabolites.glc__D_c.compartment" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 12, 371 | "metadata": {}, 372 | "outputs": [ 373 | { 374 | "data": { 375 | "text/plain": [ 376 | "'p'" 377 | ] 378 | }, 379 | "execution_count": 12, 380 | "metadata": {}, 381 | "output_type": "execute_result" 382 | } 383 | ], 384 | "source": [ 385 | "model.metabolites.glc__D_p.compartment" 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "The full name of the metabolite is available via the `.name` attribute. " 393 | ] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "execution_count": 13, 398 | "metadata": {}, 399 | "outputs": [ 400 | { 401 | "data": { 402 | "text/plain": [ 403 | "'D-Glucose'" 404 | ] 405 | }, 406 | "execution_count": 13, 407 | "metadata": {}, 408 | "output_type": "execute_result" 409 | } 410 | ], 411 | "source": [ 412 | "model.metabolites.glc__D_c.name" 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": {}, 418 | "source": [ 419 | "One can look up the molecular formula of glucose." 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": 14, 425 | "metadata": {}, 426 | "outputs": [ 427 | { 428 | "data": { 429 | "text/plain": [ 430 | "'C3H5O6P'" 431 | ] 432 | }, 433 | "execution_count": 14, 434 | "metadata": {}, 435 | "output_type": "execute_result" 436 | } 437 | ], 438 | "source": [ 439 | "model.metabolites.g3p_c.formula" 440 | ] 441 | }, 442 | { 443 | "cell_type": "markdown", 444 | "metadata": {}, 445 | "source": [ 446 | "The `.elements` attribute returns a dictionary representation of the formula." 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": 15, 452 | "metadata": {}, 453 | "outputs": [ 454 | { 455 | "data": { 456 | "text/plain": [ 457 | "{'C': 3, 'H': 5, 'O': 6, 'P': 1}" 458 | ] 459 | }, 460 | "execution_count": 15, 461 | "metadata": {}, 462 | "output_type": "execute_result" 463 | } 464 | ], 465 | "source": [ 466 | "model.metabolites.g3p_c.elements" 467 | ] 468 | }, 469 | { 470 | "cell_type": "markdown", 471 | "metadata": {}, 472 | "source": [ 473 | "Furthermore, one can look up the molecular weight of a metabolite." 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "execution_count": 16, 479 | "metadata": {}, 480 | "outputs": [ 481 | { 482 | "data": { 483 | "text/plain": [ 484 | "168.041961" 485 | ] 486 | }, 487 | "execution_count": 16, 488 | "metadata": {}, 489 | "output_type": "execute_result" 490 | } 491 | ], 492 | "source": [ 493 | "model.metabolites.g3p_c.formula_weight" 494 | ] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": {}, 499 | "source": [ 500 | "One can gather additional information (like references to external datbases) about the metabolite through the annotation attribute." 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": 17, 506 | "metadata": {}, 507 | "outputs": [ 508 | { 509 | "data": { 510 | "text/plain": [ 511 | "{'SBO': 'SBO:0000247',\n", 512 | " 'bigg.metabolite': 'g3p',\n", 513 | " 'biocyc': 'META:GAP',\n", 514 | " 'chebi': ['CHEBI:12983',\n", 515 | " 'CHEBI:12984',\n", 516 | " 'CHEBI:14333',\n", 517 | " 'CHEBI:17138',\n", 518 | " 'CHEBI:181',\n", 519 | " 'CHEBI:18324',\n", 520 | " 'CHEBI:21026',\n", 521 | " 'CHEBI:29052',\n", 522 | " 'CHEBI:5446',\n", 523 | " 'CHEBI:58027',\n", 524 | " 'CHEBI:59776'],\n", 525 | " 'hmdb': 'HMDB01112',\n", 526 | " 'kegg.compound': ['C00118', 'C00661'],\n", 527 | " 'metanetx.chemical': 'MNXM2378',\n", 528 | " 'reactome': '29578',\n", 529 | " 'seed.compound': 'cpd00102',\n", 530 | " 'unipathway.compound': ['UPC00118', 'UPC00661']}" 531 | ] 532 | }, 533 | "execution_count": 17, 534 | "metadata": {}, 535 | "output_type": "execute_result" 536 | } 537 | ], 538 | "source": [ 539 | "model.metabolites.g3p_c.annotation" 540 | ] 541 | }, 542 | { 543 | "cell_type": "markdown", 544 | "metadata": {}, 545 | "source": [ 546 | "One can use these annotations to look up the compound on [KEGG](http://www.genome.jp/dbget-bin/www_bget?cpd:C00118) for example." 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": {}, 552 | "source": [ 553 | "Metabolites are not isolated things. They participate in reactions as substrates and products." 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": 18, 559 | "metadata": {}, 560 | "outputs": [ 561 | { 562 | "data": { 563 | "text/plain": [ 564 | "frozenset({,\n", 565 | " ,\n", 566 | " ,\n", 567 | " ,\n", 568 | " ,\n", 569 | " ,\n", 570 | " ,\n", 571 | " ,\n", 572 | " ,\n", 573 | " ,\n", 574 | " ,\n", 575 | " ,\n", 576 | " ,\n", 577 | " })" 578 | ] 579 | }, 580 | "execution_count": 18, 581 | "metadata": {}, 582 | "output_type": "execute_result" 583 | } 584 | ], 585 | "source": [ 586 | "model.metabolites.g3p_c.reactions" 587 | ] 588 | }, 589 | { 590 | "cell_type": "markdown", 591 | "metadata": {}, 592 | "source": [ 593 | "### Reactions" 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": {}, 599 | "source": [ 600 | "The model contains a list of reactions. Here are the first 10 of them." 601 | ] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": 19, 606 | "metadata": {}, 607 | "outputs": [ 608 | { 609 | "data": { 610 | "text/plain": [ 611 | "[,\n", 612 | " ,\n", 613 | " ,\n", 614 | " ,\n", 615 | " ,\n", 616 | " ,\n", 617 | " ,\n", 618 | " ,\n", 619 | " ,\n", 620 | " ]" 621 | ] 622 | }, 623 | "execution_count": 19, 624 | "metadata": {}, 625 | "output_type": "execute_result" 626 | } 627 | ], 628 | "source": [ 629 | "model.reactions[0:10]" 630 | ] 631 | }, 632 | { 633 | "cell_type": "markdown", 634 | "metadata": {}, 635 | "source": [ 636 | "There are 2583 reactions in the model." 637 | ] 638 | }, 639 | { 640 | "cell_type": "code", 641 | "execution_count": 20, 642 | "metadata": {}, 643 | "outputs": [ 644 | { 645 | "data": { 646 | "text/plain": [ 647 | "2583" 648 | ] 649 | }, 650 | "execution_count": 20, 651 | "metadata": {}, 652 | "output_type": "execute_result" 653 | } 654 | ], 655 | "source": [ 656 | "len(model.reactions)" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": {}, 662 | "source": [ 663 | "Let's take a closer look at the reactions associated with Glyceraldehyde 3-phosphate (`g3p`)." 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": 21, 669 | "metadata": {}, 670 | "outputs": [ 671 | { 672 | "name": "stdout", 673 | "output_type": "stream", 674 | "text": [ 675 | "F6PA: f6p_c <=> dha_c + g3p_c Fructose 6-phosphate aldolase\n", 676 | "TKT2: e4p_c + xu5p__D_c <=> f6p_c + g3p_c Transketolase\n", 677 | "EDA: 2ddg6p_c --> g3p_c + pyr_c 2-dehydro-3-deoxy-phosphogluconate aldolase\n", 678 | "TALA: g3p_c + s7p_c <=> e4p_c + f6p_c Transaldolase\n", 679 | "DRPA: 2dr5p_c --> acald_c + g3p_c Deoxyribose-phosphate aldolase\n", 680 | "DXPS: g3p_c + h_c + pyr_c --> co2_c + dxyl5p_c 1-deoxy-D-xylulose 5-phosphate synthase\n", 681 | "TPI: dhap_c <=> g3p_c Triose-phosphate isomerase\n", 682 | "TRPS1: 3ig3p_c + ser__L_c --> g3p_c + h2o_c + trp__L_c Tryptophan synthase (indoleglycerol phosphate)\n", 683 | "TGBPA: tagdp__D_c <=> dhap_c + g3p_c Tagatose-bisphosphate aldolase\n", 684 | "GAPD: g3p_c + nad_c + pi_c <=> 13dpg_c + h_c + nadh_c Glyceraldehyde-3-phosphate dehydrogenase\n", 685 | "TKT1: r5p_c + xu5p__D_c <=> g3p_c + s7p_c Transketolase\n", 686 | "DDPGALA: 2dh3dgal6p_c <=> g3p_c + pyr_c 2-dehydro-3-deoxy-6-phosphogalactonate aldolase\n", 687 | "FBA: fdp_c <=> dhap_c + g3p_c Fructose-bisphosphate aldolase\n", 688 | "TRPS3: 3ig3p_c --> g3p_c + indole_c Tryptophan synthase (indoleglycerol phosphate)\n" 689 | ] 690 | } 691 | ], 692 | "source": [ 693 | "for reaction in model.metabolites.g3p_c.reactions:\n", 694 | " print(reaction, reaction.name)" 695 | ] 696 | }, 697 | { 698 | "cell_type": "markdown", 699 | "metadata": {}, 700 | "source": [ 701 | "The second reaction in this list is Glyceraldehyde-3-phosphate dehydrogenase (GAPD)." 702 | ] 703 | }, 704 | { 705 | "cell_type": "code", 706 | "execution_count": 22, 707 | "metadata": {}, 708 | "outputs": [ 709 | { 710 | "data": { 711 | "text/plain": [ 712 | "'Glyceraldehyde-3-phosphate dehydrogenase'" 713 | ] 714 | }, 715 | "execution_count": 22, 716 | "metadata": {}, 717 | "output_type": "execute_result" 718 | } 719 | ], 720 | "source": [ 721 | "model.reactions.GAPD.name" 722 | ] 723 | }, 724 | { 725 | "cell_type": "markdown", 726 | "metadata": {}, 727 | "source": [ 728 | "A reaction has a flux expression, which in cobrapy is coded as the forward flux + the reverse flux (a convenient way to represent the mathematical problem)." 729 | ] 730 | }, 731 | { 732 | "cell_type": "code", 733 | "execution_count": 23, 734 | "metadata": {}, 735 | "outputs": [ 736 | { 737 | "data": { 738 | "text/plain": [ 739 | "1.0*GAPD - 1.0*GAPD_reverse_459c1" 740 | ] 741 | }, 742 | "execution_count": 23, 743 | "metadata": {}, 744 | "output_type": "execute_result" 745 | } 746 | ], 747 | "source": [ 748 | "model.reactions.GAPD.flux_expression" 749 | ] 750 | }, 751 | { 752 | "cell_type": "markdown", 753 | "metadata": {}, 754 | "source": [ 755 | "### Genes and gene-protein-reaction associations" 756 | ] 757 | }, 758 | { 759 | "cell_type": "markdown", 760 | "metadata": {}, 761 | "source": [ 762 | "The model also contains genes (perhaps more appropriately gene products) and they belong to reactions." 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 24, 768 | "metadata": {}, 769 | "outputs": [ 770 | { 771 | "data": { 772 | "text/plain": [ 773 | "[,\n", 774 | " ,\n", 775 | " ,\n", 776 | " ,\n", 777 | " ]" 778 | ] 779 | }, 780 | "execution_count": 24, 781 | "metadata": {}, 782 | "output_type": "execute_result" 783 | } 784 | ], 785 | "source": [ 786 | "model.genes[0:5]" 787 | ] 788 | }, 789 | { 790 | "cell_type": "code", 791 | "execution_count": 25, 792 | "metadata": {}, 793 | "outputs": [ 794 | { 795 | "data": { 796 | "text/plain": [ 797 | "frozenset({,\n", 798 | " })" 799 | ] 800 | }, 801 | "execution_count": 25, 802 | "metadata": {}, 803 | "output_type": "execute_result" 804 | } 805 | ], 806 | "source": [ 807 | "model.genes.b1779.reactions" 808 | ] 809 | }, 810 | { 811 | "cell_type": "markdown", 812 | "metadata": {}, 813 | "source": [ 814 | "The gapA (b1779) gene is the only one associated to reaction glyceraldehyde-3-phosphate dehydrogenase." 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": 26, 820 | "metadata": {}, 821 | "outputs": [ 822 | { 823 | "data": { 824 | "text/plain": [ 825 | "'b1779'" 826 | ] 827 | }, 828 | "execution_count": 26, 829 | "metadata": {}, 830 | "output_type": "execute_result" 831 | } 832 | ], 833 | "source": [ 834 | "model.reactions.GAPD.gene_reaction_rule" 835 | ] 836 | }, 837 | { 838 | "cell_type": "markdown", 839 | "metadata": {}, 840 | "source": [ 841 | "Phosphofructokinase (PFK) on the other hand seems to be associated with to isozymes." 842 | ] 843 | }, 844 | { 845 | "cell_type": "code", 846 | "execution_count": 27, 847 | "metadata": {}, 848 | "outputs": [ 849 | { 850 | "data": { 851 | "text/plain": [ 852 | "'b3916 or b1723'" 853 | ] 854 | }, 855 | "execution_count": 27, 856 | "metadata": {}, 857 | "output_type": "execute_result" 858 | } 859 | ], 860 | "source": [ 861 | "model.reactions.PFK.gene_reaction_rule" 862 | ] 863 | }, 864 | { 865 | "cell_type": "markdown", 866 | "metadata": {}, 867 | "source": [ 868 | "One can display the gene names (typical 4 letter gene codes) instead of the identifiers (Blattner numbers in this case)." 869 | ] 870 | }, 871 | { 872 | "cell_type": "code", 873 | "execution_count": 28, 874 | "metadata": {}, 875 | "outputs": [ 876 | { 877 | "data": { 878 | "text/plain": [ 879 | "'pfkA or pfkB'" 880 | ] 881 | }, 882 | "execution_count": 28, 883 | "metadata": {}, 884 | "output_type": "execute_result" 885 | } 886 | ], 887 | "source": [ 888 | "model.reactions.PFK.gene_name_reaction_rule" 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": {}, 894 | "source": [ 895 | "Reactions are functional if all genes are functional" 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": 29, 901 | "metadata": {}, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/html": [ 906 | "\n", 907 | " \n", 908 | " \n", 909 | " \n", 910 | " \n", 911 | " \n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | "
Reaction identifierPFK
NamePhosphofructokinase
Memory address0x07f608b341ef0
Stoichiometry\n", 918 | "

atp_c + f6p_c --> adp_c + fdp_c + h_c

\n", 919 | "

ATP + D-Fructose 6-phosphate --> ADP + D-Fructose 1,6-bisphosphate + H+

\n", 920 | "
GPRb3916 or b1723
Lower bound0.0
Upper bound1000.0
\n", 929 | " " 930 | ], 931 | "text/plain": [ 932 | "" 933 | ] 934 | }, 935 | "execution_count": 29, 936 | "metadata": {}, 937 | "output_type": "execute_result" 938 | } 939 | ], 940 | "source": [ 941 | "model.reactions.PFK" 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": 30, 947 | "metadata": {}, 948 | "outputs": [ 949 | { 950 | "name": "stdout", 951 | "output_type": "stream", 952 | "text": [ 953 | "True True True\n" 954 | ] 955 | } 956 | ], 957 | "source": [ 958 | "print(model.genes.b3916.functional, model.genes.b1723.functional, model.reactions.PFK.functional)" 959 | ] 960 | }, 961 | { 962 | "cell_type": "markdown", 963 | "metadata": {}, 964 | "source": [ 965 | "If we knock out one of the isozymes, the PFK reaction stays functional, but not if we knock both." 966 | ] 967 | }, 968 | { 969 | "cell_type": "code", 970 | "execution_count": 31, 971 | "metadata": {}, 972 | "outputs": [ 973 | { 974 | "name": "stdout", 975 | "output_type": "stream", 976 | "text": [ 977 | "True\n", 978 | "False\n" 979 | ] 980 | } 981 | ], 982 | "source": [ 983 | "model_copy = model.copy()\n", 984 | "model_copy.genes.b3916.knock_out()\n", 985 | "print(model_copy.reactions.PFK.functional)\n", 986 | "\n", 987 | "model_copy = model.copy()\n", 988 | "model_copy.genes.b3916.knock_out()\n", 989 | "model_copy.genes.b1723.knock_out()\n", 990 | "print(model_copy.reactions.PFK.functional)" 991 | ] 992 | }, 993 | { 994 | "cell_type": "markdown", 995 | "metadata": {}, 996 | "source": [ 997 | "### Objective" 998 | ] 999 | }, 1000 | { 1001 | "cell_type": "markdown", 1002 | "metadata": {}, 1003 | "source": [ 1004 | "In the iJO1366 model, the default objective is to maximize the flux through the biomass reaction (i.e. growth). " 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "code", 1009 | "execution_count": 32, 1010 | "metadata": {}, 1011 | "outputs": [ 1012 | { 1013 | "name": "stdout", 1014 | "output_type": "stream", 1015 | "text": [ 1016 | "Maximize\n", 1017 | "-1.0*BIOMASS_Ec_iJO1366_core_53p95M_reverse_5c8b1 + 1.0*BIOMASS_Ec_iJO1366_core_53p95M\n" 1018 | ] 1019 | } 1020 | ], 1021 | "source": [ 1022 | "print(model.objective)" 1023 | ] 1024 | }, 1025 | { 1026 | "cell_type": "markdown", 1027 | "metadata": {}, 1028 | "source": [ 1029 | "### Querying" 1030 | ] 1031 | }, 1032 | { 1033 | "cell_type": "markdown", 1034 | "metadata": { 1035 | "collapsed": true 1036 | }, 1037 | "source": [ 1038 | "One can use `.query('search term', 'attribute_to_search_in')` to search in model metabolites, reactions, and genes. For example, one can search metabolites that contain the term _glucose_ in their name." 1039 | ] 1040 | }, 1041 | { 1042 | "cell_type": "code", 1043 | "execution_count": 33, 1044 | "metadata": {}, 1045 | "outputs": [ 1046 | { 1047 | "name": "stdout", 1048 | "output_type": "stream", 1049 | "text": [ 1050 | "6-Acetyl-D-glucose\n", 1051 | "ADPglucose\n", 1052 | "DTDP-4-dehydro-6-deoxy-D-glucose\n", 1053 | "DTDPglucose\n", 1054 | "UDPglucose\n", 1055 | "UDPglucose\n", 1056 | "UDPglucose\n" 1057 | ] 1058 | } 1059 | ], 1060 | "source": [ 1061 | "for metabolite in model.metabolites.query('glucose', 'name'):\n", 1062 | " print(metabolite.name)" 1063 | ] 1064 | }, 1065 | { 1066 | "cell_type": "markdown", 1067 | "metadata": {}, 1068 | "source": [ 1069 | "### Exercises" 1070 | ] 1071 | }, 1072 | { 1073 | "cell_type": "markdown", 1074 | "metadata": {}, 1075 | "source": [ 1076 | "Convert the cells below to code and fill in the blanks" 1077 | ] 1078 | }, 1079 | { 1080 | "cell_type": "raw", 1081 | "metadata": {}, 1082 | "source": [ 1083 | "# which reactions includes cytosolic atp?\n", 1084 | "model.____.atp_c.____" 1085 | ] 1086 | }, 1087 | { 1088 | "cell_type": "raw", 1089 | "metadata": {}, 1090 | "source": [ 1091 | "# which metabolite is named adenosine\n", 1092 | "model.metabolites.____(____, ____)" 1093 | ] 1094 | }, 1095 | { 1096 | "cell_type": "raw", 1097 | "metadata": {}, 1098 | "source": [ 1099 | "# which genes are necessary for PFK reaction?\n", 1100 | "model.____.____.____" 1101 | ] 1102 | }, 1103 | { 1104 | "cell_type": "markdown", 1105 | "metadata": {}, 1106 | "source": [ 1107 | "## Simulating models with regular flux balance analysis (FBA) (5 + 5)" 1108 | ] 1109 | }, 1110 | { 1111 | "cell_type": "code", 1112 | "execution_count": 34, 1113 | "metadata": {}, 1114 | "outputs": [ 1115 | { 1116 | "data": { 1117 | "text/html": [ 1118 | "Optimal solution with objective value 0.982
\n", 1119 | "\n", 1132 | "\n", 1133 | " \n", 1134 | " \n", 1135 | " \n", 1136 | " \n", 1137 | " \n", 1138 | " \n", 1139 | " \n", 1140 | " \n", 1141 | " \n", 1142 | " \n", 1143 | " \n", 1144 | " \n", 1145 | " \n", 1146 | " \n", 1147 | " \n", 1148 | " \n", 1149 | " \n", 1150 | " \n", 1151 | " \n", 1152 | " \n", 1153 | " \n", 1154 | " \n", 1155 | " \n", 1156 | " \n", 1157 | " \n", 1158 | " \n", 1159 | " \n", 1160 | " \n", 1161 | " \n", 1162 | " \n", 1163 | " \n", 1164 | " \n", 1165 | " \n", 1166 | " \n", 1167 | " \n", 1168 | " \n", 1169 | " \n", 1170 | " \n", 1171 | " \n", 1172 | " \n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | " \n", 1191 | " \n", 1192 | " \n", 1193 | " \n", 1194 | " \n", 1195 | " \n", 1196 | " \n", 1197 | "
fluxesreduced_costs
DM_4crsol_c2.1907e-040.0000
DM_5drib_c2.2103e-040.0000
DM_aacald_c0.0000e+000.0000
DM_amob_c1.9647e-060.0000
DM_mththf_c4.4010e-040.0000
.........
ZN2abcpp0.0000e+00-0.0083
ZN2t3pp0.0000e+00-0.0021
ZN2tpp3.3499e-040.0000
ZNabcpp0.0000e+00-0.0083
Zn2tex3.3499e-040.0000
\n", 1198 | "

2583 rows × 2 columns

\n", 1199 | "
" 1200 | ], 1201 | "text/plain": [ 1202 | "" 1203 | ] 1204 | }, 1205 | "execution_count": 34, 1206 | "metadata": {}, 1207 | "output_type": "execute_result" 1208 | } 1209 | ], 1210 | "source": [ 1211 | "model.optimize()" 1212 | ] 1213 | }, 1214 | { 1215 | "cell_type": "markdown", 1216 | "metadata": {}, 1217 | "source": [ 1218 | "Sometimes a solution cannot be found. For example, setting the lower bound of the objective function to a very high value that the model cannot achieve will trigger a warning when trying to optimize the model. Parameters reported from an infeasible model are not meaningful to interpret (except in rare occasions when you may want to figure out why a model is infeasible)." 1219 | ] 1220 | }, 1221 | { 1222 | "cell_type": "code", 1223 | "execution_count": 35, 1224 | "metadata": {}, 1225 | "outputs": [ 1226 | { 1227 | "name": "stderr", 1228 | "output_type": "stream", 1229 | "text": [ 1230 | "cobra/util/solver.py:419 \u001b[1;31mUserWarning\u001b[0m: solver status is 'infeasible'\n" 1231 | ] 1232 | }, 1233 | { 1234 | "data": { 1235 | "text/html": [ 1236 | "infeasible solution" 1237 | ], 1238 | "text/plain": [ 1239 | "" 1240 | ] 1241 | }, 1242 | "execution_count": 35, 1243 | "metadata": {}, 1244 | "output_type": "execute_result" 1245 | } 1246 | ], 1247 | "source": [ 1248 | "infeasible_model = model.copy()\n", 1249 | "infeasible_model.reactions.BIOMASS_Ec_iJO1366_core_53p95M.lower_bound = 100000\n", 1250 | "infeasible_model.optimize()" 1251 | ] 1252 | }, 1253 | { 1254 | "cell_type": "markdown", 1255 | "metadata": {}, 1256 | "source": [ 1257 | "Mathematical solvers are now so fast that for computing the solution can be even faster than it takes to collect the values from the solver. If we are only interested in the flux value of a single reaction or the objective, it is faster to use `model.slim_optimize` which only does the optimization and returnsm the objective value leaving it up to you to fetch other values that you may need. For example, let's optimize and get the flux value of the `ATPM` reaction." 1258 | ] 1259 | }, 1260 | { 1261 | "cell_type": "code", 1262 | "execution_count": 36, 1263 | "metadata": {}, 1264 | "outputs": [ 1265 | { 1266 | "name": "stdout", 1267 | "output_type": "stream", 1268 | "text": [ 1269 | "CPU times: user 20 ms, sys: 0 ns, total: 20 ms\n", 1270 | "Wall time: 19.3 ms\n" 1271 | ] 1272 | } 1273 | ], 1274 | "source": [ 1275 | "%%time\n", 1276 | "solution = model.optimize()\n", 1277 | "solution.fluxes['ATPM']" 1278 | ] 1279 | }, 1280 | { 1281 | "cell_type": "code", 1282 | "execution_count": 37, 1283 | "metadata": {}, 1284 | "outputs": [ 1285 | { 1286 | "name": "stdout", 1287 | "output_type": "stream", 1288 | "text": [ 1289 | "CPU times: user 0 ns, sys: 0 ns, total: 0 ns\n", 1290 | "Wall time: 1.72 ms\n" 1291 | ] 1292 | } 1293 | ], 1294 | "source": [ 1295 | "%%time\n", 1296 | "model.slim_optimize()\n", 1297 | "model.reactions.ATPM.flux" 1298 | ] 1299 | }, 1300 | { 1301 | "cell_type": "markdown", 1302 | "metadata": {}, 1303 | "source": [ 1304 | "### Exercises" 1305 | ] 1306 | }, 1307 | { 1308 | "cell_type": "markdown", 1309 | "metadata": {}, 1310 | "source": [ 1311 | "Convert the cell below to code and fill in the blanks." 1312 | ] 1313 | }, 1314 | { 1315 | "cell_type": "markdown", 1316 | "metadata": {}, 1317 | "source": [ 1318 | "Optimize for acetate production at minimum 0.1 $h^{-1}$ growth rate" 1319 | ] 1320 | }, 1321 | { 1322 | "cell_type": "raw", 1323 | "metadata": {}, 1324 | "source": [ 1325 | "model.____.BIOMASS_Ec_iJO1366_core_53p95M.____ = ____\n", 1326 | "____.____ = ___.____.EX_ac_e\n", 1327 | "____.____()" 1328 | ] 1329 | }, 1330 | { 1331 | "cell_type": "markdown", 1332 | "metadata": {}, 1333 | "source": [ 1334 | "## The math (5)" 1335 | ] 1336 | }, 1337 | { 1338 | "cell_type": "markdown", 1339 | "metadata": {}, 1340 | "source": [ 1341 | "### The mathematical problem" 1342 | ] 1343 | }, 1344 | { 1345 | "cell_type": "markdown", 1346 | "metadata": {}, 1347 | "source": [ 1348 | "We can calculate fluxes using our model by formulating it as a mathematical problem. There, fluxes through reactions become variables and metabolites become constraints. By deciding that fluxes in and out of the system must add up to zero, we can then compute possible flux values for all reactions (if the model is feasible). We can inspect the whole equation by:" 1349 | ] 1350 | }, 1351 | { 1352 | "cell_type": "code", 1353 | "execution_count": 38, 1354 | "metadata": {}, 1355 | "outputs": [ 1356 | { 1357 | "name": "stdout", 1358 | "output_type": "stream", 1359 | "text": [ 1360 | "\\* Problem: Unknown *\\\n", 1361 | "\n", 1362 | "Maximize\n", 1363 | " obj: + BIOMASS_Ec_iJO1366_core_53p95M\n", 1364 | " - BIOMASS_Ec_iJO1366_core_53p95M_reverse_5c8b1\n", 1365 | "\n", 1366 | "Subject To\n", 1367 | " r_1: + FMETTRS_reverse_3b6c6 + FTHFD_reverse_44321\n", 1368 | " - MTHFC_reverse_f6fcc\n", 1369 | " + 0.000223 BIOMASS_Ec_iJO1366_WT_53p95M_reverse_06c4a\n", 1370 | " + AICART_reverse_b7b59 - 0.000223 BIOMASS_Ec_iJO1366_WT_53p95M - FTHFD\n", 1371 | " + 0.000223 BIOMASS_Ec_iJO1366_core_53p95M_reverse_5c8b1 - FMETTRS\n", 1372 | " + GARFT_reverse_7ecb6 - 0.000223 BIOMASS_Ec_iJO1366_core_53p95M - GARFT\n", 1373 | " + FTHFLi - FTHFLi_reverse_a6dc7 + ULA4NFT_reverse_07217 - AICART\n", 1374 | " - ULA4NFT + MTHFC = 0\n", 1375 | " r_2: + PAPA120 + x_665 - PAPA120_reverse_75d70 - x_666 - DAGK120\n", 1376 | " + DAGK120_reverse_7cd00 = 0\n", 1377 | " r_3: - PAPA140_reverse_255f5 - DAGK140 + DAGK140_reverse_87f8f\n", 1378 | " + PAPA140 - x_668 + x_667 = 0\n", 1379 | " r_4: - x_670 + PAPA141 + x_669 - PAPA141_reverse_945ab\n", 1380 | " + DAGK141_reverse_f6e5f - DAGK141 = 0\n", 1381 | " r_5: - DAGK160 + DAGK160_reverse_0238d + x_671 + PAPA160 - x_672\n", 1382 | " - PAPA160_reverse_c64df = 0\n", 1383 | " r_6: - PAPA161_reverse_1bc33 + DAGK161_reverse_9bfe7 + PAPA161 + x_673\n", 1384 | " - DAGK161 - x_674 = 0\n", 1385 | " r_7: + x_675 - DAGK180 + PAPA180 - x_676 - PAPA180_reverse_9f7f6\n", 1386 | " + DAGK180_reverse_eb3e3 = 0\n", 1387 | " r_8: + DAGK181_reverse_8c0c8 - DAGK181 + x_677 - x_678 + PAPA181\n", 1388 | " - PAPA181_reverse_fb09e = 0\n", 1389 | " r_9: - x_682 - ALR4x_reverse_17ebf + LCARR - LCARR_reverse_9213d\n", 1390 | " + x_681 + ALR4x = 0\n", 1391 | " r_10: - LCARS_reverse_66c3d - x_686 + LCARS + x_685 = 0\n", 1392 | " r_11: - GAPD_reverse_459c1 + GAPD + PGK - PGK_reverse_02696 = 0\n", 1393 | " r_12: + DHNCOAT_reverse_58c26 - DHNCOAT + DHNCOAS\n", 1394 | " - DHNCOAS_reverse_af3a9 = 0\n", 1395 | " r_13: - AAMYL + AAMYL_reverse_58\n" 1396 | ] 1397 | } 1398 | ], 1399 | "source": [ 1400 | "print(str(model.solver)[0:1550])" 1401 | ] 1402 | }, 1403 | { 1404 | "cell_type": "markdown", 1405 | "metadata": {}, 1406 | "source": [ 1407 | "Mostly, we luckily do not have to care much about this as cobrapy will use optlang and dedicated mathematical solvers to do the hard work for us, but it can be good know about the separation between the metabolic model and the mathematical problem." 1408 | ] 1409 | }, 1410 | { 1411 | "cell_type": "code", 1412 | "execution_count": 39, 1413 | "metadata": {}, 1414 | "outputs": [ 1415 | { 1416 | "data": { 1417 | "text/plain": [ 1418 | "5166" 1419 | ] 1420 | }, 1421 | "execution_count": 39, 1422 | "metadata": {}, 1423 | "output_type": "execute_result" 1424 | } 1425 | ], 1426 | "source": [ 1427 | "len(model.variables)" 1428 | ] 1429 | }, 1430 | { 1431 | "cell_type": "code", 1432 | "execution_count": 40, 1433 | "metadata": {}, 1434 | "outputs": [ 1435 | { 1436 | "data": { 1437 | "text/plain": [ 1438 | "1805" 1439 | ] 1440 | }, 1441 | "execution_count": 40, 1442 | "metadata": {}, 1443 | "output_type": "execute_result" 1444 | } 1445 | ], 1446 | "source": [ 1447 | "len(model.constraints)" 1448 | ] 1449 | }, 1450 | { 1451 | "cell_type": "markdown", 1452 | "metadata": {}, 1453 | "source": [ 1454 | "### The stoichiometric matrix S" 1455 | ] 1456 | }, 1457 | { 1458 | "cell_type": "code", 1459 | "execution_count": 41, 1460 | "metadata": { 1461 | "collapsed": true 1462 | }, 1463 | "outputs": [], 1464 | "source": [ 1465 | "from cobra.util import create_stoichiometric_matrix" 1466 | ] 1467 | }, 1468 | { 1469 | "cell_type": "code", 1470 | "execution_count": 42, 1471 | "metadata": {}, 1472 | "outputs": [ 1473 | { 1474 | "data": { 1475 | "text/plain": [ 1476 | "array([[ 0., 0., 0., ..., 0., 0., 0.],\n", 1477 | " [ 0., 0., 0., ..., 0., 0., 0.],\n", 1478 | " [ 0., 0., 0., ..., 0., 0., 0.],\n", 1479 | " ..., \n", 1480 | " [ 0., 0., 0., ..., 0., 0., 0.],\n", 1481 | " [ 0., 0., 0., ..., 0., 0., 0.],\n", 1482 | " [ 0., 0., 0., ..., -1., -1., 1.]])" 1483 | ] 1484 | }, 1485 | "execution_count": 42, 1486 | "metadata": {}, 1487 | "output_type": "execute_result" 1488 | } 1489 | ], 1490 | "source": [ 1491 | "stoich_matrix = create_stoichiometric_matrix(model)\n", 1492 | "stoich_matrix" 1493 | ] 1494 | }, 1495 | { 1496 | "cell_type": "markdown", 1497 | "metadata": {}, 1498 | "source": [ 1499 | "This is how the the stoichiometry matrix S looks like when visualized as a matrix plot." 1500 | ] 1501 | }, 1502 | { 1503 | "cell_type": "code", 1504 | "execution_count": 43, 1505 | "metadata": { 1506 | "collapsed": true 1507 | }, 1508 | "outputs": [], 1509 | "source": [ 1510 | "%matplotlib inline" 1511 | ] 1512 | }, 1513 | { 1514 | "cell_type": "code", 1515 | "execution_count": 44, 1516 | "metadata": { 1517 | "collapsed": true 1518 | }, 1519 | "outputs": [], 1520 | "source": [ 1521 | "import matplotlib.pyplot as plt" 1522 | ] 1523 | }, 1524 | { 1525 | "cell_type": "code", 1526 | "execution_count": 45, 1527 | "metadata": {}, 1528 | "outputs": [ 1529 | { 1530 | "data": { 1531 | "text/plain": [ 1532 | "" 1533 | ] 1534 | }, 1535 | "execution_count": 45, 1536 | "metadata": {}, 1537 | "output_type": "execute_result" 1538 | }, 1539 | { 1540 | "data": { 1541 | "image/png": "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\n", 1542 | "text/plain": [ 1543 | "" 1544 | ] 1545 | }, 1546 | "metadata": {}, 1547 | "output_type": "display_data" 1548 | } 1549 | ], 1550 | "source": [ 1551 | "plt.spy(stoich_matrix, precision=0.01, markersize=.2)" 1552 | ] 1553 | }, 1554 | { 1555 | "cell_type": "markdown", 1556 | "metadata": {}, 1557 | "source": [ 1558 | "
\n", 1559 | "\n", 1560 | "**Question:** What are the diagonal and horizontal lines?\n", 1561 | "
\n" 1562 | ] 1563 | } 1564 | ], 1565 | "metadata": { 1566 | "anaconda-cloud": {}, 1567 | "kernelspec": { 1568 | "display_name": "Python 3", 1569 | "language": "python", 1570 | "name": "python3" 1571 | }, 1572 | "language_info": { 1573 | "codemirror_mode": { 1574 | "name": "ipython", 1575 | "version": 3 1576 | }, 1577 | "file_extension": ".py", 1578 | "mimetype": "text/x-python", 1579 | "name": "python", 1580 | "nbconvert_exporter": "python", 1581 | "pygments_lexer": "ipython3", 1582 | "version": "3.5.2+" 1583 | }, 1584 | "toc": { 1585 | "colors": { 1586 | "hover_highlight": "#DAA520", 1587 | "navigate_num": "#000000", 1588 | "navigate_text": "#333333", 1589 | "running_highlight": "#FF0000", 1590 | "selected_highlight": "#FFD700", 1591 | "sidebar_border": "#EEEEEE", 1592 | "wrapper_background": "#FFFFFF" 1593 | }, 1594 | "moveMenuLeft": true, 1595 | "nav_menu": { 1596 | "height": "284px", 1597 | "width": "252px" 1598 | }, 1599 | "navigate_menu": true, 1600 | "number_sections": false, 1601 | "sideBar": true, 1602 | "threshold": "3", 1603 | "toc_cell": false, 1604 | "toc_section_display": "block", 1605 | "toc_window_display": true, 1606 | "widenNotebook": false 1607 | } 1608 | }, 1609 | "nbformat": 4, 1610 | "nbformat_minor": 1 1611 | } 1612 | -------------------------------------------------------------------------------- /cobrapy-03-manipulating-the-model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Manipulating models" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Preparation" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "from cobra.io import read_sbml_model" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 2, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "model = read_sbml_model('data/e_coli_core.xml.gz')" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "## Making temporary changes to the model (5 + 2)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "Usually one relies on making copies if objects need to be changed but the original state needs to be retained and that's we what we did previously with the `model_copy = model.copy()`. Unfortunately, metabolic models can be enormous and copying those in memory can take quite long." 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 3, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "CPU times: user 16 ms, sys: 0 ns, total: 16 ms\n", 63 | "Wall time: 17 ms\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "%%time\n", 69 | "copy_of_model = model.copy()" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Yes, even milliseconds add up pretty quickly if you need to run many simulation (e.g. if you need to knock out every single gene individually in the model to check if it is essential or not)." 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 4, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "CPU times: user 2.19 s, sys: 64 ms, total: 2.26 s\n", 89 | "Wall time: 2.25 s\n" 90 | ] 91 | } 92 | ], 93 | "source": [ 94 | "%%time\n", 95 | "## avoid writing code like this...\n", 96 | "for gene in model.genes:\n", 97 | " mutant = model.copy()\n", 98 | " mutant.genes.get_by_id(gene.id).knock_out()" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "For that reason cobrapy provides a mechanism that is less time consuming. Almost all methods that make changes to the model such as knocking-out genes, reactions, adding or removing metabolites, reactions, etc. can be automatically reverted upon exit from a python context. How this works is probably best understood by looking at an example." 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 5, 111 | "metadata": {}, 112 | "outputs": [ 113 | { 114 | "name": "stdout", 115 | "output_type": "stream", 116 | "text": [ 117 | "CPU times: user 12 ms, sys: 0 ns, total: 12 ms\n", 118 | "Wall time: 11.9 ms\n" 119 | ] 120 | } 121 | ], 122 | "source": [ 123 | "%%time\n", 124 | "## instead do this; no copying!\n", 125 | "with model:\n", 126 | " for gene in model.genes:\n", 127 | " gene.knock_out()" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Here, the `with model` statement starts the context and changes done to the model one indentation level to the right, are automatically recorded. When that block finishes, the context manager is requested to roll-back all changes leaving the model looking exactly as it did before entering the context." 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "Changing flux bounds can, as indicated, also be done reversibly. For example let's set the lower and upper bound of phosphoglycerate kinase to 0 (effectively knocking out the reaction)." 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 6, 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "PGK's bounds inside the with statement\n", 154 | "0 (0, 0)\n", 155 | "Mutant growth rate: 0.0\n", 156 | "PGK's bounds outside the with statement\n", 157 | "(-1000.0, 1000.0)\n" 158 | ] 159 | } 160 | ], 161 | "source": [ 162 | "with model:\n", 163 | " model.reactions.PGK.bounds = 0, 0\n", 164 | " print(\"PGK's bounds inside the with statement\")\n", 165 | " print(model.reactions.PGK.lower_bound, model.reactions.PGK.bounds)\n", 166 | " print('Mutant growth rate: ', model.optimize().objective_value)\n", 167 | "print(\"PGK's bounds outside the with statement\")\n", 168 | "print(model.reactions.PGK.bounds)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "### Exercises" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "Convert the cell below to code and fill in the blanks" 183 | ] 184 | }, 185 | { 186 | "cell_type": "raw", 187 | "metadata": {}, 188 | "source": [ 189 | "# is the PGM reaction essential?\n", 190 | "____ ____:\n", 191 | " ____.____.PGM.___()\n", 192 | " ___ = ____.____\n", 193 | " print('growth without PGM is max', ____)\n", 194 | " \n", 195 | " " 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "## Changing the medium (2 + 3)" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "One can access the medium condition using `model.medium`. The indicated bound is the effective upper uptake bound. " 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 7, 215 | "metadata": {}, 216 | "outputs": [ 217 | { 218 | "data": { 219 | "text/plain": [ 220 | "{'EX_co2_e': 1000.0,\n", 221 | " 'EX_glc__D_e': 10.0,\n", 222 | " 'EX_h2o_e': 1000.0,\n", 223 | " 'EX_h_e': 1000.0,\n", 224 | " 'EX_nh4_e': 1000.0,\n", 225 | " 'EX_o2_e': 1000.0,\n", 226 | " 'EX_pi_e': 1000.0}" 227 | ] 228 | }, 229 | "execution_count": 7, 230 | "metadata": {}, 231 | "output_type": "execute_result" 232 | } 233 | ], 234 | "source": [ 235 | "model.medium" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "Changing the carbon source in the medium can be achieved by adjusting the flux bounds of the respective exchange reactions appropriately. For example, the following code block removes glucose from the medium and adds succinate." 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 8, 248 | "metadata": {}, 249 | "outputs": [ 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "0.397563015428\n" 255 | ] 256 | } 257 | ], 258 | "source": [ 259 | "medium = model.medium\n", 260 | "with model:\n", 261 | " medium['EX_glc__D_e'] = 0\n", 262 | " medium['EX_succ_e'] = 10\n", 263 | " model.medium = medium\n", 264 | " solution = model.optimize()\n", 265 | " print(solution.fluxes['BIOMASS_Ecoli_core_w_GAM'])" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "Changing the carbon source to succinate led to a significant drop in growth rate." 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "### Exercise" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "Convert the cell below to code and fill in the blanks" 287 | ] 288 | }, 289 | { 290 | "cell_type": "raw", 291 | "metadata": {}, 292 | "source": [ 293 | "# what is the max growth rate when growing on fumarate instead of glucose\n", 294 | "other_carbon_medium = model.medium\n", 295 | "other_carbon_medium['EX_glc__D_e'] = 0\n", 296 | "other_carbon_medium['EX_fum_e'] = 10\n", 297 | "____ ____:\n", 298 | " ____.____ = ____\n", 299 | " solution = ____.____()\n", 300 | "____" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "## Adding metabolites, reactions and pathways (10 + 10)" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 9, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "from cobra import Reaction, Metabolite" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "Ok, let's create a new reactions." 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 10, 331 | "metadata": { 332 | "collapsed": true 333 | }, 334 | "outputs": [], 335 | "source": [ 336 | "new_reaction = Reaction('alchemy')" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "This reaction is going to convert water into gold. First we need to create a new metabolite, since gold is not yet part of *E. coli's* native metabolism." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 11, 349 | "metadata": { 350 | "collapsed": true 351 | }, 352 | "outputs": [], 353 | "source": [ 354 | "gold = Metabolite(id='gold_c', compartment='c', name='GOLD')" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "Now, we're going to specify the reaction's stoichiometry." 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 12, 367 | "metadata": { 368 | "collapsed": true 369 | }, 370 | "outputs": [], 371 | "source": [ 372 | "new_reaction.add_metabolites({model.metabolites.h2o_c: -1, gold: 1})" 373 | ] 374 | }, 375 | { 376 | "cell_type": "markdown", 377 | "metadata": {}, 378 | "source": [ 379 | "Printing the reaction reveals that the reaction indeed converts water into gold." 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": 13, 385 | "metadata": {}, 386 | "outputs": [ 387 | { 388 | "name": "stdout", 389 | "output_type": "stream", 390 | "text": [ 391 | "h2o_c --> gold_c\n" 392 | ] 393 | } 394 | ], 395 | "source": [ 396 | "print(new_reaction.build_reaction_string())" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": {}, 402 | "source": [ 403 | "Now, let's add the new reaction to the model." 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 14, 409 | "metadata": { 410 | "collapsed": true 411 | }, 412 | "outputs": [], 413 | "source": [ 414 | "model.add_reactions([new_reaction])" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "Quickly check that the reaction was indeed added to the model." 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 15, 427 | "metadata": {}, 428 | "outputs": [ 429 | { 430 | "data": { 431 | "text/html": [ 432 | "\n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | "
Reaction identifieralchemy
Name
Memory address0x07f3d95b3f128
Stoichiometry\n", 444 | "

h2o_c --> gold_c

\n", 445 | "

H2O --> GOLD

\n", 446 | "
GPR
Lower bound0.0
Upper bound1000.0
\n", 455 | " " 456 | ], 457 | "text/plain": [ 458 | "" 459 | ] 460 | }, 461 | "execution_count": 15, 462 | "metadata": {}, 463 | "output_type": "execute_result" 464 | } 465 | ], 466 | "source": [ 467 | "model.reactions.alchemy" 468 | ] 469 | }, 470 | { 471 | "cell_type": "markdown", 472 | "metadata": {}, 473 | "source": [ 474 | "Let's produce some gold then!" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": 16, 480 | "metadata": {}, 481 | "outputs": [ 482 | { 483 | "data": { 484 | "text/plain": [ 485 | "0.0" 486 | ] 487 | }, 488 | "execution_count": 16, 489 | "metadata": {}, 490 | "output_type": "execute_result" 491 | } 492 | ], 493 | "source": [ 494 | "model.objective = model.reactions.alchemy\n", 495 | "model.optimize().objective_value" 496 | ] 497 | }, 498 | { 499 | "cell_type": "markdown", 500 | "metadata": {}, 501 | "source": [ 502 | ":-(\n", 503 | "\n", 504 | "What happened? Forgot to add an exchange reaction so that gold can leave the system." 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 17, 510 | "metadata": {}, 511 | "outputs": [ 512 | { 513 | "data": { 514 | "text/html": [ 515 | "\n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | "
Reaction identifierDM_gold_c
NameGOLD demand
Memory address0x07f3d95b28da0
Stoichiometry\n", 527 | "

gold_c -->

\n", 528 | "

GOLD -->

\n", 529 | "
GPR
Lower bound0
Upper bound1000.0
\n", 538 | " " 539 | ], 540 | "text/plain": [ 541 | "" 542 | ] 543 | }, 544 | "execution_count": 17, 545 | "metadata": {}, 546 | "output_type": "execute_result" 547 | } 548 | ], 549 | "source": [ 550 | "model.add_boundary(model.metabolites.gold_c, type='demand')" 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": 18, 556 | "metadata": {}, 557 | "outputs": [ 558 | { 559 | "data": { 560 | "text/plain": [ 561 | "1000.0" 562 | ] 563 | }, 564 | "execution_count": 18, 565 | "metadata": {}, 566 | "output_type": "execute_result" 567 | } 568 | ], 569 | "source": [ 570 | "model.objective = model.reactions.alchemy\n", 571 | "model.optimize().objective_value" 572 | ] 573 | }, 574 | { 575 | "cell_type": "markdown", 576 | "metadata": {}, 577 | "source": [ 578 | "Yes, much better!" 579 | ] 580 | }, 581 | { 582 | "cell_type": "markdown", 583 | "metadata": {}, 584 | "source": [ 585 | "### Exercise" 586 | ] 587 | }, 588 | { 589 | "cell_type": "markdown", 590 | "metadata": {}, 591 | "source": [ 592 | "\n", 593 | "Convert the cell below to code and fill in the blanks. Add a magic pathway that converts glucose directly to ATP. Does this lead to an increase in growth rate?" 594 | ] 595 | }, 596 | { 597 | "cell_type": "raw", 598 | "metadata": {}, 599 | "source": [ 600 | "model.objective = model.reactions.BIOMASS_Ecoli_core_w_GAM\n", 601 | "\n", 602 | "with model:\n", 603 | " ___ = ___('magic')\n", 604 | " magic.____({____: -1, ____: 1})\n", 605 | " ____\n", 606 | " ____" 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": {}, 612 | "source": [ 613 | "Time left? Add a pathway of your choice and optimize for flux through it!" 614 | ] 615 | } 616 | ], 617 | "metadata": { 618 | "anaconda-cloud": {}, 619 | "kernelspec": { 620 | "display_name": "Python 3", 621 | "language": "python", 622 | "name": "python3" 623 | }, 624 | "language_info": { 625 | "codemirror_mode": { 626 | "name": "ipython", 627 | "version": 3 628 | }, 629 | "file_extension": ".py", 630 | "mimetype": "text/x-python", 631 | "name": "python", 632 | "nbconvert_exporter": "python", 633 | "pygments_lexer": "ipython3", 634 | "version": "3.6.0" 635 | }, 636 | "toc": { 637 | "colors": { 638 | "hover_highlight": "#DAA520", 639 | "navigate_num": "#000000", 640 | "navigate_text": "#333333", 641 | "running_highlight": "#FF0000", 642 | "selected_highlight": "#FFD700", 643 | "sidebar_border": "#EEEEEE", 644 | "wrapper_background": "#FFFFFF" 645 | }, 646 | "moveMenuLeft": true, 647 | "nav_menu": { 648 | "height": "173px", 649 | "width": "252px" 650 | }, 651 | "navigate_menu": true, 652 | "number_sections": false, 653 | "sideBar": true, 654 | "threshold": "3", 655 | "toc_cell": false, 656 | "toc_section_display": "block", 657 | "toc_window_display": true, 658 | "widenNotebook": false 659 | } 660 | }, 661 | "nbformat": 4, 662 | "nbformat_minor": 1 663 | } 664 | -------------------------------------------------------------------------------- /cobrapy-05-advanced-constraints-objectives.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Tailored constraints, variables and objectives" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Thanks to the use of symbolic expressions via the optlang mathematical modeling package, it is relatively straight-forward to add new variables, constraints and advanced objectives that can not easily be formulated as a combination of different reactions and their corresponding upper and lower bounds. Here we demonstrate this optlang functionality which is exposed via `model.problem`." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Constraints" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "Suppose we want to ensure that two reactions have the same flux in our model. We can add this criteria as constraint to our model using the optlang solver interface by simply defining the relevant expression as follows." 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": { 35 | "collapsed": true 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "from cobra.io import read_sbml_model" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": { 46 | "collapsed": true 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "model = read_sbml_model('data/e_coli_core.xml.gz')" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 4, 56 | "metadata": { 57 | "collapsed": true 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "same_flux = model.problem.Constraint(\n", 62 | " model.reactions.FBA.flux_expression - model.reactions.NH4t.flux_expression,\n", 63 | " lb=0,\n", 64 | " ub=0)\n", 65 | "model.add_cons_vars(same_flux)" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "The flux for our reaction of interest is obtained by the `model.reactions.FBA.flux_expression` which is simply the sum of the forward and reverse flux, i.e.," 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 5, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "data": { 82 | "text/plain": [ 83 | "1.0*FBA - 1.0*FBA_reverse_84806" 84 | ] 85 | }, 86 | "execution_count": 5, 87 | "metadata": {}, 88 | "output_type": "execute_result" 89 | } 90 | ], 91 | "source": [ 92 | "model.reactions.FBA.flux_expression" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "Now I can maximize growth rate whilst the fluxes of reactions 'FBA' and 'NH4t' are constrained to be (near) identical." 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 6, 105 | "metadata": {}, 106 | "outputs": [ 107 | { 108 | "name": "stdout", 109 | "output_type": "stream", 110 | "text": [ 111 | "4.66274904774 4.66274904774 0.8551109609261567\n" 112 | ] 113 | } 114 | ], 115 | "source": [ 116 | "solution = model.optimize()\n", 117 | "print(solution.fluxes.FBA, solution.fluxes.NH4t,\n", 118 | " solution.objective_value)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "## Objectives\n", 126 | "\n", 127 | "\n" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Simple objective such as the maximization of the flux through one or more reactions can conveniently be done by simply \n", 135 | "assigning to the `model.objective` property as we have seen in previous chapters, e.g.," 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 7, 141 | "metadata": { 142 | "collapsed": true 143 | }, 144 | "outputs": [], 145 | "source": [ 146 | "model = read_sbml_model('data/e_coli_core.xml.gz')" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 8, 152 | "metadata": {}, 153 | "outputs": [ 154 | { 155 | "name": "stdout", 156 | "output_type": "stream", 157 | "text": [ 158 | "0.8739215069684307\n" 159 | ] 160 | } 161 | ], 162 | "source": [ 163 | "with model:\n", 164 | " model.objective = {model.reactions.BIOMASS_Ecoli_core_w_GAM: 1}\n", 165 | " model.optimize()\n", 166 | " print(model.reactions.BIOMASS_Ecoli_core_w_GAM.flux)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "The objectives mathematical expression is seen by" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 9, 179 | "metadata": {}, 180 | "outputs": [ 181 | { 182 | "data": { 183 | "text/plain": [ 184 | "-1.0*BIOMASS_Ecoli_core_w_GAM_reverse_712e5 + 1.0*BIOMASS_Ecoli_core_w_GAM" 185 | ] 186 | }, 187 | "execution_count": 9, 188 | "metadata": {}, 189 | "output_type": "execute_result" 190 | } 191 | ], 192 | "source": [ 193 | "model.objective.expression" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "But suppose we need a more complicated objective, such as minimizing the Euclidean distance of the solution to the origin minus another variable, while subject to additional linear constraints. This is an objective function with both linear and quadratic components. " 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "Consider the example problem:\n", 208 | "\n", 209 | "> **min** $\\frac{1}{2}\\left(x^2 + y^2 \\right) - y$\n", 210 | "\n", 211 | "> *subject to*\n", 212 | "\n", 213 | "> $x + y = 2$\n", 214 | "\n", 215 | "> $x \\ge 0$\n", 216 | "\n", 217 | "> $y \\ge 0$\n", 218 | "\n", 219 | "This (admittedly very artificial) problem can be visualized graphically where the optimum is indicated by the blue dot on the line of feasible solutions." 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "We return to the textbook model and set the solver to one that can handle quadratic objectives such as cplex. We then add the linear constraint that the sum of our x and y reactions, that we set to FBA and NH4t, must equal 2." 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 10, 232 | "metadata": { 233 | "collapsed": true 234 | }, 235 | "outputs": [], 236 | "source": [ 237 | "model.solver = 'cplex'" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 11, 243 | "metadata": { 244 | "collapsed": true 245 | }, 246 | "outputs": [], 247 | "source": [ 248 | "sum_two = model.problem.Constraint(\n", 249 | " model.reactions.FBA.flux_expression + model.reactions.NH4t.flux_expression,\n", 250 | " lb=2,\n", 251 | " ub=2)\n", 252 | "model.add_cons_vars(sum_two)" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "Next we add the quadratic objective" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 12, 265 | "metadata": { 266 | "collapsed": true 267 | }, 268 | "outputs": [], 269 | "source": [ 270 | "quadratic_objective = model.problem.Objective(\n", 271 | " 0.5 * model.reactions.NH4t.flux_expression**2 + 0.5 *\n", 272 | " model.reactions.FBA.flux_expression**2 -\n", 273 | " model.reactions.FBA.flux_expression,\n", 274 | " direction='min')\n", 275 | "model.objective = quadratic_objective\n", 276 | "solution = model.optimize(objective_sense=None)" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 13, 282 | "metadata": {}, 283 | "outputs": [ 284 | { 285 | "name": "stdout", 286 | "output_type": "stream", 287 | "text": [ 288 | "0.5 1.5\n" 289 | ] 290 | } 291 | ], 292 | "source": [ 293 | "print(solution.fluxes.NH4t, solution.fluxes.FBA)" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "## Variables" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "We can also create additional variables to facilitate studying the effects of new constraints and variables. Suppose we want to study the difference in flux between nitrogen and carbon uptake whilst we block other reactions. For this it will may help to add another variable representing this difference." 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 14, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "model = read_sbml_model('data/e_coli_core.xml.gz')" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 15, 324 | "metadata": { 325 | "collapsed": true 326 | }, 327 | "outputs": [], 328 | "source": [ 329 | "difference = model.problem.Variable('difference')" 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": {}, 335 | "source": [ 336 | "We use constraints to define what values this variable shall take" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 16, 342 | "metadata": { 343 | "collapsed": true 344 | }, 345 | "outputs": [], 346 | "source": [ 347 | "constraint = model.problem.Constraint(\n", 348 | " model.reactions.EX_glc__D_e.flux_expression -\n", 349 | " model.reactions.EX_nh4_e.flux_expression - difference,\n", 350 | " lb=0,\n", 351 | " ub=0)\n", 352 | "model.add_cons_vars([difference, constraint])" 353 | ] 354 | }, 355 | { 356 | "cell_type": "markdown", 357 | "metadata": {}, 358 | "source": [ 359 | "Now we can access that difference directly during our knock-out exploration by looking at its primal value." 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": 17, 365 | "metadata": {}, 366 | "outputs": [ 367 | { 368 | "name": "stdout", 369 | "output_type": "stream", 370 | "text": [ 371 | "-5.234680806802543\n", 372 | "-5.234680806802543\n", 373 | "-5.234680806802543\n", 374 | "-1.8644444444444437\n", 375 | "-1.8644444444444437\n" 376 | ] 377 | } 378 | ], 379 | "source": [ 380 | "for reaction in model.reactions[:5]:\n", 381 | " with model:\n", 382 | " reaction.knock_out()\n", 383 | " model.optimize()\n", 384 | " print(model.solver.variables.difference.primal)" 385 | ] 386 | } 387 | ], 388 | "metadata": { 389 | "kernelspec": { 390 | "display_name": "Python 3", 391 | "language": "python", 392 | "name": "python3" 393 | }, 394 | "language_info": { 395 | "codemirror_mode": { 396 | "name": "ipython", 397 | "version": 3 398 | }, 399 | "file_extension": ".py", 400 | "mimetype": "text/x-python", 401 | "name": "python", 402 | "nbconvert_exporter": "python", 403 | "pygments_lexer": "ipython3", 404 | "version": "3.6.0" 405 | }, 406 | "toc": { 407 | "colors": { 408 | "hover_highlight": "#DAA520", 409 | "navigate_num": "#000000", 410 | "navigate_text": "#333333", 411 | "running_highlight": "#FF0000", 412 | "selected_highlight": "#FFD700", 413 | "sidebar_border": "#EEEEEE", 414 | "wrapper_background": "#FFFFFF" 415 | }, 416 | "moveMenuLeft": true, 417 | "nav_menu": { 418 | "height": "98px", 419 | "width": "252px" 420 | }, 421 | "navigate_menu": true, 422 | "number_sections": false, 423 | "sideBar": true, 424 | "threshold": "3", 425 | "toc_cell": false, 426 | "toc_section_display": "block", 427 | "toc_window_display": true, 428 | "widenNotebook": false 429 | } 430 | }, 431 | "nbformat": 4, 432 | "nbformat_minor": 1 433 | } 434 | -------------------------------------------------------------------------------- /data/S4_McCloskey2013_aerobic_metabolomics.csv: -------------------------------------------------------------------------------- 1 | akg_c,5.35E-02 3mob_c,1.96E-01 prpp_c,1.66E-01 6pgl_c,5.26E-02 accoa_c,3.40E-01 adp_c,4.83E-02 r5p_c,2.55E-02 amp_c,7.35E-03 atp_c,4.82E-01 cdp_c,1.58E-02 cmp_c,1.39E-03 coa_c,3.64E-01 ctp_c,6.25E-02 datp_c,4.44E-02 dctp_c,2.41E-02 fdp_c,4.69E+00 f1p_c,2.42E-01 f6p_c,3.62E-01 dgdp_c,4.31E-03 gam6p_c,5.56E-02 g6p_c,8.50E-01 ru5p__D_c,6.71E-02 dtdpglu_c,1.65E-02 dttp_c,3.40E-02 dump_c,2.43E-05 gdp_c,4.50E-02 gtp_c,1.19E-01 imp_c,1.21E-02 itp_c,1.17E-02 asp__L_c,1.46E-01 citr__L_c,1.38E-02 glu__L_c,6.69E+00 gln__L_c,1.33E+00 phe__L_c,1.76E-02 ser__L_c,1.23E-01 thr__L_c,3.96E-01 trp__L_c,1.46E-02 nadh_c,2.07E-01 nad_c,5.84E-01 nadph_c,3.75E-01 nadp_c,4.29E-02 gthox_c,4.06E-01 pep_c,1.26E-01 gthrd_c,1.25E+00 udp_c,1.43E-02 ump_c,2.64E-02 utp_c,1.30E-01 -------------------------------------------------------------------------------- /data/custom_map.json: -------------------------------------------------------------------------------- 1 | [{"map_name":"new_map","map_id":"vc7omDuBLHsV","map_description":"","homepage":"https://escher.github.io","schema":"https://escher.github.io/escher/jsonschema/1-0-0#"},{"reactions":{"2076052":{"name":"Glyceraldehyde-3-phosphate dehydrogenase","bigg_id":"GAPD","reversibility":true,"label_x":2614.5947265625,"label_y":2255.984375,"gene_reaction_rule":"b1779","genes":[{"notes":{"original_bigg_ids":["b1779"]},"name":"gapA","bigg_id":"b1779"}],"metabolites":[{"bigg_id":"pi_c","coefficient":-1},{"bigg_id":"13dpg_c","coefficient":1},{"bigg_id":"g3p_c","coefficient":-1},{"bigg_id":"h_c","coefficient":1},{"bigg_id":"nad_c","coefficient":-1},{"bigg_id":"nadh_c","coefficient":1}],"segments":{"1169":{"from_node_id":"2076587","to_node_id":"2076588","b1":null,"b2":null},"1170":{"from_node_id":"2076589","to_node_id":"2076588","b1":null,"b2":null},"1171":{"from_node_id":"2076587","to_node_id":"2076586","b1":{"x":2599.5947265625,"y":2183.984375},"b2":{"x":2599.5947265625,"y":2129.734375}},"1172":{"from_node_id":"2076589","to_node_id":"2076590","b1":{"x":2599.5947265625,"y":2307.984375},"b2":{"x":2599.5947265625,"y":2362.234375}},"1173":{"from_node_id":"2076587","to_node_id":"2076591","b1":{"x":2599.5947265625,"y":2179.984375},"b2":{"x":2617.5947265625,"y":2174.734375}},"1174":{"from_node_id":"2076589","to_node_id":"2076592","b1":{"x":2599.5947265625,"y":2311.984375},"b2":{"x":2617.5947265625,"y":2317.234375}},"1175":{"from_node_id":"2076587","to_node_id":"2076593","b1":{"x":2599.5947265625,"y":2179.984375},"b2":{"x":2581.5947265625,"y":2174.734375}},"1176":{"from_node_id":"2076589","to_node_id":"2076594","b1":{"x":2599.5947265625,"y":2311.984375},"b2":{"x":2581.5947265625,"y":2317.234375}}}},"2076053":{"name":"Phosphoglycerate kinase","bigg_id":"PGK","reversibility":true,"label_x":2614.5947265625,"label_y":2605.984375,"gene_reaction_rule":"b2926","genes":[{"notes":{"original_bigg_ids":["b2926"]},"name":"pgk","bigg_id":"b2926"}],"metabolites":[{"bigg_id":"atp_c","coefficient":-1},{"bigg_id":"adp_c","coefficient":1},{"bigg_id":"13dpg_c","coefficient":1},{"bigg_id":"3pg_c","coefficient":-1}],"segments":{"1177":{"from_node_id":"2076595","to_node_id":"2076596","b1":null,"b2":null},"1178":{"from_node_id":"2076597","to_node_id":"2076596","b1":null,"b2":null},"1179":{"from_node_id":"2076595","to_node_id":"2076598","b1":{"x":2599.5947265625,"y":2661.984375},"b2":{"x":2581.5947265625,"y":2667.234375}},"1180":{"from_node_id":"2076597","to_node_id":"2076599","b1":{"x":2599.5947265625,"y":2529.984375},"b2":{"x":2581.5947265625,"y":2524.734375}},"1181":{"from_node_id":"2076597","to_node_id":"2076590","b1":{"x":2599.5947265625,"y":2533.984375},"b2":{"x":2599.5947265625,"y":2479.734375}},"1182":{"from_node_id":"2076595","to_node_id":"2076600","b1":{"x":2599.5947265625,"y":2657.984375},"b2":{"x":2599.5947265625,"y":2712.234375}}}},"2076054":{"name":"Glycerate kinase","bigg_id":"GLYCK","reversibility":false,"label_x":2614.5947265625,"label_y":2955.984375,"gene_reaction_rule":"b0514","genes":[{"notes":{"original_bigg_ids":["b0514"]},"name":"glxK","bigg_id":"b0514"}],"metabolites":[{"bigg_id":"h_c","coefficient":1},{"bigg_id":"glyc__R_c","coefficient":-1},{"bigg_id":"atp_c","coefficient":-1},{"bigg_id":"adp_c","coefficient":1},{"bigg_id":"3pg_c","coefficient":1}],"segments":{"1183":{"from_node_id":"2076601","to_node_id":"2076602","b1":null,"b2":null},"1184":{"from_node_id":"2076603","to_node_id":"2076602","b1":null,"b2":null},"1185":{"from_node_id":"2076603","to_node_id":"2076604","b1":{"x":2599.5947265625,"y":2879.984375},"b2":{"x":2617.5947265625,"y":2874.734375}},"1186":{"from_node_id":"2076601","to_node_id":"2076605","b1":{"x":2599.5947265625,"y":3007.984375},"b2":{"x":2599.5947265625,"y":3062.234375}},"1187":{"from_node_id":"2076601","to_node_id":"2076606","b1":{"x":2599.5947265625,"y":3011.984375},"b2":{"x":2581.5947265625,"y":3017.234375}},"1188":{"from_node_id":"2076603","to_node_id":"2076607","b1":{"x":2599.5947265625,"y":2879.984375},"b2":{"x":2581.5947265625,"y":2874.734375}},"1189":{"from_node_id":"2076603","to_node_id":"2076600","b1":{"x":2599.5947265625,"y":2883.984375},"b2":{"x":2599.5947265625,"y":2829.734375}}}}},"nodes":{"2076586":{"node_type":"metabolite","x":2599.5947265625,"y":2070.984375,"bigg_id":"pi_c","name":"Phosphate","label_x":2624.5947265625,"label_y":2070.984375,"node_is_primary":true},"2076587":{"node_type":"multimarker","x":2599.5947265625,"y":2225.984375},"2076588":{"node_type":"midmarker","x":2599.5947265625,"y":2245.984375},"2076589":{"node_type":"multimarker","x":2599.5947265625,"y":2265.984375},"2076590":{"node_type":"metabolite","x":2599.5947265625,"y":2420.984375,"bigg_id":"13dpg_c","name":"3-Phospho-D-glyceroyl phosphate","label_x":2624.5947265625,"label_y":2420.984375,"node_is_primary":true},"2076591":{"node_type":"metabolite","x":2679.5947265625,"y":2140.984375,"bigg_id":"g3p_c","name":"Glyceraldehyde 3-phosphate","label_x":2694.5947265625,"label_y":2140.984375,"node_is_primary":false},"2076592":{"node_type":"metabolite","x":2679.5947265625,"y":2350.984375,"bigg_id":"h_c","name":"H+","label_x":2694.5947265625,"label_y":2350.984375,"node_is_primary":false},"2076593":{"node_type":"metabolite","x":2519.5947265625,"y":2140.984375,"bigg_id":"nad_c","name":"Nicotinamide adenine dinucleotide","label_x":2474.5947265625,"label_y":2170.984375,"node_is_primary":false},"2076594":{"node_type":"metabolite","x":2519.5947265625,"y":2350.984375,"bigg_id":"nadh_c","name":"Nicotinamide adenine dinucleotide - reduced","label_x":2465.5947265625,"label_y":2380.984375,"node_is_primary":false},"2076595":{"node_type":"multimarker","x":2599.5947265625,"y":2615.984375},"2076596":{"node_type":"midmarker","x":2599.5947265625,"y":2595.984375},"2076597":{"node_type":"multimarker","x":2599.5947265625,"y":2575.984375},"2076598":{"node_type":"metabolite","x":2519.5947265625,"y":2700.984375,"bigg_id":"atp_c","name":"ATP","label_x":2474.5947265625,"label_y":2730.984375,"node_is_primary":false},"2076599":{"node_type":"metabolite","x":2519.5947265625,"y":2490.984375,"bigg_id":"adp_c","name":"ADP","label_x":2474.5947265625,"label_y":2520.984375,"node_is_primary":false},"2076600":{"node_type":"metabolite","x":2599.5947265625,"y":2770.984375,"bigg_id":"3pg_c","name":"3-Phospho-D-glycerate","label_x":2624.5947265625,"label_y":2770.984375,"node_is_primary":true},"2076601":{"node_type":"multimarker","x":2599.5947265625,"y":2965.984375},"2076602":{"node_type":"midmarker","x":2599.5947265625,"y":2945.984375},"2076603":{"node_type":"multimarker","x":2599.5947265625,"y":2925.984375},"2076604":{"node_type":"metabolite","x":2679.5947265625,"y":2840.984375,"bigg_id":"h_c","name":"H+","label_x":2694.5947265625,"label_y":2840.984375,"node_is_primary":false},"2076605":{"node_type":"metabolite","x":2599.5947265625,"y":3120.984375,"bigg_id":"glyc__R_c","name":"(R)-Glycerate","label_x":2624.5947265625,"label_y":3120.984375,"node_is_primary":true},"2076606":{"node_type":"metabolite","x":2519.5947265625,"y":3050.984375,"bigg_id":"atp_c","name":"ATP","label_x":2474.5947265625,"label_y":3080.984375,"node_is_primary":false},"2076607":{"node_type":"metabolite","x":2519.5947265625,"y":2840.984375,"bigg_id":"adp_c","name":"ADP","label_x":2474.5947265625,"label_y":2870.984375,"node_is_primary":false}},"text_labels":{"2076603":{"x":2970.3046875,"y":2535.755126953125,"text":"This is only a test"}},"canvas":{"x":2332.3119349921876,"y":1964.2155871401367,"width":1216.2481445078115,"height":1306.0474096723626}}] -------------------------------------------------------------------------------- /data/e_coli_core.xml.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DD-DeCaF/tutorials/b2136fa86912575d90e16f27d806a9c6b8d0bfb9/data/e_coli_core.xml.gz -------------------------------------------------------------------------------- /data/iJO1366.xml.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DD-DeCaF/tutorials/b2136fa86912575d90e16f27d806a9c6b8d0bfb9/data/iJO1366.xml.gz -------------------------------------------------------------------------------- /data/iMM904.xml.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DD-DeCaF/tutorials/b2136fa86912575d90e16f27d806a9c6b8d0bfb9/data/iMM904.xml.gz -------------------------------------------------------------------------------- /data/iris.csv: -------------------------------------------------------------------------------- 1 | sepal_length,sepal_width,petal_length,petal_width,species 2 | 5.1,3.5,1.4,0.2,setosa 3 | 4.9,3,1.4,0.2,setosa 4 | 4.7,3.2,1.3,0.2,setosa 5 | 4.6,3.1,1.5,0.2,setosa 6 | 5,3.6,1.4,0.2,setosa 7 | 5.4,3.9,1.7,0.4,setosa 8 | 4.6,3.4,1.4,0.3,setosa 9 | 5,3.4,1.5,0.2,setosa 10 | 4.4,2.9,1.4,0.2,setosa 11 | 4.9,3.1,1.5,0.1,setosa 12 | 5.4,3.7,1.5,0.2,setosa 13 | 4.8,3.4,1.6,0.2,setosa 14 | 4.8,3,1.4,0.1,setosa 15 | 4.3,3,1.1,0.1,setosa 16 | 5.8,4,1.2,0.2,setosa 17 | 5.7,4.4,1.5,0.4,setosa 18 | 5.4,3.9,1.3,0.4,setosa 19 | 5.1,3.5,1.4,0.3,setosa 20 | 5.7,3.8,1.7,0.3,setosa 21 | 5.1,3.8,1.5,0.3,setosa 22 | 5.4,3.4,1.7,0.2,setosa 23 | 5.1,3.7,1.5,0.4,setosa 24 | 4.6,3.6,1,0.2,setosa 25 | 5.1,3.3,1.7,0.5,setosa 26 | 4.8,3.4,1.9,0.2,setosa 27 | 5,3,1.6,0.2,setosa 28 | 5,3.4,1.6,0.4,setosa 29 | 5.2,3.5,1.5,0.2,setosa 30 | 5.2,3.4,1.4,0.2,setosa 31 | 4.7,3.2,1.6,0.2,setosa 32 | 4.8,3.1,1.6,0.2,setosa 33 | 5.4,3.4,1.5,0.4,setosa 34 | 5.2,4.1,1.5,0.1,setosa 35 | 5.5,4.2,1.4,0.2,setosa 36 | 4.9,3.1,1.5,0.1,setosa 37 | 5,3.2,1.2,0.2,setosa 38 | 5.5,3.5,1.3,0.2,setosa 39 | 4.9,3.1,1.5,0.1,setosa 40 | 4.4,3,1.3,0.2,setosa 41 | 5.1,3.4,1.5,0.2,setosa 42 | 5,3.5,1.3,0.3,setosa 43 | 4.5,2.3,1.3,0.3,setosa 44 | 4.4,3.2,1.3,0.2,setosa 45 | 5,3.5,1.6,0.6,setosa 46 | 5.1,3.8,1.9,0.4,setosa 47 | 4.8,3,1.4,0.3,setosa 48 | 5.1,3.8,1.6,0.2,setosa 49 | 4.6,3.2,1.4,0.2,setosa 50 | 5.3,3.7,1.5,0.2,setosa 51 | 5,3.3,1.4,0.2,setosa 52 | 7,3.2,4.7,1.4,versicolor 53 | 6.4,3.2,4.5,1.5,versicolor 54 | 6.9,3.1,4.9,1.5,versicolor 55 | 5.5,2.3,4,1.3,versicolor 56 | 6.5,2.8,4.6,1.5,versicolor 57 | 5.7,2.8,4.5,1.3,versicolor 58 | 6.3,3.3,4.7,1.6,versicolor 59 | 4.9,2.4,3.3,1,versicolor 60 | 6.6,2.9,4.6,1.3,versicolor 61 | 5.2,2.7,3.9,1.4,versicolor 62 | 5,2,3.5,1,versicolor 63 | 5.9,3,4.2,1.5,versicolor 64 | 6,2.2,4,1,versicolor 65 | 6.1,2.9,4.7,1.4,versicolor 66 | 5.6,2.9,3.6,1.3,versicolor 67 | 6.7,3.1,4.4,1.4,versicolor 68 | 5.6,3,4.5,1.5,versicolor 69 | 5.8,2.7,4.1,1,versicolor 70 | 6.2,2.2,4.5,1.5,versicolor 71 | 5.6,2.5,3.9,1.1,versicolor 72 | 5.9,3.2,4.8,1.8,versicolor 73 | 6.1,2.8,4,1.3,versicolor 74 | 6.3,2.5,4.9,1.5,versicolor 75 | 6.1,2.8,4.7,1.2,versicolor 76 | 6.4,2.9,4.3,1.3,versicolor 77 | 6.6,3,4.4,1.4,versicolor 78 | 6.8,2.8,4.8,1.4,versicolor 79 | 6.7,3,5,1.7,versicolor 80 | 6,2.9,4.5,1.5,versicolor 81 | 5.7,2.6,3.5,1,versicolor 82 | 5.5,2.4,3.8,1.1,versicolor 83 | 5.5,2.4,3.7,1,versicolor 84 | 5.8,2.7,3.9,1.2,versicolor 85 | 6,2.7,5.1,1.6,versicolor 86 | 5.4,3,4.5,1.5,versicolor 87 | 6,3.4,4.5,1.6,versicolor 88 | 6.7,3.1,4.7,1.5,versicolor 89 | 6.3,2.3,4.4,1.3,versicolor 90 | 5.6,3,4.1,1.3,versicolor 91 | 5.5,2.5,4,1.3,versicolor 92 | 5.5,2.6,4.4,1.2,versicolor 93 | 6.1,3,4.6,1.4,versicolor 94 | 5.8,2.6,4,1.2,versicolor 95 | 5,2.3,3.3,1,versicolor 96 | 5.6,2.7,4.2,1.3,versicolor 97 | 5.7,3,4.2,1.2,versicolor 98 | 5.7,2.9,4.2,1.3,versicolor 99 | 6.2,2.9,4.3,1.3,versicolor 100 | 5.1,2.5,3,1.1,versicolor 101 | 5.7,2.8,4.1,1.3,versicolor 102 | 6.3,3.3,6,2.5,virginica 103 | 5.8,2.7,5.1,1.9,virginica 104 | 7.1,3,5.9,2.1,virginica 105 | 6.3,2.9,5.6,1.8,virginica 106 | 6.5,3,5.8,2.2,virginica 107 | 7.6,3,6.6,2.1,virginica 108 | 4.9,2.5,4.5,1.7,virginica 109 | 7.3,2.9,6.3,1.8,virginica 110 | 6.7,2.5,5.8,1.8,virginica 111 | 7.2,3.6,6.1,2.5,virginica 112 | 6.5,3.2,5.1,2,virginica 113 | 6.4,2.7,5.3,1.9,virginica 114 | 6.8,3,5.5,2.1,virginica 115 | 5.7,2.5,5,2,virginica 116 | 5.8,2.8,5.1,2.4,virginica 117 | 6.4,3.2,5.3,2.3,virginica 118 | 6.5,3,5.5,1.8,virginica 119 | 7.7,3.8,6.7,2.2,virginica 120 | 7.7,2.6,6.9,2.3,virginica 121 | 6,2.2,5,1.5,virginica 122 | 6.9,3.2,5.7,2.3,virginica 123 | 5.6,2.8,4.9,2,virginica 124 | 7.7,2.8,6.7,2,virginica 125 | 6.3,2.7,4.9,1.8,virginica 126 | 6.7,3.3,5.7,2.1,virginica 127 | 7.2,3.2,6,1.8,virginica 128 | 6.2,2.8,4.8,1.8,virginica 129 | 6.1,3,4.9,1.8,virginica 130 | 6.4,2.8,5.6,2.1,virginica 131 | 7.2,3,5.8,1.6,virginica 132 | 7.4,2.8,6.1,1.9,virginica 133 | 7.9,3.8,6.4,2,virginica 134 | 6.4,2.8,5.6,2.2,virginica 135 | 6.3,2.8,5.1,1.5,virginica 136 | 6.1,2.6,5.6,1.4,virginica 137 | 7.7,3,6.1,2.3,virginica 138 | 6.3,3.4,5.6,2.4,virginica 139 | 6.4,3.1,5.5,1.8,virginica 140 | 6,3,4.8,1.8,virginica 141 | 6.9,3.1,5.4,2.1,virginica 142 | 6.7,3.1,5.6,2.4,virginica 143 | 6.9,3.1,5.1,2.3,virginica 144 | 5.8,2.7,5.1,1.9,virginica 145 | 6.8,3.2,5.9,2.3,virginica 146 | 6.7,3.3,5.7,2.5,virginica 147 | 6.7,3,5.2,2.3,virginica 148 | 6.3,2.5,5,1.9,virginica 149 | 6.5,3,5.2,2,virginica 150 | 6.2,3.4,5.4,2.3,virginica 151 | 5.9,3,5.1,1.8,virginica 152 | -------------------------------------------------------------------------------- /escher-01.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# What is Escher?\n", 8 | "\n", 9 | "Escher is a web-based tool for building, viewing, and sharing visualizations of biological pathways. These 'pathway maps' are a great way to contextualize data about metabolism, including the predictions made with COBRA and Cameo.\n", 10 | "\n", 11 | "### Links\n", 12 | "\n", 13 | "- Homepage: http://escher.github.io\n", 14 | "- Documentation: https://escher.readthedocs.org\n", 15 | "- Publication: https://doi.org/10.1371/journal.pcbi.1004321\n", 16 | "\n", 17 | "# Try the Escher application (10 min)\n", 18 | "\n", 19 | "Escher is a web application, and you can use it by visiting the [homepage](http://escher.github.io) from any browser.\n", 20 | "\n", 21 | "Try it now. Visit the homepage and click Load Map to see an example of an interactive Escher map. Try:\n", 22 | "\n", 23 | "- Load the map called \"Central metabolism (iJO1366)\" for _Escherichia coli_\n", 24 | "- Zoom and pan on the map. Drag the map to pan. Use the + and - buttons on the left to zoom (or the + and - keys).\n", 25 | "- Search by selecting **Find** from the **View** menu at the top. You can search by reaction, metabolite, or gene.\n", 26 | "- Save the map as a JSON file in the **Map** menu. This file represents the current Escher map, and you can load it in later.\n", 27 | "- Save the map as SVG in the **Map** menu. This saves an image of the map that is great for generating figures with application like Adobe Illustrator and Inkscape.\n", 28 | "\n", 29 | "In Escher, you can also edit existing pathways, draw new pathways, and save the map in a number of formats. You will find more detail in the [docs](https://escher.readthedocs.org). Try some of these features out if you have time." 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "# Escher for Python (20 min)\n", 37 | "\n", 38 | "We also provide a Python package to create Escher visualizations from Python code and embed them in Jupyter notebooks. This is especially useful if you are making predictions or analyzing data in Python, and you want a quick visualization.\n", 39 | "\n", 40 | "In this notebook, we'll go through the key features of Escher that can be used to visualize predicted fluxes from COBRA.\n", 41 | "\n", 42 | "To get started, import escher, cobra, and cameo." 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": { 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "import escher\n", 54 | "import cobra\n", 55 | "import cameo" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Finding maps\n", 63 | "\n", 64 | "The maps on the Escher website are also available from Python. You can list them like this." 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "escher.list_available_maps()" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "## Launch the builder\n", 81 | "\n", 82 | "Use the `Builder` class to create a new Escher map. You can pass a `map_name` from `list_available_maps` for a pre-built map. Then display it in the notebook.\n", 83 | "\n", 84 | "This map will let you pan and zoom, but editing features are not enabled." 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "b = escher.Builder(map_name='e_coli_core.Core metabolism')\n", 94 | "b.display_in_notebook()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## Plot FBA solutions in Escher\n", 102 | "\n", 103 | "Flux predictions are easy to plot on the map. Let's first generate a flux vector in COBRApy." 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": { 110 | "scrolled": false 111 | }, 112 | "outputs": [], 113 | "source": [ 114 | "model = cobra.io.read_sbml_model('data/e_coli_core.xml.gz')\n", 115 | "solution = model.optimize()\n", 116 | "print('Growth rate: %.2f' % solution.objective_value)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "Now these we have a solution, let's visualize it on the map. To improve the visual style, you can adjust many options of the Builder. Some of the most useful ones are:\n", 124 | "\n", 125 | "- reaction_scale: Pass a color & size scale as a list of points. You can set points for 'min', 'mean', 'max', 'median', 'Q1', 'Q3', and 'value'. These are the same options available in the Settings menu on the Escher website.\n", 126 | "- hide_secondary_metabolite: Simplify the map by hiding cofactors.\n", 127 | "- reaction_styles: Pass an array with any or 'size' to size reactions, 'color' to color them, 'text' to include data in their text labels, and 'abs' to visualize the absolute value of your data\n", 128 | "\n", 129 | "Similar settings are available for metabolite data, and they are all described in the [docs](https://escher.readthedocs.io/en/stable/python_api.html)." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "b = escher.Builder(map_name='e_coli_core.Core metabolism',\n", 139 | " reaction_data=dict(solution.fluxes),\n", 140 | " # change the default colors\n", 141 | " reaction_scale=[{'type': 'min', 'color': '#cccccc', 'size': 4},\n", 142 | " {'type': 'value', 'value': 0.1, 'color': '#cccccc', 'size': 8},\n", 143 | " {'type': 'mean', 'color': '#0000dd', 'size': 20},\n", 144 | " {'type': 'max', 'color': '#ff0000', 'size': 40}],\n", 145 | " # absolute value and no text for data\n", 146 | " reaction_styles=['size', 'color', 'abs'],\n", 147 | " # only show the primary metabolites\n", 148 | " hide_secondary_metabolites=True)\n", 149 | "b.display_in_notebook()" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": { 155 | "heading_collapsed": true 156 | }, 157 | "source": [ 158 | "# Escher for experimental data (20min)\n", 159 | "\n", 160 | "Escher can be used to visualize any experimental data associated with reactions, metabolites, or genes.\n", 161 | "\n", 162 | "Reaction-associated data is loaded just like fluxes. Each data point should have a key that is a reaction ID on the map and a floating point value.\n", 163 | "\n", 164 | "## Metabolite data\n", 165 | "\n", 166 | "For metabolite data, each point should have a key that is a metabolite ID on the map. Let's look at an existing CSV file of metabolomics data." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": { 173 | "collapsed": true, 174 | "hidden": true 175 | }, 176 | "outputs": [], 177 | "source": [ 178 | "import pandas as pd\n", 179 | "metabolomics = pd.read_table('data/S4_McCloskey2013_aerobic_metabolomics.csv', sep=',', header=None)\n", 180 | "metabolomics.head()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": { 186 | "hidden": true 187 | }, 188 | "source": [ 189 | "Escher expects a dictionary, so let's make a dictionary out of this data and pass it into a Builder as `metabolite_data`." 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": { 196 | "collapsed": true, 197 | "hidden": true 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "metabolomics_dict = dict(metabolomics.values)" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": { 208 | "collapsed": true, 209 | "hidden": true 210 | }, 211 | "outputs": [], 212 | "source": [ 213 | "b = escher.Builder(map_name='e_coli_core.Core metabolism',\n", 214 | " metabolite_data=metabolomics_dict,\n", 215 | " metabolite_scale=[\n", 216 | " {'type': 'min', 'color': 'white', 'size': 10},\n", 217 | " {'type': 'median', 'color': 'green', 'size': 20},\n", 218 | " {'type': 'max', 'color': 'red', 'size': 40},\n", 219 | " ],\n", 220 | " enable_tooltips=False, \n", 221 | " )\n", 222 | "b.display_in_notebook()" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": { 228 | "hidden": true 229 | }, 230 | "source": [ 231 | "## Gene data & dataset comparison with Escher\n", 232 | "\n", 233 | "For metabolite data, each point should have a key that is a gene ID on the map. To see the genes, first load a map with the option `show_gene_reaction_rules=True`.\n", 234 | "\n", 235 | "Escher also allows you to load two datasets and visualize a comparison, so we will try that with gene data." 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true, 243 | "hidden": true 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "b = escher.Builder(map_name='e_coli_core.Core metabolism',\n", 248 | " show_gene_reaction_rules=True,\n", 249 | " )\n", 250 | "b.display_in_notebook()" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "hidden": true 257 | }, 258 | "source": [ 259 | "You can provide genes by ID (the locus tags show on the map) or by name (you can see these when you hover over a gene).\n", 260 | "\n", 261 | "Let's load some example RNA-seq data." 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": { 268 | "collapsed": true, 269 | "hidden": true 270 | }, 271 | "outputs": [], 272 | "source": [ 273 | "rnaseq = pd.read_table('data/S6_RNA-seq_aerobic_to_anaerobic.csv', sep=',', header=0, index_col=0)\n", 274 | "rnaseq.head()" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": { 280 | "collapsed": true, 281 | "hidden": true 282 | }, 283 | "source": [ 284 | "Notice that we have two datasets here. For multiple datasets, Escher expects an array of data dictionaries. E.g.:\n", 285 | "\n", 286 | "`[ { 'b0001': 4895.5133 ... }, { 'b0001': 8567.3833 ...} ]`\n", 287 | "\n", 288 | "We can make that array like this:" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "metadata": { 295 | "collapsed": true, 296 | "hidden": true 297 | }, 298 | "outputs": [], 299 | "source": [ 300 | "rnaseq_array = [dict(zip(rnaseq.index, x)) for x in rnaseq.values.T]" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": { 306 | "hidden": true 307 | }, 308 | "source": [ 309 | "Let's plot it on the map! Genes are visualized on reactions, so the `reaction_scale` and `reaction_styles` options still work here. We also have new options for `reaction_compare_style` which can be 'fold', 'log2_fold', or 'diff' and define how the two datasets are compared on the map." 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": null, 315 | "metadata": { 316 | "collapsed": true, 317 | "hidden": true 318 | }, 319 | "outputs": [], 320 | "source": [ 321 | "b = escher.Builder(map_name='e_coli_core.Core metabolism',\n", 322 | " gene_data=rnaseq_array,\n", 323 | " reaction_compare_style='log2_fold',\n", 324 | " # change the default colors\n", 325 | " reaction_scale=[{'type': 'min', 'color': 'green', 'size': 25},\n", 326 | " {'type': 'value', 'value': 0, 'color': '#cccccc', 'size': 8},\n", 327 | " {'type': 'max', 'color': 'red', 'size': 25}],\n", 328 | " # absolute value and no text for data\n", 329 | " reaction_styles=['size', 'color', 'text'],\n", 330 | " # only show the primary metabolites\n", 331 | " hide_secondary_metabolites=True)\n", 332 | "b.display_in_notebook()" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": { 338 | "hidden": true 339 | }, 340 | "source": [ 341 | "Right away, we can see that genes activated in anaerobic conditions (e.g. fermentation pathways) are red, and genes activated in aerobic conditions (e.g. TCA cycle) are green." 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "# Editing maps and loading custom maps in Escher (10min)\n", 349 | "\n", 350 | "Escher lets you build your own maps for any model. The Jupyter notebook is not a great environment for interactiving heavily with the maps, so editing is easier on the website. All you need is a JSON file for your COBRA model to serve as the source of content for your new map. You can also build new maps with the models already available on the Escher website, or you can edit existing maps to fit your needs.\n", 351 | "\n", 352 | "There are some more tips on this page in the documentation: \n", 353 | "\n", 354 | "https://escher.readthedocs.io/en/stable/contribute_maps.html\n", 355 | "\n", 356 | "\n", 357 | "Once you have a custom map, you can visualized it in the Escher Python package by passing a filename to `map_json`." 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": null, 363 | "metadata": { 364 | "collapsed": true 365 | }, 366 | "outputs": [], 367 | "source": [ 368 | "# pass the model to a new builder\n", 369 | "b = escher.Builder(map_json='data/custom_map.json')\n", 370 | "b.display_in_notebook()" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "To make the file accessible to Escher, go to the Jupyter file manager and use the Upload button to add your custom map to the data folder. Then try visualizing it here in Escher:" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": { 384 | "collapsed": true 385 | }, 386 | "outputs": [], 387 | "source": [ 388 | "b = escher.Builder(map_json='data/CHANGE_ME.json')\n", 389 | "b.display_in_notebook()" 390 | ] 391 | } 392 | ], 393 | "metadata": { 394 | "kernelspec": { 395 | "display_name": "Python 3", 396 | "language": "python", 397 | "name": "python3" 398 | }, 399 | "language_info": { 400 | "codemirror_mode": { 401 | "name": "ipython", 402 | "version": 3 403 | }, 404 | "file_extension": ".py", 405 | "mimetype": "text/x-python", 406 | "name": "python", 407 | "nbconvert_exporter": "python", 408 | "pygments_lexer": "ipython3", 409 | "version": "3.6.0" 410 | } 411 | }, 412 | "nbformat": 4, 413 | "nbformat_minor": 1 414 | } 415 | -------------------------------------------------------------------------------- /python-01-crash-course.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Programming with Python and the Jupyter notebook" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This lesson is a very brief introduction to Python and the Jupyter notebook, aiming to give you the bare essentials needed to follow our tutorial on cobrapy, escher and cameo. It is not meant as a general introduction to Python, for that there are plenty of other resources, you can for example start with [learnpython.org](https://www.learnpython.org/).\n", 15 | "\n", 16 | "We assume that you have Jupyter installed and running, or using it via our online interactive server, please look elsewhere for getting to this point, e.g. [this comprehensive tutorial](https://www.datacamp.com/community/tutorials/tutorial-jupyter-notebook#gs.mVNYmI4)." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Moving around and make new cells (3 + 2)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "The notebook is consists of 'cells' which are either python code or markdown (like this one).\n", 31 | "\n", 32 | "- If you press “esc” and “return” alternately, the outer border of your code cell will change from gray to green. The difference in color is subtle.\n", 33 | " - These are the control (gray) and edit (green) modes of your notebook.\n", 34 | "- If you use the “esc” and “return” keys to make the surround gray and then press the “h” key, a list of all the shortcut keys will appear.\n", 35 | "- When in control mode (esc/gray),\n", 36 | " - The “b” key will make a new cell below the currently selected cell.\n", 37 | " - The “a” key will make one above.\n", 38 | " - The “x” key will delete the current cell.\n", 39 | "- You can undo actions with Ctrl + z in edit mode (green) and just \"z\" in control mode (gray).\n", 40 | "- There are lots of shortcuts you can try out and most actions can be done with the menus at the top of the page if you forget the shortcuts.\n", 41 | "- If you remember the “esc” and “h” shortcut, you will be able to find out all the rest." 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "If you press \"B\" and you get a new cell below the current one, preceded by a \"In [ ]\". This indicates that you have a \"code\" cell. If you hit \"esc\" and \"m\" you convert the cell to a markdown cell. In such cells you can write text as you like and use formatting with regular characters\n", 49 | "\n", 50 | "- A leading # makes a heading\n", 51 | "- Bullet lists starts with -\n", 52 | "- \\*\\*fat\\*\\* **fat**\n", 53 | "- \\*italic\\* *italic*\n", 54 | "\n", 55 | "Plenty more formatting such as tables and math notation but we won't go into that here." 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "### Exercise" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "Make this text a headline\n", 70 | "\n", 71 | "Make this a sub-headline\n", 72 | "\n", 73 | "Write some text in a new cell and add some formatting" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "## Variables, functions and assignment (5 + 1)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "- A variable is like a box in which you can put a value. \n", 88 | "- You can make a new variable by inventing a name and assigning a value to it.\n", 89 | "- Variables names can be almost anything (but must not start with a number). Names are case-sensitive so `Age` is not the same as `age`." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 1, 95 | "metadata": { 96 | "collapsed": true 97 | }, 98 | "outputs": [], 99 | "source": [ 100 | "age = 42\n", 101 | "last_name = 'Wang'\n", 102 | "shoe_size = 10.5" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 2, 108 | "metadata": {}, 109 | "outputs": [ 110 | { 111 | "data": { 112 | "text/plain": [ 113 | "42" 114 | ] 115 | }, 116 | "execution_count": 2, 117 | "metadata": {}, 118 | "output_type": "execute_result" 119 | } 120 | ], 121 | "source": [ 122 | "age" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 3, 128 | "metadata": {}, 129 | "outputs": [ 130 | { 131 | "data": { 132 | "text/plain": [ 133 | "'Wang'" 134 | ] 135 | }, 136 | "execution_count": 3, 137 | "metadata": {}, 138 | "output_type": "execute_result" 139 | } 140 | ], 141 | "source": [ 142 | "last_name" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "- Functions are just like in math, something that takes a value and spits out another. In addition they may have a side-effect, like printing something to the screen. `print` is such a function." 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 4, 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "name": "stdout", 159 | "output_type": "stream", 160 | "text": [ 161 | "Mr Wang is 42 years old and wears size 10.5 boots.\n" 162 | ] 163 | } 164 | ], 165 | "source": [ 166 | "print('Mr', last_name, 'is', age, 'years old and wears size', shoe_size, 'boots.')" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "Making new functions is done using the `def` keyword." 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 5, 179 | "metadata": { 180 | "collapsed": true 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "def one_plus(x):\n", 185 | " return 1 + x" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 6, 191 | "metadata": {}, 192 | "outputs": [ 193 | { 194 | "data": { 195 | "text/plain": [ 196 | "6" 197 | ] 198 | }, 199 | "execution_count": 6, 200 | "metadata": {}, 201 | "output_type": "execute_result" 202 | } 203 | ], 204 | "source": [ 205 | "one_plus(5)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "### Exercise" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "Convert to code cells and fill in the blanks" 220 | ] 221 | }, 222 | { 223 | "cell_type": "raw", 224 | "metadata": {}, 225 | "source": [ 226 | "name = ____\n", 227 | "____ = 42\n", 228 | "print(____)" 229 | ] 230 | }, 231 | { 232 | "cell_type": "raw", 233 | "metadata": {}, 234 | "source": [ 235 | "def age_in_five_years(____):\n", 236 | " print(_____)\n", 237 | "\n", 238 | "age_in_five_years(42)\n", 239 | "# 47" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "## Data types (3 + 1)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "Every value has a type.\n", 254 | "\n", 255 | "- The three most common types are strings, like `last_name`, integer (any whole number), like `age` and float (any real value number), like `shoe_size`.\n", 256 | "- Notice the difference between a variable name, `last_name` and the value `'Wang'`. One does not have quotation marks, the other does.\n", 257 | "- What you can do with variables depend on their type. Adding two numbers makes sense, so can adding two strings. But subtracting one string from another does not." 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 7, 263 | "metadata": {}, 264 | "outputs": [ 265 | { 266 | "data": { 267 | "text/plain": [ 268 | "5" 269 | ] 270 | }, 271 | "execution_count": 7, 272 | "metadata": {}, 273 | "output_type": "execute_result" 274 | } 275 | ], 276 | "source": [ 277 | "x = 2\n", 278 | "y = 3\n", 279 | "x + y" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": 8, 285 | "metadata": {}, 286 | "outputs": [ 287 | { 288 | "data": { 289 | "text/plain": [ 290 | "'abcdef'" 291 | ] 292 | }, 293 | "execution_count": 8, 294 | "metadata": {}, 295 | "output_type": "execute_result" 296 | } 297 | ], 298 | "source": [ 299 | "x = 'abc'\n", 300 | "y = 'def'\n", 301 | "x + y" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 9, 307 | "metadata": { 308 | "collapsed": true 309 | }, 310 | "outputs": [], 311 | "source": [ 312 | "# but what this should do is not easy to say and consequently also not allowed\n", 313 | "# x - y" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 10, 319 | "metadata": {}, 320 | "outputs": [ 321 | { 322 | "name": "stdout", 323 | "output_type": "stream", 324 | "text": [ 325 | "\n", 326 | "\n", 327 | "\n" 328 | ] 329 | } 330 | ], 331 | "source": [ 332 | "x = 'abc'\n", 333 | "y = 3\n", 334 | "z = 3.14156\n", 335 | "print(type(x))\n", 336 | "print(type(y))\n", 337 | "print(type(z))" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": {}, 343 | "source": [ 344 | "### Exercises" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "Correct the code below" 352 | ] 353 | }, 354 | { 355 | "cell_type": "raw", 356 | "metadata": {}, 357 | "source": [ 358 | "'1.2' + 2 == 2.2" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "## Getting help (1 + 2)" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "In Jupyter, you can read documentation for functions by typing a question mark after (or before) an object." 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": 11, 378 | "metadata": { 379 | "collapsed": true 380 | }, 381 | "outputs": [], 382 | "source": [ 383 | "print?" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": {}, 389 | "source": [ 390 | "and you can get help on parameters (i.e. what you put inside the brackets) for functions by shift-tab, or just tab for a shorter version." 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": {}, 396 | "source": [ 397 | "### Exercise" 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "metadata": {}, 403 | "source": [ 404 | "- What does the built-in function `len` do? Try using it.\n", 405 | "- Try tab and shift-tab to see information about parameters for `print`." 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "## Error messages (2 + 2)" 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": {}, 418 | "source": [ 419 | "As you learn to program in Python you will see lots of error messages. An error, aka exception, arises when you try to do something that is ill-defined, either by obvious mistakes, when you pass the wrong types of arguments (perhaps due to poor documentation..), or when the program you are trying to run is buggy. Error messages can look scary and incomprehensible but more often than not they are actually useful." 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": 12, 425 | "metadata": { 426 | "collapsed": true 427 | }, 428 | "outputs": [], 429 | "source": [ 430 | "# Forgot to close quotation marks around the string\n", 431 | "# name = 'Kiyofumi" 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": 13, 437 | "metadata": { 438 | "collapsed": true 439 | }, 440 | "outputs": [], 441 | "source": [ 442 | "# Forgot closing parenthesis\n", 443 | "# print('hello worls" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": 14, 449 | "metadata": { 450 | "collapsed": true 451 | }, 452 | "outputs": [], 453 | "source": [ 454 | "# Wrong indentation\n", 455 | "# name = 'Kiyofumi'\n", 456 | "# age = 42" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": {}, 462 | "source": [ 463 | "### Exercises\n", 464 | "- Uncomment the code above and try to understand the error messages. " 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "## More data types (2 + 2)" 472 | ] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": {}, 477 | "source": [ 478 | "There are many data types in Python and as you will see a bit further on, it is easy to make new ones. In our further notebooks we make frequent use of two particular types, `list` and `set`." 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "A `list` is exactly that, an ordered collection of items. You can create a list with values as shown below:" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": 15, 491 | "metadata": { 492 | "collapsed": true 493 | }, 494 | "outputs": [], 495 | "source": [ 496 | "the_abc = ['a', 'b', 'c']" 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": {}, 502 | "source": [ 503 | "We get access the elements in the list by using indexing" 504 | ] 505 | }, 506 | { 507 | "cell_type": "code", 508 | "execution_count": 16, 509 | "metadata": {}, 510 | "outputs": [ 511 | { 512 | "data": { 513 | "text/plain": [ 514 | "'a'" 515 | ] 516 | }, 517 | "execution_count": 16, 518 | "metadata": {}, 519 | "output_type": "execute_result" 520 | } 521 | ], 522 | "source": [ 523 | "the_abc[0]" 524 | ] 525 | }, 526 | { 527 | "cell_type": "code", 528 | "execution_count": 17, 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "data": { 533 | "text/plain": [ 534 | "'b'" 535 | ] 536 | }, 537 | "execution_count": 17, 538 | "metadata": {}, 539 | "output_type": "execute_result" 540 | } 541 | ], 542 | "source": [ 543 | "the_abc[1]" 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": 18, 549 | "metadata": {}, 550 | "outputs": [ 551 | { 552 | "data": { 553 | "text/plain": [ 554 | "3" 555 | ] 556 | }, 557 | "execution_count": 18, 558 | "metadata": {}, 559 | "output_type": "execute_result" 560 | } 561 | ], 562 | "source": [ 563 | "len(the_abc)" 564 | ] 565 | }, 566 | { 567 | "cell_type": "markdown", 568 | "metadata": {}, 569 | "source": [ 570 | "A `set` is also a collection of items, but, they don't have a particular order." 571 | ] 572 | }, 573 | { 574 | "cell_type": "code", 575 | "execution_count": 19, 576 | "metadata": { 577 | "collapsed": true 578 | }, 579 | "outputs": [], 580 | "source": [ 581 | "joy_division = {'ian', 'peter', 'bernard', 'stephen'}" 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": {}, 587 | "source": [ 588 | "### Exercises" 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "metadata": {}, 594 | "source": [ 595 | "Convert the cells below to code and fill in the blanks." 596 | ] 597 | }, 598 | { 599 | "cell_type": "raw", 600 | "metadata": {}, 601 | "source": [ 602 | "___(joy_division) == 4" 603 | ] 604 | }, 605 | { 606 | "cell_type": "raw", 607 | "metadata": {}, 608 | "source": [ 609 | "the_abc + ____ == ['a', 'b', 'c', 'd', 'e', 'f']" 610 | ] 611 | }, 612 | { 613 | "cell_type": "markdown", 614 | "metadata": {}, 615 | "source": [ 616 | "## For loops (2 + 2)" 617 | ] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "metadata": {}, 622 | "source": [ 623 | "If we want to do an action for every item in a list, then it would be tedious to write it like this" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": 20, 629 | "metadata": {}, 630 | "outputs": [ 631 | { 632 | "name": "stdout", 633 | "output_type": "stream", 634 | "text": [ 635 | "a\n", 636 | "b\n", 637 | "c\n" 638 | ] 639 | } 640 | ], 641 | "source": [ 642 | "print(the_abc[0])\n", 643 | "print(the_abc[1])\n", 644 | "print(the_abc[2])" 645 | ] 646 | }, 647 | { 648 | "cell_type": "markdown", 649 | "metadata": {}, 650 | "source": [ 651 | "Particularly if we wanted to use a list with thousands of elements.. For this we can instead use a for loop. Note the indentation which says that everything below the `for`, and immediately one level to the right, should be done once for every item in the iterable." 652 | ] 653 | }, 654 | { 655 | "cell_type": "code", 656 | "execution_count": 21, 657 | "metadata": {}, 658 | "outputs": [ 659 | { 660 | "name": "stdout", 661 | "output_type": "stream", 662 | "text": [ 663 | "a\n", 664 | "b\n", 665 | "c\n" 666 | ] 667 | } 668 | ], 669 | "source": [ 670 | "for letter in the_abc: # the_abc is our iterable here.\n", 671 | " print(letter)" 672 | ] 673 | }, 674 | { 675 | "cell_type": "markdown", 676 | "metadata": {}, 677 | "source": [ 678 | "### Exercises" 679 | ] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "Convert the cell below to code and fill in the blanks." 686 | ] 687 | }, 688 | { 689 | "cell_type": "raw", 690 | "metadata": {}, 691 | "source": [ 692 | "cumsum = 0\n", 693 | "for i in ____:\n", 694 | " cumsum = ____\n", 695 | "cumsum\n", 696 | "# 6" 697 | ] 698 | }, 699 | { 700 | "cell_type": "markdown", 701 | "metadata": {}, 702 | "source": [ 703 | "## Classes and object-orientation (5 + 5)" 704 | ] 705 | }, 706 | { 707 | "cell_type": "markdown", 708 | "metadata": {}, 709 | "source": [ 710 | "In cobrapy, cameo and escher, we use many objects that are more complicated than just strings and numbers. For this we make use of something called *classes*. A class describes what properties and methods an object of its class shall have. Think for example of a tree. A tree class could be written like this." 711 | ] 712 | }, 713 | { 714 | "cell_type": "code", 715 | "execution_count": 22, 716 | "metadata": { 717 | "collapsed": true 718 | }, 719 | "outputs": [], 720 | "source": [ 721 | "class Tree(object):\n", 722 | " def __init__(self, height=0, species = 'oak'):\n", 723 | " self.height = height\n", 724 | " self.species = species" 725 | ] 726 | }, 727 | { 728 | "cell_type": "markdown", 729 | "metadata": {}, 730 | "source": [ 731 | "Now we can make a new tree like this" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 23, 737 | "metadata": { 738 | "collapsed": true 739 | }, 740 | "outputs": [], 741 | "source": [ 742 | "tree = Tree()" 743 | ] 744 | }, 745 | { 746 | "cell_type": "markdown", 747 | "metadata": {}, 748 | "source": [ 749 | "And inspect how tall it is and what species using the '.'" 750 | ] 751 | }, 752 | { 753 | "cell_type": "code", 754 | "execution_count": 24, 755 | "metadata": {}, 756 | "outputs": [ 757 | { 758 | "name": "stdout", 759 | "output_type": "stream", 760 | "text": [ 761 | "My newly planted tree is an oak and it is 0 meters tall\n" 762 | ] 763 | } 764 | ], 765 | "source": [ 766 | "print('My newly planted tree is an', tree.species, 'and it is', tree.height, 'meters tall')" 767 | ] 768 | }, 769 | { 770 | "cell_type": "markdown", 771 | "metadata": {}, 772 | "source": [ 773 | "We could add a method to the class like this. Note the indentation, because its is one level to the right of the `class Tree:` it becomes part of that class." 774 | ] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "execution_count": 25, 779 | "metadata": { 780 | "collapsed": true 781 | }, 782 | "outputs": [], 783 | "source": [ 784 | "class Tree(object):\n", 785 | " def __init__(self, height=0, species = 'oak'):\n", 786 | " self.height = height\n", 787 | " self.species = species\n", 788 | "\n", 789 | " def grow(self, meters):\n", 790 | " self.height += meters" 791 | ] 792 | }, 793 | { 794 | "cell_type": "code", 795 | "execution_count": 26, 796 | "metadata": { 797 | "collapsed": true 798 | }, 799 | "outputs": [], 800 | "source": [ 801 | "tree = Tree()\n", 802 | "tree.grow(1)" 803 | ] 804 | }, 805 | { 806 | "cell_type": "code", 807 | "execution_count": 27, 808 | "metadata": {}, 809 | "outputs": [ 810 | { 811 | "name": "stdout", 812 | "output_type": "stream", 813 | "text": [ 814 | "My 2 year old tree is an oak and it is 1 meters tall\n" 815 | ] 816 | } 817 | ], 818 | "source": [ 819 | "print('My 2 year old tree is an', tree.species, 'and it is', tree.height, 'meters tall')" 820 | ] 821 | }, 822 | { 823 | "cell_type": "markdown", 824 | "metadata": {}, 825 | "source": [ 826 | "By convention, class names are capitalized, variables and functions are lower-case." 827 | ] 828 | }, 829 | { 830 | "cell_type": "markdown", 831 | "metadata": {}, 832 | "source": [ 833 | "### Exercises" 834 | ] 835 | }, 836 | { 837 | "cell_type": "markdown", 838 | "metadata": {}, 839 | "source": [ 840 | "Convert the cell below to code and fill in the blanks." 841 | ] 842 | }, 843 | { 844 | "cell_type": "raw", 845 | "metadata": { 846 | "collapsed": true 847 | }, 848 | "source": [ 849 | "class Wallet(____):\n", 850 | " def __init__(self, content=0):\n", 851 | " self.content = content\n", 852 | "\n", 853 | " def earn(self, amount):\n", 854 | " self.content __ amount\n", 855 | " \n", 856 | " def spend(____, ____):\n", 857 | " ____\n", 858 | "\n", 859 | "wallet = Wallet()\n", 860 | "wallet.earn(10)\n", 861 | "wallet.spend(3)\n", 862 | "wallet.content\n", 863 | "# 7" 864 | ] 865 | }, 866 | { 867 | "cell_type": "markdown", 868 | "metadata": {}, 869 | "source": [ 870 | "## Packages and modules (3)" 871 | ] 872 | }, 873 | { 874 | "cell_type": "markdown", 875 | "metadata": {}, 876 | "source": [ 877 | "Most of the useful functions and class you will use reside in a package, which is a collection of modules. cobrapy, cameo and escher are examples of packages. To use one of these you have to import them." 878 | ] 879 | }, 880 | { 881 | "cell_type": "code", 882 | "execution_count": 28, 883 | "metadata": { 884 | "collapsed": true 885 | }, 886 | "outputs": [], 887 | "source": [ 888 | "import cobra" 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": {}, 894 | "source": [ 895 | "Now we can have access to members of that package. E.g." 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": 29, 901 | "metadata": {}, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/plain": [ 906 | "'0.8.1'" 907 | ] 908 | }, 909 | "execution_count": 29, 910 | "metadata": {}, 911 | "output_type": "execute_result" 912 | } 913 | ], 914 | "source": [ 915 | "cobra.__version__" 916 | ] 917 | }, 918 | { 919 | "cell_type": "markdown", 920 | "metadata": {}, 921 | "source": [ 922 | "Within cobrapy, there are lots of modules. We can import from the top-level " 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": 30, 928 | "metadata": { 929 | "collapsed": true 930 | }, 931 | "outputs": [], 932 | "source": [ 933 | "from cobra import Model" 934 | ] 935 | }, 936 | { 937 | "cell_type": "markdown", 938 | "metadata": {}, 939 | "source": [ 940 | "or from lower-level modules" 941 | ] 942 | }, 943 | { 944 | "cell_type": "code", 945 | "execution_count": 31, 946 | "metadata": { 947 | "collapsed": true 948 | }, 949 | "outputs": [], 950 | "source": [ 951 | "from cobra.flux_analysis import flux_variability_analysis" 952 | ] 953 | }, 954 | { 955 | "cell_type": "markdown", 956 | "metadata": {}, 957 | "source": [ 958 | "Don't want to type that long name? Tab is your friend." 959 | ] 960 | }, 961 | { 962 | "cell_type": "markdown", 963 | "metadata": {}, 964 | "source": [ 965 | "## Data frames (5)" 966 | ] 967 | }, 968 | { 969 | "cell_type": "markdown", 970 | "metadata": {}, 971 | "source": [ 972 | "From the pandas package, we get access to the extremely useful class `DataFrame` and since we use that on numerous occasions in this tutorial let's have a look at it." 973 | ] 974 | }, 975 | { 976 | "cell_type": "code", 977 | "execution_count": 32, 978 | "metadata": { 979 | "collapsed": true 980 | }, 981 | "outputs": [], 982 | "source": [ 983 | "import pandas" 984 | ] 985 | }, 986 | { 987 | "cell_type": "code", 988 | "execution_count": 33, 989 | "metadata": { 990 | "collapsed": true 991 | }, 992 | "outputs": [], 993 | "source": [ 994 | "iris = pandas.read_csv('data/iris.csv')" 995 | ] 996 | }, 997 | { 998 | "cell_type": "code", 999 | "execution_count": 34, 1000 | "metadata": {}, 1001 | "outputs": [ 1002 | { 1003 | "data": { 1004 | "text/html": [ 1005 | "
\n", 1006 | "\n", 1019 | "\n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | " \n", 1068 | " \n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1072 | " \n", 1073 | " \n", 1074 | " \n", 1075 | " \n", 1076 | " \n", 1077 | " \n", 1078 | " \n", 1079 | " \n", 1080 | " \n", 1081 | " \n", 1082 | " \n", 1083 | " \n", 1084 | " \n", 1085 | " \n", 1086 | " \n", 1087 | " \n", 1088 | " \n", 1089 | " \n", 1090 | " \n", 1091 | " \n", 1092 | " \n", 1093 | " \n", 1094 | " \n", 1095 | " \n", 1096 | " \n", 1097 | " \n", 1098 | " \n", 1099 | " \n", 1100 | " \n", 1101 | " \n", 1102 | " \n", 1103 | " \n", 1104 | " \n", 1105 | " \n", 1106 | " \n", 1107 | " \n", 1108 | " \n", 1109 | " \n", 1110 | " \n", 1111 | " \n", 1112 | " \n", 1113 | " \n", 1114 | " \n", 1115 | " \n", 1116 | " \n", 1117 | " \n", 1118 | " \n", 1119 | " \n", 1120 | "
sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
..................
1456.73.05.22.3virginica
1466.32.55.01.9virginica
1476.53.05.22.0virginica
1486.23.45.42.3virginica
1495.93.05.11.8virginica
\n", 1121 | "

150 rows × 5 columns

\n", 1122 | "
" 1123 | ], 1124 | "text/plain": [ 1125 | " sepal_length sepal_width petal_length petal_width species\n", 1126 | "0 5.1 3.5 1.4 0.2 setosa\n", 1127 | "1 4.9 3.0 1.4 0.2 setosa\n", 1128 | "2 4.7 3.2 1.3 0.2 setosa\n", 1129 | "3 4.6 3.1 1.5 0.2 setosa\n", 1130 | "4 5.0 3.6 1.4 0.2 setosa\n", 1131 | ".. ... ... ... ... ...\n", 1132 | "145 6.7 3.0 5.2 2.3 virginica\n", 1133 | "146 6.3 2.5 5.0 1.9 virginica\n", 1134 | "147 6.5 3.0 5.2 2.0 virginica\n", 1135 | "148 6.2 3.4 5.4 2.3 virginica\n", 1136 | "149 5.9 3.0 5.1 1.8 virginica\n", 1137 | "\n", 1138 | "[150 rows x 5 columns]" 1139 | ] 1140 | }, 1141 | "execution_count": 34, 1142 | "metadata": {}, 1143 | "output_type": "execute_result" 1144 | } 1145 | ], 1146 | "source": [ 1147 | "iris" 1148 | ] 1149 | }, 1150 | { 1151 | "cell_type": "code", 1152 | "execution_count": 35, 1153 | "metadata": {}, 1154 | "outputs": [ 1155 | { 1156 | "data": { 1157 | "text/html": [ 1158 | "
\n", 1159 | "\n", 1172 | "\n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | " \n", 1191 | " \n", 1192 | " \n", 1193 | " \n", 1194 | " \n", 1195 | " \n", 1196 | " \n", 1197 | " \n", 1198 | " \n", 1199 | " \n", 1200 | " \n", 1201 | " \n", 1202 | " \n", 1203 | " \n", 1204 | " \n", 1205 | " \n", 1206 | " \n", 1207 | " \n", 1208 | " \n", 1209 | " \n", 1210 | " \n", 1211 | " \n", 1212 | " \n", 1213 | " \n", 1214 | " \n", 1215 | " \n", 1216 | " \n", 1217 | " \n", 1218 | " \n", 1219 | " \n", 1220 | " \n", 1221 | " \n", 1222 | " \n", 1223 | " \n", 1224 | " \n", 1225 | " \n", 1226 | " \n", 1227 | " \n", 1228 | " \n", 1229 | " \n", 1230 | " \n", 1231 | " \n", 1232 | " \n", 1233 | " \n", 1234 | " \n", 1235 | " \n", 1236 | " \n", 1237 | " \n", 1238 | " \n", 1239 | " \n", 1240 | "
sepal_lengthsepal_widthpetal_lengthpetal_width
count150.0000150.0000150.0000150.0000
mean5.84333.05403.75871.1987
std0.82810.43361.76440.7632
min4.30002.00001.00000.1000
25%5.10002.80001.60000.3000
50%5.80003.00004.35001.3000
75%6.40003.30005.10001.8000
max7.90004.40006.90002.5000
\n", 1241 | "
" 1242 | ], 1243 | "text/plain": [ 1244 | " sepal_length sepal_width petal_length petal_width\n", 1245 | "count 150.0000 150.0000 150.0000 150.0000\n", 1246 | "mean 5.8433 3.0540 3.7587 1.1987\n", 1247 | "std 0.8281 0.4336 1.7644 0.7632\n", 1248 | "min 4.3000 2.0000 1.0000 0.1000\n", 1249 | "25% 5.1000 2.8000 1.6000 0.3000\n", 1250 | "50% 5.8000 3.0000 4.3500 1.3000\n", 1251 | "75% 6.4000 3.3000 5.1000 1.8000\n", 1252 | "max 7.9000 4.4000 6.9000 2.5000" 1253 | ] 1254 | }, 1255 | "execution_count": 35, 1256 | "metadata": {}, 1257 | "output_type": "execute_result" 1258 | } 1259 | ], 1260 | "source": [ 1261 | "iris.describe()" 1262 | ] 1263 | }, 1264 | { 1265 | "cell_type": "markdown", 1266 | "metadata": {}, 1267 | "source": [ 1268 | "Data frames is kind of table that has rows and columns and indices. You can access values by the column or row index." 1269 | ] 1270 | }, 1271 | { 1272 | "cell_type": "code", 1273 | "execution_count": 36, 1274 | "metadata": {}, 1275 | "outputs": [ 1276 | { 1277 | "data": { 1278 | "text/plain": [ 1279 | "0 5.1\n", 1280 | "1 4.9\n", 1281 | "2 4.7\n", 1282 | "3 4.6\n", 1283 | "4 5.0\n", 1284 | " ... \n", 1285 | "145 6.7\n", 1286 | "146 6.3\n", 1287 | "147 6.5\n", 1288 | "148 6.2\n", 1289 | "149 5.9\n", 1290 | "Name: sepal_length, Length: 150, dtype: float64" 1291 | ] 1292 | }, 1293 | "execution_count": 36, 1294 | "metadata": {}, 1295 | "output_type": "execute_result" 1296 | } 1297 | ], 1298 | "source": [ 1299 | "iris['sepal_length']\n", 1300 | "# is the same as\n", 1301 | "iris.sepal_length" 1302 | ] 1303 | }, 1304 | { 1305 | "cell_type": "code", 1306 | "execution_count": 37, 1307 | "metadata": {}, 1308 | "outputs": [ 1309 | { 1310 | "data": { 1311 | "text/plain": [ 1312 | "sepal_length 5.1\n", 1313 | "sepal_width 3.5\n", 1314 | "petal_length 1.4\n", 1315 | "petal_width 0.2\n", 1316 | "species setosa\n", 1317 | "Name: 0, dtype: object" 1318 | ] 1319 | }, 1320 | "execution_count": 37, 1321 | "metadata": {}, 1322 | "output_type": "execute_result" 1323 | } 1324 | ], 1325 | "source": [ 1326 | "iris.iloc[0]" 1327 | ] 1328 | }, 1329 | { 1330 | "cell_type": "markdown", 1331 | "metadata": {}, 1332 | "source": [ 1333 | "We can also make more complex queries" 1334 | ] 1335 | }, 1336 | { 1337 | "cell_type": "code", 1338 | "execution_count": 38, 1339 | "metadata": {}, 1340 | "outputs": [ 1341 | { 1342 | "data": { 1343 | "text/html": [ 1344 | "
\n", 1345 | "\n", 1358 | "\n", 1359 | " \n", 1360 | " \n", 1361 | " \n", 1362 | " \n", 1363 | " \n", 1364 | " \n", 1365 | " \n", 1366 | " \n", 1367 | " \n", 1368 | " \n", 1369 | " \n", 1370 | " \n", 1371 | " \n", 1372 | " \n", 1373 | " \n", 1374 | " \n", 1375 | " \n", 1376 | " \n", 1377 | " \n", 1378 | " \n", 1379 | " \n", 1380 | " \n", 1381 | " \n", 1382 | " \n", 1383 | " \n", 1384 | " \n", 1385 | " \n", 1386 | " \n", 1387 | " \n", 1388 | " \n", 1389 | " \n", 1390 | " \n", 1391 | " \n", 1392 | " \n", 1393 | " \n", 1394 | " \n", 1395 | " \n", 1396 | " \n", 1397 | " \n", 1398 | " \n", 1399 | " \n", 1400 | " \n", 1401 | " \n", 1402 | " \n", 1403 | " \n", 1404 | " \n", 1405 | " \n", 1406 | " \n", 1407 | " \n", 1408 | " \n", 1409 | " \n", 1410 | " \n", 1411 | " \n", 1412 | " \n", 1413 | " \n", 1414 | " \n", 1415 | " \n", 1416 | " \n", 1417 | " \n", 1418 | " \n", 1419 | " \n", 1420 | " \n", 1421 | " \n", 1422 | " \n", 1423 | " \n", 1424 | " \n", 1425 | " \n", 1426 | " \n", 1427 | " \n", 1428 | " \n", 1429 | " \n", 1430 | " \n", 1431 | " \n", 1432 | " \n", 1433 | " \n", 1434 | " \n", 1435 | "
sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
507.03.24.71.4versicolor
526.93.14.91.5versicolor
586.62.94.61.3versicolor
656.73.14.41.4versicolor
756.63.04.41.4versicolor
766.82.84.81.4versicolor
776.73.05.01.7versicolor
866.73.14.71.5versicolor
\n", 1436 | "
" 1437 | ], 1438 | "text/plain": [ 1439 | " sepal_length sepal_width petal_length petal_width species\n", 1440 | "50 7.0 3.2 4.7 1.4 versicolor\n", 1441 | "52 6.9 3.1 4.9 1.5 versicolor\n", 1442 | "58 6.6 2.9 4.6 1.3 versicolor\n", 1443 | "65 6.7 3.1 4.4 1.4 versicolor\n", 1444 | "75 6.6 3.0 4.4 1.4 versicolor\n", 1445 | "76 6.8 2.8 4.8 1.4 versicolor\n", 1446 | "77 6.7 3.0 5.0 1.7 versicolor\n", 1447 | "86 6.7 3.1 4.7 1.5 versicolor" 1448 | ] 1449 | }, 1450 | "execution_count": 38, 1451 | "metadata": {}, 1452 | "output_type": "execute_result" 1453 | } 1454 | ], 1455 | "source": [ 1456 | "iris[(iris.sepal_length > 6.5) & (iris.species == 'versicolor')]" 1457 | ] 1458 | }, 1459 | { 1460 | "cell_type": "code", 1461 | "execution_count": 39, 1462 | "metadata": {}, 1463 | "outputs": [ 1464 | { 1465 | "data": { 1466 | "text/plain": [ 1467 | "" 1468 | ] 1469 | }, 1470 | "execution_count": 39, 1471 | "metadata": {}, 1472 | "output_type": "execute_result" 1473 | }, 1474 | { 1475 | "data": { 1476 | "image/png": "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\nRU5ErkJggg==\n", 1477 | "text/plain": [ 1478 | "" 1479 | ] 1480 | }, 1481 | "metadata": {}, 1482 | "output_type": "display_data" 1483 | } 1484 | ], 1485 | "source": [ 1486 | "%matplotlib inline\n", 1487 | "iris.plot.scatter(x='sepal_length', y='sepal_width')" 1488 | ] 1489 | }, 1490 | { 1491 | "cell_type": "markdown", 1492 | "metadata": {}, 1493 | "source": [ 1494 | "As you may expect there are tons of things you can do with a data frame but this is all you need to know for this tutorial." 1495 | ] 1496 | } 1497 | ], 1498 | "metadata": { 1499 | "kernelspec": { 1500 | "display_name": "Python 3", 1501 | "language": "python", 1502 | "name": "python3" 1503 | }, 1504 | "language_info": { 1505 | "codemirror_mode": { 1506 | "name": "ipython", 1507 | "version": 3 1508 | }, 1509 | "file_extension": ".py", 1510 | "mimetype": "text/x-python", 1511 | "name": "python", 1512 | "nbconvert_exporter": "python", 1513 | "pygments_lexer": "ipython3", 1514 | "version": "3.5.2+" 1515 | }, 1516 | "toc": { 1517 | "colors": { 1518 | "hover_highlight": "#DAA520", 1519 | "navigate_num": "#000000", 1520 | "navigate_text": "#333333", 1521 | "running_highlight": "#FF0000", 1522 | "selected_highlight": "#FFD700", 1523 | "sidebar_border": "#EEEEEE", 1524 | "wrapper_background": "#FFFFFF" 1525 | }, 1526 | "moveMenuLeft": true, 1527 | "nav_menu": { 1528 | "height": "377px", 1529 | "width": "252px" 1530 | }, 1531 | "navigate_menu": true, 1532 | "number_sections": false, 1533 | "sideBar": true, 1534 | "threshold": "3", 1535 | "toc_cell": false, 1536 | "toc_section_display": "block", 1537 | "toc_window_display": true, 1538 | "widenNotebook": false 1539 | } 1540 | }, 1541 | "nbformat": 4, 1542 | "nbformat_minor": 2 1543 | } 1544 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cobra==0.8.1 2 | cameo==0.11.0 3 | pandas==0.20.3 4 | escher==1.6.0 5 | plotly==2.0.12 6 | matplotlib==2.0.2 7 | -------------------------------------------------------------------------------- /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DD-DeCaF/tutorials/b2136fa86912575d90e16f27d806a9c6b8d0bfb9/slides.pdf -------------------------------------------------------------------------------- /slides.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DD-DeCaF/tutorials/b2136fa86912575d90e16f27d806a9c6b8d0bfb9/slides.pptx --------------------------------------------------------------------------------