├── .gitignore ├── README.md ├── dipy ├── 001-dipy-reconstruction.ipynb ├── 002-cross-validation.ipynb ├── 003-dipy-tracking.ipynb ├── 004-quickbundles.ipynb └── 005-linear-fascicle-evaluation.ipynb ├── index.ipynb ├── intro ├── 001-python.ipynb ├── 002-python-full.ipynb ├── 003-object-oriented-python.ipynb ├── circle.py ├── test_circle.py └── testing.md ├── neuroimaging ├── 000-notebooks.ipynb ├── 001-arrays.ipynb ├── 002-spatial-transformations.ipynb ├── 003-plots.ipynb ├── 004-calculations.ipynb ├── 005-interactions.ipynb ├── data │ ├── run1.nii.gz │ ├── someones_anatomy.nii.gz │ └── someones_epi.nii.gz └── images │ ├── array.png │ ├── array.svg │ ├── fourier.png │ ├── illustrating_affine.png │ └── localizer.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-neuroimaging-lesson 2 | Lessons on neuroimaging analysis with Python 3 | 4 | ## Instructions 5 | 6 | To run these notebooks, you will need to install the 7 | [`IPython`](http://ipython.org) software, and a few other scientific libraries. 8 | One way to install these is by using the (free) Anaconda software distribution: 9 | 10 | - Go to the [Anaconda website](http://continuum.io/downloads) and follow the 11 | instructions therein to download and install the distribution for your 12 | platform. 13 | 14 | You will also need the [`nibabel`](http://nipy.org/nibabel) library: 15 | 16 | - To install `nibabel` in a terminal command line execute: 17 | 18 | pip install nibabel 19 | 20 | Once you have followed these instructions, you should have all the software 21 | needed installed on your computer. To start running the notebooks, change the 22 | working directory of your terminal to the directory where you have downloaded 23 | the notebook files (using `cd`) and type: 24 | 25 | ipython notebook 26 | 27 | A browser window should open with a list of notebook files. Start by reading 28 | the `index` notebook and follow the instructions from there. 29 | -------------------------------------------------------------------------------- /dipy/002-cross-validation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# K-fold cross-validation for model comparison\n", 8 | "\n", 9 | "Different models of diffusion MRI can be compared based on their accuracy in\n", 10 | "fitting the diffusion signal. Here, we demonstrate this by comparing two\n", 11 | "models: the diffusion tensor model (DTI) and constrained spherical\n", 12 | "deconvolution (CSD). These models differ from each other substantially. DTI\n", 13 | "approximates the diffusion pattern as a 3D Gaussian distribution, and has only\n", 14 | "6 free parameters. CSD, on the other hand, fits many more parameters. The\n", 15 | "models aare also not nested, so they cannot be compared using the\n", 16 | "log-likelihood ratio.\n", 17 | "\n", 18 | "A general way to perform model comparison is cross-validation [Hastie2008]_. In\n", 19 | "this method, a model is fit to some of the data (a *learning set*) and the\n", 20 | "model is then used to predict a held-out set (a *testing set*). The model\n", 21 | "predictions can then be compared to estimate prediction error on the held out\n", 22 | "set. This method has been used for comparison of models such as DTI and CSD\n", 23 | "[Rokem2014]_, and has the advantage that it the comparison is imprevious to\n", 24 | "differences in the number of parameters in the model, and it can be used to\n", 25 | "compare models that are not nested.\n", 26 | "\n", 27 | "In `dipy`, we include an implementation of k-fold cross-validation. In this\n", 28 | "method, the data is divided into $k$ different segments. In each iteration\n", 29 | "$\\frac{1}{k}th$ of the data is held out and the model is fit to the other\n", 30 | "$\\frac{k-1}{k}$ parts of the data. A prediction of the held out data is done\n", 31 | "and recorded. At the end of $k$ iterations a prediction of all of the data will\n", 32 | "have been conducted, and this can be compared directly to all of the data.\n", 33 | "\n", 34 | "First, we import that modules needed for this example. In particular, the\n", 35 | ":mod:`reconst.cross_validation` module implements k-fold cross-validation" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 1, 41 | "metadata": { 42 | "collapsed": true 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "import numpy as np\n", 47 | "import matplotlib.pyplot as plt\n", 48 | "%matplotlib inline \n", 49 | "\n", 50 | "\n", 51 | "import dipy.data as dpd\n", 52 | "import dipy.reconst.cross_validation as xval\n", 53 | "import dipy.reconst.dti as dti\n", 54 | "import dipy.reconst.csdeconv as csd\n", 55 | "import scipy.stats as stats" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "We fetch some data and select a couple of voxels to perform comparisons on. One\n", 63 | "lies in the corpus callosum (cc), while the other is in the centrum semiovale\n", 64 | "(cso), a part of the brain known to contain multiple crossing white matter\n", 65 | "fiber populations." 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 2, 71 | "metadata": {}, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "Dataset is already in place. If you want to fetch it again please first remove the folder /home/ubuntu/.dipy/stanford_hardi \n", 78 | "Dataset is already in place. If you want to fetch it again please first remove the folder /home/ubuntu/.dipy/stanford_hardi \n" 79 | ] 80 | } 81 | ], 82 | "source": [ 83 | "dpd.fetch_stanford_hardi()\n", 84 | "img, gtab = dpd.read_stanford_hardi()\n", 85 | "data = img.get_data()\n", 86 | "\n", 87 | "cc_vox = data[40, 70, 38]\n", 88 | "cso_vox = data[30, 76, 38]" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "We initialize each kind of model:" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 3, 101 | "metadata": { 102 | "collapsed": true 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "dti_model = dti.TensorModel(gtab)\n", 107 | "response, ratio = csd.auto_response(gtab, data, roi_radius=10, fa_thr=0.7)\n", 108 | "csd_model = csd.ConstrainedSphericalDeconvModel(gtab, response)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "Next, we perform cross-validation for each kind of model, comparing model\n", 116 | "predictions to the diffusion MRI data in each one of these voxels.\n", 117 | "\n", 118 | "Note that we use 2-fold cross-validation, which means that in each iteration,\n", 119 | "the model will be fit to half of the data, and used to predict the other half." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 4, 125 | "metadata": { 126 | "collapsed": true 127 | }, 128 | "outputs": [], 129 | "source": [ 130 | "dti_cc = xval.kfold_xval(dti_model, cc_vox, 2)\n", 131 | "csd_cc = xval.kfold_xval(csd_model, cc_vox, 2, response)\n", 132 | "dti_cso = xval.kfold_xval(dti_model, cso_vox, 2)\n", 133 | "csd_cso = xval.kfold_xval(csd_model, cso_vox, 2, response)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "We plot a scatter plot of the data with the model predictions in each of these\n", 141 | "voxels, focusing only on the diffusion-weighted measurements (each point\n", 142 | "corresponds to a different gradient direction). The two models are compared in\n", 143 | "each sub-plot (blue=DTI, red=CSD)." 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": {}, 150 | "outputs": [ 151 | { 152 | "data": { 153 | "image/png": "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\nRU5ErkJggg==\n", 154 | "text/plain": [ 155 | "" 156 | ] 157 | }, 158 | "metadata": {}, 159 | "output_type": "display_data" 160 | } 161 | ], 162 | "source": [ 163 | "fig, ax = plt.subplots(1,2)\n", 164 | "fig.set_size_inches([12,6])\n", 165 | "ax[0].plot(cc_vox[~gtab.b0s_mask], dti_cc[~gtab.b0s_mask], 'o', color='b')\n", 166 | "ax[0].plot(cc_vox[~gtab.b0s_mask], csd_cc[~gtab.b0s_mask], 'o', color='r')\n", 167 | "ax[1].plot(cso_vox[~gtab.b0s_mask], dti_cso[~gtab.b0s_mask], 'o', color='b', label='DTI')\n", 168 | "ax[1].plot(cso_vox[~gtab.b0s_mask], csd_cso[~gtab.b0s_mask], 'o', color='r', label='CSD')\n", 169 | "plt.legend(loc='upper left')\n", 170 | "for this_ax in ax:\n", 171 | " this_ax.set_xlabel('Data (relative to S0)')\n", 172 | " this_ax.set_ylabel('Model prediction (relative to S0)')" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "We can also quantify the goodness of fit of the models by calculating an\n", 180 | "R-squared score:" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 6, 186 | "metadata": {}, 187 | "outputs": [ 188 | { 189 | "name": "stdout", 190 | "output_type": "stream", 191 | "text": [ 192 | "Corpus callosum\n", 193 | "DTI R2 : 0.7983734494\n", 194 | "CSD R2 : 0.789404729624\n", 195 | "\n", 196 | "Centrum Semiovale\n", 197 | "DTI R2 : 0.497306858367\n", 198 | "CSD R2 : 0.597206157242\n", 199 | "\n" 200 | ] 201 | } 202 | ], 203 | "source": [ 204 | "cc_dti_r2=stats.pearsonr(cc_vox[~gtab.b0s_mask], dti_cc[~gtab.b0s_mask])[0]**2\n", 205 | "cc_csd_r2=stats.pearsonr(cc_vox[~gtab.b0s_mask], csd_cc[~gtab.b0s_mask])[0]**2\n", 206 | "cso_dti_r2=stats.pearsonr(cso_vox[~gtab.b0s_mask], dti_cso[~gtab.b0s_mask])[0]**2\n", 207 | "cso_csd_r2=stats.pearsonr(cso_vox[~gtab.b0s_mask], csd_cso[~gtab.b0s_mask])[0]**2\n", 208 | "\n", 209 | "print(\"Corpus callosum\\n\"\n", 210 | " \"DTI R2 : %s\\n\"\n", 211 | " \"CSD R2 : %s\\n\"\n", 212 | " \"\\n\"\n", 213 | " \"Centrum Semiovale\\n\"\n", 214 | " \"DTI R2 : %s\\n\"\n", 215 | " \"CSD R2 : %s\\n\" % (cc_dti_r2, cc_csd_r2, cso_dti_r2, cso_csd_r2))" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "As you can see, DTI is a pretty good model for describing the signal in the CC,\n", 223 | "while CSD is much better in describing the signal in regions of multiple\n", 224 | "crossing fibers.\n", 225 | "\n", 226 | "\n", 227 | "References\n", 228 | "----------\n", 229 | "\n", 230 | "[Hastie, T., Tibshirani, R., Friedman, J. (2008). The Elements of Statistical Learning: Data Mining, Inference and Prediction. Springer-Verlag, Berlin]()\n", 231 | "\n", 232 | "[A. Rokem, J. Yeatman, F. Pestilli, K.N. Kay, A. Mezer, S. van der Walt, B.A. Wandell (2015). Evaluating the accuracy of diffusion MRI models in white matter. PLoS One, DOI: 10.1371/journal.pone.0123272](http://arokem.org/publications/papers/Rokem2015DWImodels.pdf)\n" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": { 239 | "collapsed": true 240 | }, 241 | "outputs": [], 242 | "source": [] 243 | } 244 | ], 245 | "metadata": { 246 | "kernelspec": { 247 | "display_name": "Python 3", 248 | "language": "python", 249 | "name": "python3" 250 | }, 251 | "language_info": { 252 | "codemirror_mode": { 253 | "name": "ipython", 254 | "version": 3 255 | }, 256 | "file_extension": ".py", 257 | "mimetype": "text/x-python", 258 | "name": "python", 259 | "nbconvert_exporter": "python", 260 | "pygments_lexer": "ipython3", 261 | "version": "3.6.1" 262 | } 263 | }, 264 | "nbformat": 4, 265 | "nbformat_minor": 2 266 | } 267 | -------------------------------------------------------------------------------- /index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This is the list of notebooks we will go through in this session:\n", 8 | "\n", 9 | "1. [notebooks](000-notebooks.ipynb)\n", 10 | "\n", 11 | "1. [arrays](001-arrays.ipynb)\n", 12 | "\n", 13 | "1. [plots](002-plots.ipynb)\n", 14 | "\n", 15 | "1. [calculations](003-calculations.ipynb)\n", 16 | "\n", 17 | "1. [interactions](004-interactions.ipynb)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": { 24 | "collapsed": true 25 | }, 26 | "outputs": [], 27 | "source": [] 28 | } 29 | ], 30 | "metadata": { 31 | "kernelspec": { 32 | "display_name": "Python 3", 33 | "language": "python", 34 | "name": "python3" 35 | }, 36 | "language_info": { 37 | "codemirror_mode": { 38 | "name": "ipython", 39 | "version": 3 40 | }, 41 | "file_extension": ".py", 42 | "mimetype": "text/x-python", 43 | "name": "python", 44 | "nbconvert_exporter": "python", 45 | "pygments_lexer": "ipython3", 46 | "version": "3.4.3" 47 | } 48 | }, 49 | "nbformat": 4, 50 | "nbformat_minor": 0 51 | } 52 | -------------------------------------------------------------------------------- /intro/001-python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Python Basics\n", 8 | "\n", 9 | "Prepared by: Ariel Rokem \n", 10 | "Thanks to: Matt Davis, Justin Kitzes (Software Carpentry)" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "## Welcome to the notebook! \n", 18 | "\n", 19 | "The notebook is made out of cells. This is a Markdown cell\n", 20 | "\n", 21 | "To execute a cell type Shift-Enter" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "__TIP:__ To quickly create a new cell below an existing one, type Ctrl-m then b.\n", 29 | "Other shortcuts for making, deleting, and moving cells are in the menubar on the left of the\n", 30 | "screen. To hide the menubar, click on the vertical gray divider separating it from this\n", 31 | "main page." 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "Simply type text " 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "Markdown can contain mathematical expressions written in latex\n", 46 | "\n", 47 | "\n", 48 | "$a=\\frac{1}{e^{-bD}}$" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": { 55 | "collapsed": false 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "# This is a code cell. \n", 60 | "# Comments are preceded by a hash sign ('#'), and are not interpreted at all.\n", 61 | "# You can also write code in this cell: \n", 62 | "# Type shift-enter to exectue the code" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## 1. Individual things\n", 70 | "\n", 71 | "The most basic component of any programming language are \"things\", also called variables or\n", 72 | "(in special cases) objects.\n", 73 | "\n", 74 | "The most common basic \"things\" in Python are integers, floats, strings, booleans, and\n", 75 | "some special objects of various types. We'll meet many of these as we go through the lesson." 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": { 82 | "collapsed": true 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "# A thing\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": { 93 | "collapsed": true 94 | }, 95 | "outputs": [], 96 | "source": [ 97 | "# Use print to show multiple things in the same cell\n", 98 | "# Note that you can use single or double quotes for strings\n" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": { 105 | "collapsed": true 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "# Things can be stored as variables\n" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": { 116 | "collapsed": true 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "# The type function tells us the type of thing we have\n" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": { 127 | "collapsed": false 128 | }, 129 | "outputs": [], 130 | "source": [] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "## 2. Commands that operate on things\n", 137 | "\n", 138 | "Just storing data in variables isn't much use to us. Right away, we'd like to start performing\n", 139 | "operations and manipulations on data and variables.\n", 140 | "\n", 141 | "There are three very common means of performing an operation on a thing." 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "### 2.1 Use an operator\n", 149 | "\n", 150 | "All of the basic math operators work like you think they should for numbers. They can also\n", 151 | "do some useful operations on other things, like strings. There are also boolean operators that\n", 152 | "compare quantities and give back a `bool` variable as a result." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": { 159 | "collapsed": true 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "# Standard math operators work as expected on numbers\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": { 170 | "collapsed": true 171 | }, 172 | "outputs": [], 173 | "source": [ 174 | "# Except for division\n", 175 | "# Also watch out for exponentiation - you want **, not ^" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": { 182 | "collapsed": true 183 | }, 184 | "outputs": [], 185 | "source": [ 186 | "# There are also operators for strings\n" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": { 193 | "collapsed": false 194 | }, 195 | "outputs": [], 196 | "source": [] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "collapsed": true 203 | }, 204 | "outputs": [], 205 | "source": [ 206 | "# Boolean operators compare two things\n" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "### 2.2 Use a function\n", 214 | "\n", 215 | "These will be very familiar to anyone who has programmed in any language, and work like you\n", 216 | "would expect." 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "metadata": { 223 | "collapsed": true 224 | }, 225 | "outputs": [], 226 | "source": [ 227 | "# There are thousands of functions that operate on things\n" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "__TIP:__ To find out what a function does, you can type it's name and then a question mark to\n", 235 | "get a pop up help window. Or, to see what arguments it takes, you can type its name, an open\n", 236 | "parenthesis, and hit tab." 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": { 243 | "collapsed": false 244 | }, 245 | "outputs": [], 246 | "source": [] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": { 252 | "collapsed": false 253 | }, 254 | "outputs": [], 255 | "source": [] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": { 261 | "collapsed": false 262 | }, 263 | "outputs": [], 264 | "source": [] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": { 270 | "collapsed": true 271 | }, 272 | "outputs": [], 273 | "source": [ 274 | "#round?\n" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "__TIP:__ Many useful functions are not in the Python built in library, but are in external\n", 282 | "scientific packages. These need to be imported into your Python notebook (or program) before\n", 283 | "they can be used. Probably the most important of these are numpy and matplotlib." 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": { 290 | "collapsed": true 291 | }, 292 | "outputs": [], 293 | "source": [ 294 | "# Many useful functions are in external packages\n", 295 | "# Let's meet numpy\n", 296 | "import numpy as np" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "metadata": { 303 | "collapsed": false 304 | }, 305 | "outputs": [], 306 | "source": [ 307 | "np" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "# To see what's in a package, type the name, a period, then hit tab\n", 319 | "#np?\n" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "metadata": { 326 | "collapsed": true 327 | }, 328 | "outputs": [], 329 | "source": [ 330 | "# Some examples of numpy functions and \"things\"\n" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": {}, 336 | "source": [ 337 | "### 2.3 Use a method\n", 338 | "\n", 339 | "Before we get any farther into the Python language, we have to say a word about \"objects\". We\n", 340 | "will not be teaching object oriented programming in this tutorial, but you will encounter objects\n", 341 | "throughout Python (in fact, even seemingly simple things like ints and strings are actually\n", 342 | "objects in Python).\n", 343 | "\n", 344 | "In the simplest terms, you can think of an object as a small bundled \"thing\" that contains within\n", 345 | "itself both data and functions that operate on that data. For example, strings in Python are\n", 346 | "objects that contain a set of characters and also various functions that operate on the set of\n", 347 | "characters. When bundled in an object, these functions are called \"methods\".\n", 348 | "\n", 349 | "Instead of the \"normal\" `function(arguments)` syntax, methods are called using the\n", 350 | "syntax `variable.method(arguments)`." 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": null, 356 | "metadata": { 357 | "collapsed": false 358 | }, 359 | "outputs": [], 360 | "source": [] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": { 366 | "collapsed": false 367 | }, 368 | "outputs": [], 369 | "source": [] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": null, 374 | "metadata": { 375 | "collapsed": true 376 | }, 377 | "outputs": [], 378 | "source": [ 379 | "# A string is actually an object\n" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": null, 385 | "metadata": { 386 | "collapsed": false 387 | }, 388 | "outputs": [], 389 | "source": [] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "metadata": { 395 | "collapsed": true 396 | }, 397 | "outputs": [], 398 | "source": [ 399 | "# Objects have bundled methods\n", 400 | "#a." 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "### EXERCISE 1 - Introducing logistic growth\n", 408 | "\n", 409 | "Throughout this lesson, we will successively build towards a program that will calculate the\n", 410 | "logistic growth of a population of bacteria in a petri dish (or bears in the woods, if you\n", 411 | "prefer). \n", 412 | "\n", 413 | "As a reminder, a commonly used discrete time equation for logistic population growth is\n", 414 | "\n", 415 | "$n[t+1] = n[t] + r \\cdot n[t] \\cdot (1 - \\frac{n[t]}{K})$\n", 416 | "\n", 417 | "where n(t) is the population size at time t, r is the net per capita growth rate, and K is the\n", 418 | "carrying capacity of the dish/woods.\n", 419 | "\n", 420 | "To get started, write Python expressions that do the following:\n", 421 | "\n", 422 | "1. Create variables for `r`, `K`, and `n0`, setting these equal to 0.6, 100, and 10, respectively.\n", 423 | "1. Create the variable `n1` and calculate it's value. Do the same for `n2`.\n", 424 | "1. Check the type of `n2` - what is it?\n", 425 | "1. Modify your calculations for `n1` and `n2` so that these values are rounded to the nearest\n", 426 | "integer.\n", 427 | "\n", 428 | "__Bonus__\n", 429 | "\n", 430 | "1. Test whether `n2` is larger than 20, and print out \"n2 more than 20: \"\n", 431 | "followed by the answer (either True or False).\n", 432 | "1. Figure out how to test whether `n2` is an integer (a mathematical integer, not necessarily\n", 433 | "whether it is an integer type) (HINT: look at the methods of `n2` by typing `n2.` and pressing\n", 434 | "tab.)" 435 | ] 436 | }, 437 | { 438 | "cell_type": "markdown", 439 | "metadata": {}, 440 | "source": [ 441 | "## 3. Collections of things\n", 442 | "\n", 443 | "Once the number of variables that you are interested in starts getting large, working with them\n", 444 | "all individually starts to get unwieldy. To help stay organized, we can use collections of things.\n", 445 | "\n", 446 | "Probably 99% of your work in scientific Python will use one of four types of collections:\n", 447 | "lists, tuples, dictionaries, and numpy arrays. We'll look quickly at each of these and what\n", 448 | "they can do for you." 449 | ] 450 | }, 451 | { 452 | "cell_type": "markdown", 453 | "metadata": {}, 454 | "source": [ 455 | "### 3.1 Lists\n", 456 | "\n", 457 | "Lists are probably the handiest and most flexible type of container. Lists are declared with\n", 458 | "square brackets []. Individual elements of a list can be selected using the syntax `a[ind]`." 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": null, 464 | "metadata": { 465 | "collapsed": true 466 | }, 467 | "outputs": [], 468 | "source": [ 469 | "# Lists are created with square bracket syntax\n" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": null, 475 | "metadata": { 476 | "collapsed": false 477 | }, 478 | "outputs": [], 479 | "source": [] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": null, 484 | "metadata": { 485 | "collapsed": true 486 | }, 487 | "outputs": [], 488 | "source": [ 489 | "# Lists (and all collections) are also indexed with square brackets\n", 490 | "# NOTE: The first index is zero, not one\n" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "metadata": { 497 | "collapsed": true 498 | }, 499 | "outputs": [], 500 | "source": [ 501 | "# Lists can be sliced by putting a colon between indexes\n", 502 | "# NOTE: Indexing is 0-based, and the end value is not inclusive\n" 503 | ] 504 | }, 505 | { 506 | "cell_type": "code", 507 | "execution_count": null, 508 | "metadata": { 509 | "collapsed": true 510 | }, 511 | "outputs": [], 512 | "source": [ 513 | "# You can leave off the start or end if desired\n" 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": null, 519 | "metadata": { 520 | "collapsed": false 521 | }, 522 | "outputs": [], 523 | "source": [] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": null, 528 | "metadata": { 529 | "collapsed": true 530 | }, 531 | "outputs": [], 532 | "source": [ 533 | "# Lists are objects, like everything else, and have methods such as append" 534 | ] 535 | }, 536 | { 537 | "cell_type": "code", 538 | "execution_count": null, 539 | "metadata": { 540 | "collapsed": false 541 | }, 542 | "outputs": [], 543 | "source": [] 544 | }, 545 | { 546 | "cell_type": "code", 547 | "execution_count": null, 548 | "metadata": { 549 | "collapsed": false 550 | }, 551 | "outputs": [], 552 | "source": [] 553 | }, 554 | { 555 | "cell_type": "markdown", 556 | "metadata": {}, 557 | "source": [ 558 | "__TIP:__ A 'gotcha' for some new Python users is that many collections, including lists,\n", 559 | "actually store pointers to data, not the data itself. This makes the language much more efficient,\n", 560 | "but can lead to some very hard to trace bugs..." 561 | ] 562 | }, 563 | { 564 | "cell_type": "code", 565 | "execution_count": null, 566 | "metadata": { 567 | "collapsed": true 568 | }, 569 | "outputs": [], 570 | "source": [ 571 | "# Assigning b = a does not make a copy of a, rather b and a now point to the same data\n" 572 | ] 573 | }, 574 | { 575 | "cell_type": "markdown", 576 | "metadata": {}, 577 | "source": [ 578 | "### EXERCISE 2 - Storing population size in a list\n", 579 | "\n", 580 | "Copy your code from Exercise 1 into the box below, and do the following:\n", 581 | "\n", 582 | "1. Modify your code so that the values of `n0`, `n1`, `n2`, and `n3` are stored in a list and not as\n", 583 | "separate individual variables. HINT: You can start off by declaring an empty list using the syntax\n", 584 | "`n = []`, and then append each new calculated value of `nt` to the list.\n", 585 | "1. Get the first and last values in the list, calculate their ratio, and print out \"Grew by a factor of \"\n", 586 | "followed by the result.\n", 587 | "\n", 588 | "__Bonus__\n", 589 | "\n", 590 | "1. Extract the last value in two different ways: first, by using the index for\n", 591 | "the last item in the list, and second, presuming that you do not know how long the list is.\n", 592 | "1. Change the values of `r` and `K` to make sure that your cell still runs correctly and gives\n", 593 | "reasonable answers." 594 | ] 595 | }, 596 | { 597 | "cell_type": "code", 598 | "execution_count": null, 599 | "metadata": { 600 | "collapsed": false 601 | }, 602 | "outputs": [], 603 | "source": [] 604 | }, 605 | { 606 | "cell_type": "code", 607 | "execution_count": null, 608 | "metadata": { 609 | "collapsed": false 610 | }, 611 | "outputs": [], 612 | "source": [] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": null, 617 | "metadata": { 618 | "collapsed": false 619 | }, 620 | "outputs": [], 621 | "source": [] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": null, 626 | "metadata": { 627 | "collapsed": true 628 | }, 629 | "outputs": [], 630 | "source": [] 631 | }, 632 | { 633 | "cell_type": "markdown", 634 | "metadata": {}, 635 | "source": [ 636 | "### 3.2 Tuples\n", 637 | "\n", 638 | "We won't say a whole lot about tuples except to mention that they basically work just like lists, with\n", 639 | "two major exceptions:\n", 640 | "\n", 641 | "1. You declare tuples using () instead of []\n", 642 | "1. Once you make a tuple, you can't change what's in it\n", 643 | "\n", 644 | "You'll see tuples come up throughout the Python language, and over time you'll develop a feel for when\n", 645 | "to use them. In general, they're often used instead of lists to group items when the position in the\n", 646 | "collection is critical to understanding the item's meaning, such as (x,y), and when you want to make\n", 647 | "sure that you don't accidentally modify any of the items later." 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": { 654 | "collapsed": false 655 | }, 656 | "outputs": [], 657 | "source": [] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": {}, 662 | "source": [ 663 | "### 3.3 Dictionaries\n", 664 | "\n", 665 | "Dictionaries are the collection to use when you want to store and retrieve things by their names\n", 666 | "(or some other kind of key) instead of by their position in the collection. A good example is a set\n", 667 | "of model parameters, each of which has a name and a value. Dictionaries are declared using {}." 668 | ] 669 | }, 670 | { 671 | "cell_type": "code", 672 | "execution_count": null, 673 | "metadata": { 674 | "collapsed": true 675 | }, 676 | "outputs": [], 677 | "source": [ 678 | "# Make a dictionary of model parameters\n" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": null, 684 | "metadata": { 685 | "collapsed": false 686 | }, 687 | "outputs": [], 688 | "source": [] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": {}, 693 | "source": [ 694 | "### 3.4 Numpy arrays (ndarrays)\n", 695 | "\n", 696 | "Even though numpy arrays (often written as ndarrays, for n-dimensional arrays) are not part of the\n", 697 | "core Python libraries, they are so useful in scientific Python that we'll include them here in the \n", 698 | "core lesson. Numpy arrays are collections of things, all of which must be the same type, that work\n", 699 | "similarly to lists (as we've described them so far). The most important are:\n", 700 | "\n", 701 | "1. You can easily perform elementwise operations (and matrix algebra) on arrays\n", 702 | "1. Arrays can be n-dimensional\n", 703 | "1. Arrays must be pre-allocated (ie, there is no equivalent to append)\n", 704 | "\n", 705 | "Arrays can be created from existing collections such as lists, or instantiated \"from scratch\" in a \n", 706 | "few useful ways.\n", 707 | "\n", 708 | "When getting started with scientific Python, you will probably want to try to use ndarrays whenever\n", 709 | "possible, saving the other types of collections for those cases when you have a specific reason to use\n", 710 | "them." 711 | ] 712 | }, 713 | { 714 | "cell_type": "code", 715 | "execution_count": null, 716 | "metadata": { 717 | "collapsed": false 718 | }, 719 | "outputs": [], 720 | "source": [ 721 | "import numpy as np" 722 | ] 723 | }, 724 | { 725 | "cell_type": "code", 726 | "execution_count": null, 727 | "metadata": { 728 | "collapsed": true 729 | }, 730 | "outputs": [], 731 | "source": [ 732 | "# Make an array from a list\n" 733 | ] 734 | }, 735 | { 736 | "cell_type": "code", 737 | "execution_count": null, 738 | "metadata": { 739 | "collapsed": false 740 | }, 741 | "outputs": [], 742 | "source": [] 743 | }, 744 | { 745 | "cell_type": "code", 746 | "execution_count": null, 747 | "metadata": { 748 | "collapsed": true 749 | }, 750 | "outputs": [], 751 | "source": [ 752 | "# Do arithmetic on arrays\n" 753 | ] 754 | }, 755 | { 756 | "cell_type": "code", 757 | "execution_count": null, 758 | "metadata": { 759 | "collapsed": true 760 | }, 761 | "outputs": [], 762 | "source": [ 763 | "# Boolean operators work on arrays too, and they return boolean arrays\n" 764 | ] 765 | }, 766 | { 767 | "cell_type": "code", 768 | "execution_count": null, 769 | "metadata": { 770 | "collapsed": true 771 | }, 772 | "outputs": [], 773 | "source": [ 774 | "# Indexing arrays\n" 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": null, 780 | "metadata": { 781 | "collapsed": true 782 | }, 783 | "outputs": [], 784 | "source": [ 785 | "# Arrays can also be indexed with other boolean arrays\n" 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": null, 791 | "metadata": { 792 | "collapsed": true 793 | }, 794 | "outputs": [], 795 | "source": [ 796 | "# ndarrays have attributes in addition to methods\n" 797 | ] 798 | }, 799 | { 800 | "cell_type": "code", 801 | "execution_count": null, 802 | "metadata": { 803 | "collapsed": true 804 | }, 805 | "outputs": [], 806 | "source": [ 807 | "# There are handy ways to make arrays full of ones and zeros\n" 808 | ] 809 | }, 810 | { 811 | "cell_type": "code", 812 | "execution_count": null, 813 | "metadata": { 814 | "collapsed": false 815 | }, 816 | "outputs": [], 817 | "source": [ 818 | "# You can also easily make arrays of number sequences\n" 819 | ] 820 | }, 821 | { 822 | "cell_type": "markdown", 823 | "metadata": {}, 824 | "source": [ 825 | "## 4. Repeating yourself\n", 826 | "\n", 827 | "So far, everything that we've done could, in principle, be done by hand calculation. In this section\n", 828 | "and the next, we really start to take advantage of the power of programming languages to do things\n", 829 | "for us automatically.\n", 830 | "\n", 831 | "We start here with ways to repeat yourself. The two most common ways of doing this are known as for\n", 832 | "loops and while loops. For loops in Python are useful when you want to cycle over all of the items\n", 833 | "in a collection (such as all of the elements of an array), and while loops are useful when you want to\n", 834 | "cycle for an indefinite amount of time until some condition is met.\n", 835 | "\n", 836 | "The basic examples below will work for looping over lists, tuples, and arrays. Looping over dictionaries\n", 837 | "is a bit different, since there is a key and a value for each item in a dictionary. Have a look at the\n", 838 | "Python docs for more information." 839 | ] 840 | }, 841 | { 842 | "cell_type": "code", 843 | "execution_count": null, 844 | "metadata": { 845 | "collapsed": true 846 | }, 847 | "outputs": [], 848 | "source": [ 849 | "# A basic for loop - don't forget the white space!\n" 850 | ] 851 | }, 852 | { 853 | "cell_type": "code", 854 | "execution_count": null, 855 | "metadata": { 856 | "collapsed": true 857 | }, 858 | "outputs": [], 859 | "source": [ 860 | "# Sum all of the values in a collection using a for loop\n" 861 | ] 862 | }, 863 | { 864 | "cell_type": "code", 865 | "execution_count": null, 866 | "metadata": { 867 | "collapsed": true 868 | }, 869 | "outputs": [], 870 | "source": [ 871 | "# Often we want to loop over the indexes of a collection, not just the items\n" 872 | ] 873 | }, 874 | { 875 | "cell_type": "code", 876 | "execution_count": null, 877 | "metadata": { 878 | "collapsed": true 879 | }, 880 | "outputs": [], 881 | "source": [ 882 | "# While loops are useful when you don't know how many steps you will need,\n", 883 | "# and want to stop once a certain condition is met.\n", 884 | "\n" 885 | ] 886 | }, 887 | { 888 | "cell_type": "code", 889 | "execution_count": null, 890 | "metadata": { 891 | "collapsed": false 892 | }, 893 | "outputs": [], 894 | "source": [ 895 | " " 896 | ] 897 | }, 898 | { 899 | "cell_type": "markdown", 900 | "metadata": {}, 901 | "source": [ 902 | "__TIP:__ Once we start really generating useful and large collections of data, it becomes unwieldy to\n", 903 | "inspect our results manually. The code below shows how to make a very simple plot of an array.\n", 904 | "We'll do much more plotting later on, this is just to get started." 905 | ] 906 | }, 907 | { 908 | "cell_type": "code", 909 | "execution_count": null, 910 | "metadata": { 911 | "collapsed": false 912 | }, 913 | "outputs": [], 914 | "source": [ 915 | "# Load up pylab, a useful plotting library. This is an IPython magic: \n", 916 | "%pylab inline\n", 917 | "# Make some x and y data and plot it" 918 | ] 919 | }, 920 | { 921 | "cell_type": "code", 922 | "execution_count": null, 923 | "metadata": { 924 | "collapsed": false 925 | }, 926 | "outputs": [], 927 | "source": [] 928 | }, 929 | { 930 | "cell_type": "markdown", 931 | "metadata": {}, 932 | "source": [ 933 | "### EXERCISE 4 - Using loops to repeat calculations\n", 934 | "\n", 935 | "FINALLY, let's get smart about our calculations of `nt`. Copy your code from Exercise 3 into the box\n", 936 | "below, and do the following:\n", 937 | "\n", 938 | "1. Write a for loop to fill in the values of `nt` for 100 time steps. HINT: You will need to \n", 939 | "create an array of the step numbers using a command like `step = np.arange(1, 100)`. (Why does\n", 940 | "this array start at 1 and not at 0?). Then, loop over the values of the step array,\n", 941 | "and use each step value to index the array `n`.\n", 942 | "1. Plot the array `n`.\n", 943 | "1. Play around with the values of `r` and `K` and see how it changes the plot. What happens if you set\n", 944 | "`r` to 1.9 or 3?\n", 945 | "\n", 946 | "__Bonus__\n", 947 | "\n", 948 | "1. Modify your code to use a while loop that will stop your calculation once the population size is\n", 949 | "greater than 90. HINT: Start a step counter `i` at 1, and check that the value in `n[i-1]` is less than 90\n", 950 | "each time around the loop. Increment the step counter within the loop so that you have a record\n", 951 | "of what step the calculation stopped at." 952 | ] 953 | }, 954 | { 955 | "cell_type": "code", 956 | "execution_count": null, 957 | "metadata": { 958 | "collapsed": true 959 | }, 960 | "outputs": [], 961 | "source": [] 962 | }, 963 | { 964 | "cell_type": "markdown", 965 | "metadata": {}, 966 | "source": [ 967 | "## 5. Making choices\n", 968 | "\n", 969 | "Often we want to check if a condition is True and take one action if it is, and another action if the\n", 970 | "condition is False. We can achieve this in Python with an if statement.\n", 971 | "\n", 972 | "__TIP:__ You can use any expression that returns a boolean value (True or False) in an if statement.\n", 973 | "Common boolean operators are ==, !=, <, <=, >, >=. You can also use `is` and `is not` if you want to\n", 974 | "check if two variables are identical in the sense that they are stored in the same location in memory." 975 | ] 976 | }, 977 | { 978 | "cell_type": "code", 979 | "execution_count": null, 980 | "metadata": { 981 | "collapsed": true 982 | }, 983 | "outputs": [], 984 | "source": [ 985 | "# A simple if statement\n" 986 | ] 987 | }, 988 | { 989 | "cell_type": "code", 990 | "execution_count": null, 991 | "metadata": { 992 | "collapsed": true 993 | }, 994 | "outputs": [], 995 | "source": [ 996 | "# If statements can rely on boolean variables\n" 997 | ] 998 | }, 999 | { 1000 | "cell_type": "code", 1001 | "execution_count": null, 1002 | "metadata": { 1003 | "collapsed": false 1004 | }, 1005 | "outputs": [], 1006 | "source": [] 1007 | }, 1008 | { 1009 | "cell_type": "markdown", 1010 | "metadata": {}, 1011 | "source": [ 1012 | "### EXERCISE 5 - Making the model stochastic with an if statement\n", 1013 | "\n", 1014 | "Deterministic models are boring, so let's introduce some element of randomness into our logistic\n", 1015 | "growth model. We'll model a simple \"catastrophe\" process, in which a catastrophe happens in 10% of the\n", 1016 | "time steps that reduces the population back down to the size at n0. Copy your code from Exercise 4\n", 1017 | "into the box below, and do the following:\n", 1018 | "\n", 1019 | "1. Inside your for loop, add a variable called `cata`, for catastrophe, that will be True if a catastrophe\n", 1020 | "has occurred, and False if it hasn't. A simple way to do this is to generate a random number using\n", 1021 | "`np.random.rand()`, which will give you a pseudorandom number between 0 and 1. Check whether this number\n", 1022 | "is less than 0.1 - this check will be True 10% of the time.\n", 1023 | "1. Using your boolean variable `cata`, add an if statement to your for loop that checks whether `cat` is\n", 1024 | "true in each time step. If it is true, set the population back to the size at n[0]. Otherwise, perform\n", 1025 | "the usual logistic growth calculation." 1026 | ] 1027 | }, 1028 | { 1029 | "cell_type": "code", 1030 | "execution_count": null, 1031 | "metadata": { 1032 | "collapsed": false 1033 | }, 1034 | "outputs": [], 1035 | "source": [] 1036 | }, 1037 | { 1038 | "cell_type": "code", 1039 | "execution_count": null, 1040 | "metadata": { 1041 | "collapsed": false 1042 | }, 1043 | "outputs": [], 1044 | "source": [ 1045 | "counter" 1046 | ] 1047 | }, 1048 | { 1049 | "cell_type": "markdown", 1050 | "metadata": {}, 1051 | "source": [ 1052 | "## 6. Creating chunks with functions and modules\n", 1053 | "\n", 1054 | "One way to write a program is to simply string together commands, like the ones described above, in a long\n", 1055 | "file, and then to run that file to generate your results. This may work, but it can be cognitively difficult\n", 1056 | "to follow the logic of programs written in this style. Also, it does not allow you to reuse your code\n", 1057 | "easily - for example, what if we wanted to run our logistic growth model for several different choices of\n", 1058 | "initial parameters?\n", 1059 | "\n", 1060 | "The most important ways to \"chunk\" code into more manageable pieces is to create functions and then\n", 1061 | "to gather these functions into modules, and eventually packages. Below we will discuss how to create\n", 1062 | "functions and modules. A third common type of \"chunk\" in Python is classes, but we will not be covering\n", 1063 | "object-oriented programming in this workshop." 1064 | ] 1065 | }, 1066 | { 1067 | "cell_type": "code", 1068 | "execution_count": null, 1069 | "metadata": { 1070 | "collapsed": true 1071 | }, 1072 | "outputs": [], 1073 | "source": [ 1074 | "# We've been using functions all day\n" 1075 | ] 1076 | }, 1077 | { 1078 | "cell_type": "code", 1079 | "execution_count": null, 1080 | "metadata": { 1081 | "collapsed": true 1082 | }, 1083 | "outputs": [], 1084 | "source": [ 1085 | "# It's very easy to write your own functions\n" 1086 | ] 1087 | }, 1088 | { 1089 | "cell_type": "code", 1090 | "execution_count": null, 1091 | "metadata": { 1092 | "collapsed": true 1093 | }, 1094 | "outputs": [], 1095 | "source": [ 1096 | "# Once a function is \"run\" and saved in memory, it's available just like any other function\n" 1097 | ] 1098 | }, 1099 | { 1100 | "cell_type": "code", 1101 | "execution_count": null, 1102 | "metadata": { 1103 | "collapsed": false 1104 | }, 1105 | "outputs": [], 1106 | "source": [] 1107 | }, 1108 | { 1109 | "cell_type": "code", 1110 | "execution_count": null, 1111 | "metadata": { 1112 | "collapsed": true 1113 | }, 1114 | "outputs": [], 1115 | "source": [ 1116 | "# It's useful to include docstrings to describe what your function does" 1117 | ] 1118 | }, 1119 | { 1120 | "cell_type": "code", 1121 | "execution_count": null, 1122 | "metadata": { 1123 | "collapsed": true 1124 | }, 1125 | "outputs": [], 1126 | "source": [] 1127 | }, 1128 | { 1129 | "cell_type": "code", 1130 | "execution_count": null, 1131 | "metadata": { 1132 | "collapsed": true 1133 | }, 1134 | "outputs": [], 1135 | "source": [ 1136 | "# All arguments must be present, or the function will return an error\n" 1137 | ] 1138 | }, 1139 | { 1140 | "cell_type": "code", 1141 | "execution_count": null, 1142 | "metadata": { 1143 | "collapsed": true 1144 | }, 1145 | "outputs": [], 1146 | "source": [ 1147 | "# Keyword arguments can be used to make some arguments optional by giving them a default value\n", 1148 | "# All mandatory arguments must come first, in order\n" 1149 | ] 1150 | }, 1151 | { 1152 | "cell_type": "markdown", 1153 | "metadata": {}, 1154 | "source": [ 1155 | "### EXERCISE 6 - Creating a logistic growth function\n", 1156 | "\n", 1157 | "Finally, let's turn our logistic growth model into a function that we can use over and over again. \n", 1158 | "Copy your code from Exercise 5 into the box below, and do the following:\n", 1159 | "\n", 1160 | "1. Turn your code into a function called `logistic_growth` that takes four arguments: `r`, `K`, `n0`,\n", 1161 | "and `p` (the probability of a catastrophe). Make `p` a keyword argument with a default value of 0.1.\n", 1162 | "Have your function return the `n` array.\n", 1163 | "1. Write a nice docstring describing what your function does.\n", 1164 | "1. In a subsequent cell, call your function with different values of the parameters to make sure it works.\n", 1165 | "Store the returned value of `n` and make a plot from it." 1166 | ] 1167 | }, 1168 | { 1169 | "cell_type": "markdown", 1170 | "metadata": {}, 1171 | "source": [ 1172 | "### Putting the growth function(s) in a module\n", 1173 | "\n", 1174 | "We can make our functions more easily reusable by placing them into modules that we can import, just\n", 1175 | "like we have been doing with `numpy`. It's pretty simple to do this.\n", 1176 | "\n", 1177 | "1. Copy your function(s) from Exercise 6 into a new text file, in the same directory as this notebook,\n", 1178 | "called `pop.py`.\n", 1179 | "1. In the cell below, type `import pop` to import the module. Type `pop.` and hit tab to see the available\n", 1180 | "functions in the module. Try running the logistic growth function that was imported from the module with\n", 1181 | "a few different parameter values." 1182 | ] 1183 | }, 1184 | { 1185 | "cell_type": "code", 1186 | "execution_count": null, 1187 | "metadata": { 1188 | "collapsed": false 1189 | }, 1190 | "outputs": [], 1191 | "source": [] 1192 | } 1193 | ], 1194 | "metadata": { 1195 | "kernelspec": { 1196 | "display_name": "Python 3", 1197 | "language": "python", 1198 | "name": "python3" 1199 | }, 1200 | "language_info": { 1201 | "codemirror_mode": { 1202 | "name": "ipython", 1203 | "version": 3 1204 | }, 1205 | "file_extension": ".py", 1206 | "mimetype": "text/x-python", 1207 | "name": "python", 1208 | "nbconvert_exporter": "python", 1209 | "pygments_lexer": "ipython3", 1210 | "version": "3.4.3" 1211 | } 1212 | }, 1213 | "nbformat": 4, 1214 | "nbformat_minor": 0 1215 | } 1216 | -------------------------------------------------------------------------------- /intro/003-object-oriented-python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Object-oriented programming in Python\n", 8 | "\n", 9 | "Object-oriented programming is powerful programming paradigm that allows us to build complext abstractions and to systematically relate different abstractions to each other.\n", 10 | "It enhances the reusability of ideas and makes your code, if the abstractions are the right ones (...) more powerful and more consistent.\n", 11 | "Through introspection, It also lets your data-structures tell you about themselves. " 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "Here is Steve Jobs' explanation of object-oriented programming in a [1994 Rolling Stones interview](http://www.rollingstone.com/culture/news/steve-jobs-in-1994-the-rolling-stone-interview-20110117):\n", 19 | "\n", 20 | " Jeff Goodell: Would you explain, in simple terms, exactly what object-oriented software is?\n", 21 | " \n", 22 | " Steve Jobs: Objects are like people. They're living, breathing things that have knowledge inside them about \n", 23 | " how to do things and have memory inside them so they can remember things. And rather than interacting with \n", 24 | " them at a very low level, you interact with them at a very high level of abstraction, like we're doing right \n", 25 | " here.\n", 26 | "\n", 27 | " Here's an example: If I'm your laundry object, you can give me your dirty clothes and send me a message that \n", 28 | " says, \"Can you get my clothes laundered, please.\" I happen to know where the best laundry place in \n", 29 | " San Francisco is. And I speak English, and I have dollars in my pockets. So I go out and hail a taxicab and \n", 30 | " tell the driver to take me to this place in San Francisco. I go get your clothes laundered, I jump back in the \n", 31 | " cab, I get back here. I give you your clean clothes and say, \"Here are your clean clothes.\"\n", 32 | "\n", 33 | " You have no idea how I did that. You have no knowledge of the laundry place. Maybe you speak French, and you \n", 34 | " can't even hail a taxi. You can't pay for one, you don't have dollars in your pocket. Yet I knew how to do all \n", 35 | " of that. And you didn't have to know any of it. All that complexity was hidden inside of me, and we were able to \n", 36 | " interact at a very high level of abstraction. That's what objects are. They encapsulate complexity, and the \n", 37 | " interfaces to that complexity are high level." 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "## Building classes \n", 45 | "\n", 46 | "We'll implement a simple set of classes that can be used to do geometry. A\n", 47 | "class is defined using the `class` key-word: " 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 1, 53 | "metadata": { 54 | "collapsed": false 55 | }, 56 | "outputs": [], 57 | "source": [ 58 | "class Point():\n", 59 | " \"\"\"A class for representing points in the planes \"\"\" \n", 60 | " def __init__(self, x, y):\n", 61 | " \"\"\" \n", 62 | " Initialize a Point class instance\n", 63 | " \n", 64 | " ... more here ...\n", 65 | " \"\"\"\n", 66 | " self.x = x\n", 67 | " self.y = y" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "p1 = Point(10, 10)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [ 88 | { 89 | "data": { 90 | "text/plain": [ 91 | "10" 92 | ] 93 | }, 94 | "execution_count": 3, 95 | "metadata": {}, 96 | "output_type": "execute_result" 97 | } 98 | ], 99 | "source": [ 100 | "p1.x" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "Here, we've defined the x and y coordinates of this point on the plain. The\n", 108 | "variables `x` and `y` are called 'attributes' of each instance of this\n", 109 | "class. We can define functions that use this class:" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 4, 115 | "metadata": { 116 | "collapsed": false 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "# Since we're using numpy here, let's import it: \n", 121 | "import numpy as np\n", 122 | "\n", 123 | "def distance(p1, p2):\n", 124 | " return np.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)\n", 125 | " \n", 126 | " " 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 5, 132 | "metadata": { 133 | "collapsed": false 134 | }, 135 | "outputs": [], 136 | "source": [ 137 | "p2 = Point(0,0)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 6, 143 | "metadata": { 144 | "collapsed": false 145 | }, 146 | "outputs": [ 147 | { 148 | "data": { 149 | "text/plain": [ 150 | "14.142135623730951" 151 | ] 152 | }, 153 | "execution_count": 6, 154 | "metadata": {}, 155 | "output_type": "execute_result" 156 | } 157 | ], 158 | "source": [ 159 | "distance(p1, p2)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Consider writing a test of this here... " 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "Alternatively, you can define the distance function as a 'method' of the class. These are special attributes, that are also functions:" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 7, 179 | "metadata": { 180 | "collapsed": false 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "class Point():\n", 185 | " \"\"\"A class for representing points in the planes \"\"\" \n", 186 | " def __init__(self, x, y):\n", 187 | " \"\"\" \n", 188 | " Initialize a Point class instance\n", 189 | " \n", 190 | " ... more here ...\n", 191 | " \"\"\"\n", 192 | " self.x = x\n", 193 | " self.y = y\n", 194 | " \n", 195 | " def distance(self, p2):\n", 196 | " \"\"\"Calculate the distance to another Point\n", 197 | " \n", 198 | " Parameters\n", 199 | " ----------\n", 200 | " p2 : Point class instance\n", 201 | " \n", 202 | " \"\"\"\n", 203 | " return np.sqrt((self.x - p2.x)**2 + (self.y - p2.y)**2)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "This object knows how to compute the distance between itself and other objects of the same kind" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 8, 216 | "metadata": { 217 | "collapsed": false 218 | }, 219 | "outputs": [ 220 | { 221 | "data": { 222 | "text/plain": [ 223 | "14.142135623730951" 224 | ] 225 | }, 226 | "execution_count": 8, 227 | "metadata": {}, 228 | "output_type": "execute_result" 229 | } 230 | ], 231 | "source": [ 232 | "p1 = Point(10, 10)\n", 233 | "p2 = Point(0, 0)\n", 234 | "\n", 235 | "p1.distance(p2)" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "Other objects can be built using this object. For example: " 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 9, 248 | "metadata": { 249 | "collapsed": false 250 | }, 251 | "outputs": [], 252 | "source": [ 253 | "class Circle():\n", 254 | " \"\"\"Represent circles\"\"\" \n", 255 | " def __init__(self, center_x, center_y, radius):\n", 256 | " \"\"\" \n", 257 | " Initialize a Point class instance\n", 258 | " \"\"\"\n", 259 | " self.center = Point(center_x, center_y)\n", 260 | " self.radius = radius\n", 261 | " \n", 262 | " def area(self):\n", 263 | " return np.pi * self.radius ** 2\n", 264 | "\n", 265 | " def circumference(self):\n", 266 | " return 2 * np.pi * self.radius\n", 267 | " \n", 268 | " def distance(self, c2):\n", 269 | " \"\"\" Calculates the center-to-center distance between two circles \"\"\"\n", 270 | " return self.center.distance(c2.center)\n", 271 | " " 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "Alternatively, you can build objects through inherticance: \n", 279 | "\n", 280 | "\n" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 10, 286 | "metadata": { 287 | "collapsed": false 288 | }, 289 | "outputs": [], 290 | "source": [ 291 | "class FancyPoint(Point):\n", 292 | " def __init__(self, x, y, color):\n", 293 | " \"\"\"A colorful point\"\"\" \n", 294 | " Point.__init__(self, x, y)\n", 295 | "\n", 296 | " self.R = color[0]\n", 297 | " self.G = color[1]\n", 298 | " self.B = color[2]\n", 299 | " \n", 300 | " def color_distance(self, fp2):\n", 301 | " \"\"\" \n", 302 | " Calculate the distance between FancyPoint objects in RGB space\n", 303 | " \"\"\" \n", 304 | " return np.sqrt((self.R - fp2.R)**2 + (self.G - fp2.G)**2 +(self.B - fp2.B)**2)" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 11, 310 | "metadata": { 311 | "collapsed": false 312 | }, 313 | "outputs": [], 314 | "source": [ 315 | "fp1 = FancyPoint(10, 10, [0, 0, 1])\n", 316 | "fp2 = FancyPoint(0, 0, [1, 1, 1])" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 12, 322 | "metadata": { 323 | "collapsed": false 324 | }, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "1.4142135623730951" 330 | ] 331 | }, 332 | "execution_count": 12, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | } 336 | ], 337 | "source": [ 338 | "fp1.color_distance(fp2)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "## When should we inherit? \n", 346 | "\n", 347 | "If you are creating a new class, the basic question you should ask is:\n", 348 | "\n", 349 | "** Is my new class \"an X\" , or does it \"have an X\"? ** \n", 350 | "\n", 351 | "If the answer is the former, you can inherit from X. Otherwise, you should just have X be an attribute of your new class." 352 | ] 353 | } 354 | ], 355 | "metadata": { 356 | "kernelspec": { 357 | "display_name": "Python 2", 358 | "language": "python", 359 | "name": "python2" 360 | }, 361 | "language_info": { 362 | "codemirror_mode": { 363 | "name": "ipython", 364 | "version": 2 365 | }, 366 | "file_extension": ".py", 367 | "mimetype": "text/x-python", 368 | "name": "python", 369 | "nbconvert_exporter": "python", 370 | "pygments_lexer": "ipython2", 371 | "version": "2.7.10" 372 | } 373 | }, 374 | "nbformat": 4, 375 | "nbformat_minor": 0 376 | } 377 | -------------------------------------------------------------------------------- /intro/circle.py: -------------------------------------------------------------------------------- 1 | """ 2 | circle.py 3 | 4 | This is what it does 5 | 6 | """ 7 | 8 | import numpy as np 9 | 10 | FACTOR = 1000. 11 | 12 | 13 | def area(r): 14 | return np.pi * (r ** 2) 15 | 16 | 17 | def circumference(r): 18 | return np.pi * 2 * r 19 | 20 | 21 | class Circle(): 22 | 23 | -------------------------------------------------------------------------------- /intro/test_circle.py: -------------------------------------------------------------------------------- 1 | import circle 2 | import numpy as np 3 | import numpy.testing as npt 4 | 5 | def test_area(): 6 | npt.assert_equal(circle.area(1), np.pi) 7 | npt.assert_almost_equal(circle.area(np.sqrt(np.pi)) , (np.pi ** 2)) 8 | 9 | -------------------------------------------------------------------------------- /intro/testing.md: -------------------------------------------------------------------------------- 1 | ## Testing python code with nosetests 2 | 3 | Testing serves several purposes: 4 | 5 | 1. Be sure your code runs. 6 | 1. Be sure it does the right thing. 7 | 1. Be sure not to break it when you make new things. 8 | 9 | We will focus on 'unit tests'. These are tests that consider units of code as 10 | independent items to be tested. Ideally, no reads from disk, or from a network 11 | are required. Other kinds of tests are 'end-to-end' tests, which exercise a 12 | full pipeline of analysis 13 | 14 | ## Installing nose: 15 | 16 | We will use the testing framework `nose`, which is also used by many of the 17 | open source scientific software libraries (`numpy`/`scipy`/etc). With Anaconda 18 | installed, installing `nose` should be as simple as: 19 | 20 | conda install nose 21 | 22 | Alternatively, you can also use the `pip` package manager: 23 | 24 | pip install nose 25 | 26 | Note that if `pip` is unavaliable you can install that via a : `conda install pip`. You get the picture. 27 | 28 | ## The `assert` statement 29 | 30 | Before we start running `nose`, let's consider what software tests should 31 | do. For now let's consider just the most generic case: a testing suite 32 | should run every statement in your code, and check that the resulting variables 33 | have the `correct` value. against: 34 | 35 | 1. A result calculated using pencil and paper 36 | 1. A result calculated using an alternative (e.g. slower, but safer) method 37 | 1. A result calculated with a previous version of the software that you have 38 | some reason to believe is true (this is called 'regression testing'). 39 | 40 | Thus, testing software relies on making specific assertions about what you 41 | expect the code to do. This uses the python `assert` statement. This statement 42 | evaluates the Truth of an expression, and then raises an error if the statement 43 | is false: 44 | 45 | 46 | a = 1 47 | b = 1 48 | c = 2 49 | assert a == b # nothing happens 50 | assert b == c # raises error 51 | 52 | If you want, you can raise an informative error: 53 | 54 | assert b == c, "%s is not equal to %s"%(b,c) 55 | 56 | To use this as a test for your analysis code, consider the following function 57 | to calculate the area of a circle: 58 | 59 | def area(r): 60 | return np.pi * (r ** 2) 61 | 62 | We can assert our expectation that the area of a circle with radius of 63 | 1 will be equal to pi in the following python statement 64 | 65 | assert area(1) == np.pi 66 | 67 | We might also expect the area of a circle with radius sqrt(pi) to be 68 | equal to pi squared: 69 | 70 | assert area(np.sqrt(np.p)) == np.pi ** 2 71 | 72 | But this raises an AssertionError. Why? This demonstrates the limitations that 73 | computers have in representing real numbers. Often our calculations will be 74 | approximations, or will be susceptible to this kind of floating point error. 75 | Instead of requiring strict equality, we can instead state what level of 76 | precision we can guarantee: 77 | 78 | assert np.abs(area(np.sqrt(np.pi))-(np.pi ** 2)) < 10e-15 79 | 80 | This means that even when you are not able to provide an *exact* answer, you 81 | can still test that your answer is *close enough*. What *enough* means, depends 82 | on the specifics of your analysis. A particle physicist might require precision 83 | to 15 significant digits, but most biologists are happy if they can get the 84 | sign right... 85 | 86 | ## Using `nose` to automate tests: 87 | 88 | The `nosetests` application seeks files named `test_*.py` in the file paths 89 | below the pwd, and runs every function in these files that is defined as `def 90 | test_*()`. If all these functions run with no error, and no failed assertions, 91 | then nose reports back with the number of functions that have been 92 | run. Otherwise, errors and assertion failures are reported. 93 | 94 | Let's examine the simple module `circle.py` and its test file `test_circle.py` 95 | as an example of this. 96 | 97 | ## 98 | -------------------------------------------------------------------------------- /neuroimaging/000-notebooks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Author: [**Ariel Rokem**](http://arokem.org), [**The University of Washington eScience Institute**](http://escience.washington.edu)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### What is this thing?\n" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "This is an [IPython](http://ipython.org) notebook. It is a format for writing, sharing and presenting technical documents, such as scientific analysis. Each section in this notebook is caled a 'cell'. The current cell is designated as a *markdown* cell. This means, that it is meant to show mostly text. The [markdown format](https://help.github.com/articles/markdown-basics/) is used by many to document software and other things, and allows some light annotations, to add things like links, images, section headings, etc.\n", 22 | "\n", 23 | "When you hit shift-return, this cell gets rendered. \n", 24 | "\n", 25 | "The following cell is a code cell. When you hit shift-return in that cell, the code in that cell gets executed" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 1, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "a = 1" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "That wasn't too sophisticated, but notice that as long as the kernel is running, that variable persists. For example, the following cell does what you might expect." 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": { 50 | "collapsed": false 51 | }, 52 | "outputs": [ 53 | { 54 | "name": "stdout", 55 | "output_type": "stream", 56 | "text": [ 57 | "1\n" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "print(a)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "We'll get to plotting in a little while, but for now let me just show you that you can embed figures in the notebook, by setting a magic command: `%matplotlib inline`. " 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 3, 75 | "metadata": { 76 | "collapsed": true 77 | }, 78 | "outputs": [], 79 | "source": [ 80 | "%matplotlib inline" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "If you run that, the plots that you create will get embedded into the notebook. See the following cell for a simple example (we'll explain all of this example in a later section)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [ 97 | { 98 | "data": { 99 | "text/plain": [ 100 | "[]" 101 | ] 102 | }, 103 | "execution_count": 4, 104 | "metadata": {}, 105 | "output_type": "execute_result" 106 | }, 107 | { 108 | "data": { 109 | "image/png": "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\nQmCC\n", 110 | "text/plain": [ 111 | "" 112 | ] 113 | }, 114 | "metadata": {}, 115 | "output_type": "display_data" 116 | } 117 | ], 118 | "source": [ 119 | "import matplotlib.pyplot as plt\n", 120 | "plt.plot([0, 1, 2, 3, 4, 5], [0, 2, 4, 8, 16, 32])" 121 | ] 122 | } 123 | ], 124 | "metadata": { 125 | "kernelspec": { 126 | "display_name": "Python 3", 127 | "language": "python", 128 | "name": "python3" 129 | }, 130 | "language_info": { 131 | "codemirror_mode": { 132 | "name": "ipython", 133 | "version": 3 134 | }, 135 | "file_extension": ".py", 136 | "mimetype": "text/x-python", 137 | "name": "python", 138 | "nbconvert_exporter": "python", 139 | "pygments_lexer": "ipython3", 140 | "version": "3.4.3" 141 | } 142 | }, 143 | "nbformat": 4, 144 | "nbformat_minor": 0 145 | } 146 | -------------------------------------------------------------------------------- /neuroimaging/001-arrays.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Author: [**Ariel Rokem**](http://arokem.org), [**The University of Washington eScience Institute**](http://escience.washington.edu)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### Reading neuroimaging data into an array\n", 15 | "\n", 16 | "When you conduct a neuroimaging experiment, the computer that controls the scanner and receives the data from the scanner saves your data to a file. Neuroimaging data appears in many different file formats: `NiFTI`, `Minc`, `Dicom`, etc. These files all contain representations of the data that you collected in the form of an **array**. \n", 17 | "\n", 18 | "What is an array? It is a way of representing the data in the computer memory as a *table*, that is *multi-dimensional* and *homogenous*.\n", 19 | "\n", 20 | "What does this mean? \n", 21 | "\n", 22 | "- *table* means that you will be able to read all or some of the numbers representing your data by addressing the variable that holds your array. It's like addressing a member of your lab to tell you the answer to a question you have, except here you are going to 'ask' a variable in your computer memory. Arrays are usually not as smart as your lab members, but they have very good memory.\n", 23 | "\n", 24 | "- *multi-dimensional* means that you can represent different aspects of your data along different axes. For example, the three dimensions of space can be represented in different dimensions of the table:" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "![Arrays](./images/array.svg)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "- *homogenous* actually means two different things: \n", 39 | " - The shape of the array is homogenous, so if there are three items in the first column, there have to be three items in all the columns. \n", 40 | " - The data-type is homogenous. If the first item is an integer, all the other items will be integers as well." 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "To demonstrate the properties of arrays, we will use the [`numpy`](https://numpy.org) library. This library contains implementations of many scientifically useful functions and objects. In particular, it contains an implementation of arrays that we will use throughout the folllowing examples." 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 1, 53 | "metadata": { 54 | "collapsed": true 55 | }, 56 | "outputs": [], 57 | "source": [ 58 | "import numpy as np" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 2, 64 | "metadata": { 65 | "collapsed": true 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "# Numpy is a package. To see what's in a package, type the name, a period, then hit tab\n", 70 | "#np?\n", 71 | "#np." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 3, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "2.0\n", 86 | "3.14159265359\n", 87 | "1.22464679915e-16\n" 88 | ] 89 | } 90 | ], 91 | "source": [ 92 | "# Some examples of numpy functions and \"things\":\n", 93 | "print(np.sqrt(4))\n", 94 | "print(np.pi) # Not a function, just a variable\n", 95 | "print(np.sin(np.pi)) # A function on a variable :) " 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "### Numpy arrays (ndarrays)\n", 103 | "\n", 104 | "Creating a NumPy array is as simple as passing a sequence to `np.array` " 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 4, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "\n", 119 | "float64\n", 120 | "(3,)\n" 121 | ] 122 | } 123 | ], 124 | "source": [ 125 | "arr1 = np.array([1, 2.3, 4]) \n", 126 | "print(type(arr1))\n", 127 | "print(arr1.dtype)\n", 128 | "print(arr1.shape)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 5, 134 | "metadata": { 135 | "collapsed": false 136 | }, 137 | "outputs": [ 138 | { 139 | "name": "stdout", 140 | "output_type": "stream", 141 | "text": [ 142 | "[ 1. 2.3 4. ]\n" 143 | ] 144 | } 145 | ], 146 | "source": [ 147 | "print(arr1)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "### You can create arrays with special generating functions: \n", 155 | "\n", 156 | "`np.arange(start, stop, [step])`\n", 157 | "\n", 158 | "`np.zeros(shape)`\n", 159 | "\n", 160 | "`np.ones(shape)`" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 6, 166 | "metadata": { 167 | "collapsed": false 168 | }, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "[2 3 4]\n", 175 | "[1 3]\n", 176 | "[1 3 5 7 9]\n" 177 | ] 178 | } 179 | ], 180 | "source": [ 181 | "arr4 = np.arange(2, 5)\n", 182 | "print(arr4)\n", 183 | "arr5 = np.arange(1, 5, 2)\n", 184 | "print(arr5)\n", 185 | "arr6 = np.arange(1, 10, 2)\n", 186 | "print(arr6)" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": { 192 | "collapsed": true 193 | }, 194 | "source": [ 195 | "## Exercise : Create an Array\n", 196 | "\n", 197 | "Create an array with values ranging from 0 to 10, in increments of 0.5.\n", 198 | "\n", 199 | "Reminder: get help by typing np.arange?, np.ndarray?, np.array?, etc." 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "### Arithmetic with arrays\n", 207 | "\n", 208 | "Since numpy exists to perform efficient numerical operations in Python, arrays have all the usual arithmetic operations available to them. These operations are performed element-wise (i.e. the same operation is performed independently on each element of the array)." 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 8, 214 | "metadata": { 215 | "collapsed": false 216 | }, 217 | "outputs": [ 218 | { 219 | "ename": "ValueError", 220 | "evalue": "operands could not be broadcast together with shapes (5,) (6,) ", 221 | "output_type": "error", 222 | "traceback": [ 223 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 224 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 225 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mB\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m11\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mB\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mB\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 226 | "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (5,) (6,) " 227 | ] 228 | } 229 | ], 230 | "source": [ 231 | "A = np.arange(5)\n", 232 | "B = np.arange(5, 11)\n", 233 | "\n", 234 | "print (A+B)\n", 235 | "\n", 236 | "print(B-A)\n", 237 | "\n", 238 | "print(A*B)" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "### What would happen if A and B did not have the same `shape`?" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "### Arithmetic with scalars:\n", 253 | "\n", 254 | "In addition, if one of the arguments is a scalar, that value will be applied to all the elements of the array." 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": 9, 260 | "metadata": { 261 | "collapsed": false 262 | }, 263 | "outputs": [ 264 | { 265 | "name": "stdout", 266 | "output_type": "stream", 267 | "text": [ 268 | "[10 11 12 13 14]\n", 269 | "[0 2 4 6 8]\n", 270 | "[ 0 1 4 9 16]\n" 271 | ] 272 | } 273 | ], 274 | "source": [ 275 | "A = np.arange(5)\n", 276 | "print(A+10)\n", 277 | "print(2*A)\n", 278 | "print(A**2)" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "### Arrays are addressed through indexing\n", 286 | "\n", 287 | "**Python uses zero-based indexing**: The first item in the array is item `0`\n", 288 | "\n", 289 | "The second item is item `1`, the third is item `2`, etc." 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 10, 295 | "metadata": { 296 | "collapsed": false 297 | }, 298 | "outputs": [ 299 | { 300 | "name": "stdout", 301 | "output_type": "stream", 302 | "text": [ 303 | "[0 1 2 3 4]\n", 304 | "0\n", 305 | "1\n", 306 | "2\n" 307 | ] 308 | } 309 | ], 310 | "source": [ 311 | "print(A)\n", 312 | "print(A[0])\n", 313 | "print(A[1])\n", 314 | "print(A[2])" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "### `numpy` contains various functions for calculations on arrays" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": 11, 327 | "metadata": { 328 | "collapsed": false 329 | }, 330 | "outputs": [ 331 | { 332 | "name": "stdout", 333 | "output_type": "stream", 334 | "text": [ 335 | "[ 1. 2.71828183 7.3890561 20.08553692 54.59815003]\n", 336 | "2.0\n" 337 | ] 338 | } 339 | ], 340 | "source": [ 341 | "# This gets the exponent, element-wise:\n", 342 | "print(np.exp(A))\n", 343 | "\n", 344 | "# This is the average number in the entire array:\n", 345 | "print(np.mean(A))" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "### Data in Nifti files is stored as an array \n", 353 | "\n", 354 | "In the tutorial directory, we have included a single run of an fMRI experiment that was included in the FIAC competition. The experiment is described in full in a paper by Dehaene-Lambertz et al. (2006), but for the purposes of what we do today, the exact details of the acquisition and the task are not particularly important.\n", 355 | "\n", 356 | "We can read out this array into the computer memory using the `nibabel` library" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 12, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "import nibabel as nib" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "Loading the file is simple:" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 13, 380 | "metadata": { 381 | "collapsed": false 382 | }, 383 | "outputs": [], 384 | "source": [ 385 | "img = nib.load('./data/run1.nii.gz')" 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "But note that in order to save time and memory, nibabel is pretty lazy about reading data from file, until we really need this data. \n", 393 | "\n", 394 | "Meaning that at this point, we've only read information *about* the data, not the data itself. This thing is not the data array yet. What is it then? " 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 14, 400 | "metadata": { 401 | "collapsed": false 402 | }, 403 | "outputs": [ 404 | { 405 | "data": { 406 | "text/plain": [ 407 | "nibabel.nifti1.Nifti1Image" 408 | ] 409 | }, 410 | "execution_count": 14, 411 | "metadata": {}, 412 | "output_type": "execute_result" 413 | } 414 | ], 415 | "source": [ 416 | "type(img)" 417 | ] 418 | }, 419 | { 420 | "cell_type": "markdown", 421 | "metadata": {}, 422 | "source": [ 423 | "It's a `Nifti1Image` object! That means that it is a variable that holds various attributes of the data. For example, the 4 by 4 matrix that describes the spatial transformation between the world coordinates and the image coordinates" 424 | ] 425 | }, 426 | { 427 | "cell_type": "code", 428 | "execution_count": 15, 429 | "metadata": { 430 | "collapsed": true 431 | }, 432 | "outputs": [], 433 | "source": [ 434 | "hdr = img.get_header()" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": 17, 440 | "metadata": { 441 | "collapsed": false 442 | }, 443 | "outputs": [ 444 | { 445 | "name": "stdout", 446 | "output_type": "stream", 447 | "text": [ 448 | " object, endian='<'\n", 449 | "sizeof_hdr : 348\n", 450 | "data_type : \n", 451 | "db_name : \n", 452 | "extents : 0\n", 453 | "session_error : 0\n", 454 | "regular : r\n", 455 | "dim_info : 0\n", 456 | "dim : [ 4 64 64 30 191 1 1 1]\n", 457 | "intent_p1 : 0.0\n", 458 | "intent_p2 : 0.0\n", 459 | "intent_p3 : 0.0\n", 460 | "intent_code : none\n", 461 | "datatype : int16\n", 462 | "bitpix : 16\n", 463 | "slice_start : 0\n", 464 | "pixdim : [ 1. 3.00000238 3.00000381 4. 1. 1. 1.\n", 465 | " 1. ]\n", 466 | "vox_offset : 0.0\n", 467 | "scl_slope : nan\n", 468 | "scl_inter : nan\n", 469 | "slice_end : 0\n", 470 | "slice_code : unknown\n", 471 | "xyzt_units : 0\n", 472 | "cal_max : 0.0\n", 473 | "cal_min : 0.0\n", 474 | "slice_duration : 0.0\n", 475 | "toffset : 0.0\n", 476 | "glmax : 0\n", 477 | "glmin : 0\n", 478 | "descrip : 4.EPI-FIAC\n", 479 | "aux_file : \n", 480 | "qform_code : scanner\n", 481 | "sform_code : unknown\n", 482 | "quatern_b : 0.0\n", 483 | "quatern_c : 0.0\n", 484 | "quatern_d : 1.0\n", 485 | "qoffset_x : 0.0\n", 486 | "qoffset_y : 0.0\n", 487 | "qoffset_z : 0.0\n", 488 | "srow_x : [ 0. 0. 0. 0.]\n", 489 | "srow_y : [ 0. 0. 0. 0.]\n", 490 | "srow_z : [ 0. 0. 0. 0.]\n", 491 | "intent_name : \n", 492 | "magic : n+1\n" 493 | ] 494 | } 495 | ], 496 | "source": [ 497 | "print(hdr)" 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "execution_count": 18, 503 | "metadata": { 504 | "collapsed": false 505 | }, 506 | "outputs": [ 507 | { 508 | "data": { 509 | "text/plain": [ 510 | "array([[-3.00000238, 0. , 0. , 0. ],\n", 511 | " [ 0. , -3.00000381, 0. , 0. ],\n", 512 | " [ 0. , 0. , 4. , 0. ],\n", 513 | " [ 0. , 0. , 0. , 1. ]])" 514 | ] 515 | }, 516 | "execution_count": 18, 517 | "metadata": {}, 518 | "output_type": "execute_result" 519 | } 520 | ], 521 | "source": [ 522 | "img.affine" 523 | ] 524 | }, 525 | { 526 | "cell_type": "markdown", 527 | "metadata": {}, 528 | "source": [ 529 | "This object also has functions. You can get the data, by calling a function of that object:" 530 | ] 531 | }, 532 | { 533 | "cell_type": "markdown", 534 | "metadata": {}, 535 | "source": [ 536 | "There's a header in there that provides some additional information:\n" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 93, 542 | "metadata": { 543 | "collapsed": false 544 | }, 545 | "outputs": [ 546 | { 547 | "name": "stdout", 548 | "output_type": "stream", 549 | "text": [ 550 | "(3.0000024, 3.0000038, 4.0, 1.0)\n" 551 | ] 552 | } 553 | ], 554 | "source": [ 555 | "hdr = img.get_header()\n", 556 | "print(hdr.get_zooms())" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 19, 562 | "metadata": { 563 | "collapsed": false 564 | }, 565 | "outputs": [ 566 | { 567 | "name": "stdout", 568 | "output_type": "stream", 569 | "text": [ 570 | "\n" 571 | ] 572 | } 573 | ], 574 | "source": [ 575 | "data = img.get_data()\n", 576 | "print(type(data))" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": 22, 582 | "metadata": { 583 | "collapsed": false 584 | }, 585 | "outputs": [ 586 | { 587 | "name": "stdout", 588 | "output_type": "stream", 589 | "text": [ 590 | "(64, 64, 30, 191)\n", 591 | "float64\n" 592 | ] 593 | } 594 | ], 595 | "source": [ 596 | "print(data.shape)\n", 597 | "print(data.dtype)" 598 | ] 599 | }, 600 | { 601 | "cell_type": "markdown", 602 | "metadata": {}, 603 | "source": [ 604 | "This is a 4-dimensional array! We happen to know that time is the last dimension, and there are 191 TRs recorded in this data. There are 30 slices in each TR/volume, with an inplane matrix of 64 by 64 in each slice. " 605 | ] 606 | }, 607 | { 608 | "cell_type": "markdown", 609 | "metadata": {}, 610 | "source": [ 611 | "We can easily access different parts of the data. Here is the full time-series for the central voxel in the volume:" 612 | ] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": 21, 617 | "metadata": { 618 | "collapsed": false 619 | }, 620 | "outputs": [ 621 | { 622 | "name": "stdout", 623 | "output_type": "stream", 624 | "text": [ 625 | "[ 3881. 3886. 4052. 3995. 3963. 3910. 3908. 3899. 3922. 3854.\n", 626 | " 3903. 3812. 4041. 3896. 4037. 3857. 3981. 3901. 3931. 3867.\n", 627 | " 3999. 4002. 3948. 4091. 3915. 3929. 3888. 3925. 3955. 3854.\n", 628 | " 4023. 3861. 4011. 3864. 3736. 3961. 4019. 3790. 4041. 3941.\n", 629 | " 4005. 3952. 3924. 3952. 3942. 3895. 3907. 4001. 3892. 4000.\n", 630 | " 3835. 3823. 3922. 3792. 4021. 3841. 3972. 3804. 3837. 3851.\n", 631 | " 3922. 3996. 3899. 3816. 3793. 3703. 3995. 3848. 3917. 3790.\n", 632 | " 3828. 3978. 3833. 3726. 3950. 3868. 3812. 3910. 3882. 3847.\n", 633 | " 3829. 3882. 3804. 3735. 3969. 3778. 3702. 3788. 3851. 3921.\n", 634 | " 3769. 3836. 3794. 3884. 4060. 3883. 3810. 3785. 3995. 3926.\n", 635 | " 3775. 3834. 3883. 3909. 3850. 3896. 3844. 3716. 3879. 4015.\n", 636 | " 3903. 3893. 3760. 3931. 3876. 3986. 3809. 3835. 3949. 3874.\n", 637 | " 3772. 3896. 3875. 3706. 3838. 3839. 3882. 4007. 3803. 3854.\n", 638 | " 3795. 3780. 3793. 3728. 3783. 3828. 3742. 3760. 3672. 3913.\n", 639 | " 3649. 3924. 3679. 3735. 3702. 3717. 3742. 3856. 3696. 3834.\n", 640 | " 3616. 3797. 3745. 3773. 3652. 3637. 3793. 3764. 3756. 3761.\n", 641 | " 3851. 3717. 3686. 3825. 3772. 3793. 3617. 3791. 3824. 3769.\n", 642 | " 3713. 3616. 3921. 3597. 3646. 3581. 3670. 3742. 3605. 3665.\n", 643 | " 3765. 3753. 3646. 3692. 3734. 3748. 3590. 3787. 3651. 3671.\n", 644 | " 3695.]\n", 645 | "(191,)\n" 646 | ] 647 | } 648 | ], 649 | "source": [ 650 | "center_voxel_time_series = data[32, 32, 15, :]\n", 651 | "print(center_voxel_time_series)\n", 652 | "print(center_voxel_time_series.shape)" 653 | ] 654 | }, 655 | { 656 | "cell_type": "markdown", 657 | "metadata": {}, 658 | "source": [ 659 | "It's a one-dimensional array! Here is the middle slice for the last time-point" 660 | ] 661 | }, 662 | { 663 | "cell_type": "code", 664 | "execution_count": 27, 665 | "metadata": { 666 | "collapsed": false 667 | }, 668 | "outputs": [ 669 | { 670 | "name": "stdout", 671 | "output_type": "stream", 672 | "text": [ 673 | "[[ 19. 54. 59. ..., 44. 52. 57.]\n", 674 | " [ 27. 32. 12. ..., 19. 17. 65.]\n", 675 | " [ 28. 46. 85. ..., 69. 58. 74.]\n", 676 | " ..., \n", 677 | " [ 25. 63. 5. ..., 66. 31. 56.]\n", 678 | " [ 47. 69. 35. ..., 70. 72. 40.]\n", 679 | " [ 28. 38. 33. ..., 14. 53. 16.]]\n", 680 | "(64, 64)\n" 681 | ] 682 | } 683 | ], 684 | "source": [ 685 | "middle_slice_t0 = data[:, :, 15, -1] # Using negative numbers allows you to count *from the end*\n", 686 | "print(middle_slice_t0)\n", 687 | "print(middle_slice_t0.shape)" 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": {}, 693 | "source": [ 694 | "That's a 2D array. You get the picture, I hope.\n", 695 | "\n", 696 | "You can do all kinds of operations with the data using functions:" 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "execution_count": 28, 702 | "metadata": { 703 | "collapsed": false 704 | }, 705 | "outputs": [ 706 | { 707 | "name": "stdout", 708 | "output_type": "stream", 709 | "text": [ 710 | "3839.65968586\n", 711 | "110.300135033\n", 712 | "34.8110152785\n" 713 | ] 714 | } 715 | ], 716 | "source": [ 717 | "print(np.mean(center_voxel_time_series))\n", 718 | "print(np.std(center_voxel_time_series))\n", 719 | "# TSNR is mean/std:\n", 720 | "print(np.mean(center_voxel_time_series)/np.std(center_voxel_time_series))" 721 | ] 722 | }, 723 | { 724 | "cell_type": "markdown", 725 | "metadata": {}, 726 | "source": [ 727 | "### Using functions on parts of the data\n", 728 | "\n", 729 | "Many `numpy` functions have an `axis` optional argument. These arguments allow you to perform a reduction of the data along one of the dimensions of the array.\n", 730 | "\n", 731 | "For example, if you want to extract a 3D array with the mean/std in every one of the voxels:" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 29, 737 | "metadata": { 738 | "collapsed": false 739 | }, 740 | "outputs": [ 741 | { 742 | "data": { 743 | "text/plain": [ 744 | "1039.0608225451299" 745 | ] 746 | }, 747 | "execution_count": 29, 748 | "metadata": {}, 749 | "output_type": "execute_result" 750 | } 751 | ], 752 | "source": [ 753 | "np.mean(data)" 754 | ] 755 | }, 756 | { 757 | "cell_type": "code", 758 | "execution_count": 43, 759 | "metadata": { 760 | "collapsed": false 761 | }, 762 | "outputs": [ 763 | { 764 | "name": "stdout", 765 | "output_type": "stream", 766 | "text": [ 767 | "(64, 64, 30)\n" 768 | ] 769 | } 770 | ], 771 | "source": [ 772 | "mean_tseries = np.mean(data, axis=-1) # Select the last dimension\n", 773 | "std_tseries = np.std(data, axis=-1)\n", 774 | "tsnr = mean_tseries/std_tseries\n", 775 | "print(mean_tseries.shape)" 776 | ] 777 | }, 778 | { 779 | "cell_type": "code", 780 | "execution_count": 100, 781 | "metadata": { 782 | "collapsed": false 783 | }, 784 | "outputs": [ 785 | { 786 | "name": "stdout", 787 | "output_type": "stream", 788 | "text": [ 789 | "(64, 64, 30)\n" 790 | ] 791 | } 792 | ], 793 | "source": [ 794 | "print(tsnr.shape)" 795 | ] 796 | }, 797 | { 798 | "cell_type": "markdown", 799 | "metadata": {}, 800 | "source": [ 801 | "You can save the resulting array into a new file:" 802 | ] 803 | }, 804 | { 805 | "cell_type": "code", 806 | "execution_count": 44, 807 | "metadata": { 808 | "collapsed": true 809 | }, 810 | "outputs": [], 811 | "source": [ 812 | "new_img = nib.Nifti1Image(tsnr, img.affine)" 813 | ] 814 | }, 815 | { 816 | "cell_type": "code", 817 | "execution_count": 48, 818 | "metadata": { 819 | "collapsed": true 820 | }, 821 | "outputs": [], 822 | "source": [ 823 | "new_img.to_filename('tsnr.nii.gz')" 824 | ] 825 | }, 826 | { 827 | "cell_type": "code", 828 | "execution_count": 50, 829 | "metadata": { 830 | "collapsed": false 831 | }, 832 | "outputs": [ 833 | { 834 | "data": { 835 | "text/plain": [ 836 | "['AnalyzeHeader',\n", 837 | " 'AnalyzeImage',\n", 838 | " 'FileHolder',\n", 839 | " 'FileHolderError',\n", 840 | " 'MGHImage',\n", 841 | " 'Minc1Image',\n", 842 | " 'Minc2Image',\n", 843 | " 'MincImage',\n", 844 | " 'Nifti1Header',\n", 845 | " 'Nifti1Image',\n", 846 | " 'Nifti1Pair',\n", 847 | " 'Nifti2Header',\n", 848 | " 'Nifti2Image',\n", 849 | " 'Nifti2Pair',\n", 850 | " 'OrientationError',\n", 851 | " 'Spm2AnalyzeHeader',\n", 852 | " 'Spm2AnalyzeImage',\n", 853 | " 'Spm99AnalyzeHeader',\n", 854 | " 'Spm99AnalyzeImage',\n", 855 | " '_ModuleProxy',\n", 856 | " '__builtins__',\n", 857 | " '__doc__',\n", 858 | " '__file__',\n", 859 | " '__name__',\n", 860 | " '__package__',\n", 861 | " '__path__',\n", 862 | " '__version__',\n", 863 | " '_get_pkg_info',\n", 864 | " 'aff2axcodes',\n", 865 | " 'affines',\n", 866 | " 'ana',\n", 867 | " 'analyze',\n", 868 | " 'apply_orientation',\n", 869 | " 'arrayproxy',\n", 870 | " 'arraywriters',\n", 871 | " 'as_closest_canonical',\n", 872 | " 'batteryrunners',\n", 873 | " 'bench',\n", 874 | " 'casting',\n", 875 | " 'class_map',\n", 876 | " 'concat_images',\n", 877 | " 'deprecated',\n", 878 | " 'ecat',\n", 879 | " 'eulerangles',\n", 880 | " 'ext_map',\n", 881 | " 'externals',\n", 882 | " 'fileholders',\n", 883 | " 'filename_parser',\n", 884 | " 'fileslice',\n", 885 | " 'flip_axis',\n", 886 | " 'four_to_three',\n", 887 | " 'freesurfer',\n", 888 | " 'funcs',\n", 889 | " 'get_info',\n", 890 | " 'imageclasses',\n", 891 | " 'imageglobals',\n", 892 | " 'info',\n", 893 | " 'io_orientation',\n", 894 | " 'is_proxy',\n", 895 | " 'keywordonly',\n", 896 | " 'load',\n", 897 | " 'loadsave',\n", 898 | " 'minc',\n", 899 | " 'minc1',\n", 900 | " 'minc2',\n", 901 | " 'mriutils',\n", 902 | " 'ni1',\n", 903 | " 'nifti1',\n", 904 | " 'nifti2',\n", 905 | " 'openers',\n", 906 | " 'optpkg',\n", 907 | " 'orientation_affine',\n", 908 | " 'orientations',\n", 909 | " 'os',\n", 910 | " 'parrec',\n", 911 | " 'pkg_info',\n", 912 | " 'py3k',\n", 913 | " 'quaternions',\n", 914 | " 'save',\n", 915 | " 'spatialimages',\n", 916 | " 'spm2',\n", 917 | " 'spm2analyze',\n", 918 | " 'spm99',\n", 919 | " 'spm99analyze',\n", 920 | " 'squeeze_image',\n", 921 | " 'test',\n", 922 | " 'trackvis',\n", 923 | " 'tripwire',\n", 924 | " 'volumeutils',\n", 925 | " 'wrapstruct']" 926 | ] 927 | }, 928 | "execution_count": 50, 929 | "metadata": {}, 930 | "output_type": "execute_result" 931 | } 932 | ], 933 | "source": [ 934 | "dir(nib)" 935 | ] 936 | }, 937 | { 938 | "cell_type": "code", 939 | "execution_count": null, 940 | "metadata": { 941 | "collapsed": true 942 | }, 943 | "outputs": [], 944 | "source": [] 945 | } 946 | ], 947 | "metadata": { 948 | "kernelspec": { 949 | "display_name": "Python 2", 950 | "language": "python", 951 | "name": "python2" 952 | }, 953 | "language_info": { 954 | "codemirror_mode": { 955 | "name": "ipython", 956 | "version": 2 957 | }, 958 | "file_extension": ".py", 959 | "mimetype": "text/x-python", 960 | "name": "python", 961 | "nbconvert_exporter": "python", 962 | "pygments_lexer": "ipython2", 963 | "version": "2.7.10" 964 | } 965 | }, 966 | "nbformat": 4, 967 | "nbformat_minor": 0 968 | } 969 | -------------------------------------------------------------------------------- /neuroimaging/data/run1.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/data/run1.nii.gz -------------------------------------------------------------------------------- /neuroimaging/data/someones_anatomy.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/data/someones_anatomy.nii.gz -------------------------------------------------------------------------------- /neuroimaging/data/someones_epi.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/data/someones_epi.nii.gz -------------------------------------------------------------------------------- /neuroimaging/images/array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/images/array.png -------------------------------------------------------------------------------- /neuroimaging/images/fourier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/images/fourier.png -------------------------------------------------------------------------------- /neuroimaging/images/illustrating_affine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/images/illustrating_affine.png -------------------------------------------------------------------------------- /neuroimaging/images/localizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacarpentry/python-neuroimaging-lesson/50704d4cfb46097f31fb35742af1741849a2c8ab/neuroimaging/images/localizer.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | nibabel --------------------------------------------------------------------------------