├── testmpi.py ├── test_ring1.py ├── test_ring2.py ├── ring.py ├── ballandstick.py ├── ball-and-stick-4.ipynb ├── ball-and-stick-3.ipynb └── pythontutorial.ipynb /testmpi.py: -------------------------------------------------------------------------------- 1 | from neuron import h 2 | h.nrnmpi_init() # initialize MPI 3 | pc = h.ParallelContext() 4 | print('I am {} of {}'.format(pc.id(), pc.nhost())) 5 | h.quit() 6 | -------------------------------------------------------------------------------- /test_ring1.py: -------------------------------------------------------------------------------- 1 | from neuron import h 2 | from neuron.units import ms, mV 3 | import matplotlib.pyplot as plt 4 | from ring import Ring 5 | 6 | cell_to_plot = 0 7 | 8 | ring = Ring() 9 | 10 | pc = h.ParallelContext() 11 | pc.set_maxstep(10 * ms) 12 | 13 | t = h.Vector().record(h._ref_t) 14 | h.finitialize(-65 * mV) 15 | pc.psolve(100 * ms) 16 | 17 | if pc.gid_exists(cell_to_plot): 18 | plt.figure() 19 | plt.plot(t, pc.gid2cell(cell_to_plot).soma_v) 20 | plt.show() 21 | 22 | pc.barrier() 23 | pc.done() 24 | h.quit() -------------------------------------------------------------------------------- /test_ring2.py: -------------------------------------------------------------------------------- 1 | from neuron import h 2 | from neuron.units import ms, mV 3 | import matplotlib.pyplot as plt 4 | from ring import Ring 5 | 6 | ring = Ring() 7 | 8 | pc = h.ParallelContext() 9 | pc.set_maxstep(10 * ms) 10 | 11 | t = h.Vector().record(h._ref_t) 12 | h.finitialize(-65 * mV) 13 | pc.psolve(100 * ms) 14 | 15 | # send all spike time data to node 0 16 | local_data = {cell._gid: list(cell.spike_times) for cell in ring.cells} 17 | all_data = pc.py_alltoall([local_data] + [None] * (pc.nhost() - 1)) 18 | 19 | if pc.id() == 0: 20 | # combine the data from the various processes 21 | data = {} 22 | for process_data in all_data: 23 | data.update(process_data) 24 | # plot it 25 | plt.figure() 26 | for i, spike_times in data.items(): 27 | plt.vlines(spike_times, i + 0.5, i + 1.5) 28 | plt.show() 29 | 30 | pc.barrier() 31 | pc.done() 32 | h.quit() -------------------------------------------------------------------------------- /ring.py: -------------------------------------------------------------------------------- 1 | from neuron import h 2 | from ballandstick import BallAndStick 3 | 4 | ### MPI must be initialized before we create a ParallelContext object 5 | h.nrnmpi_init() 6 | pc = h.ParallelContext() 7 | 8 | class Ring: 9 | """A network of *N* ball-and-stick cells where cell n makes an 10 | excitatory synapse onto cell n + 1 and the last, Nth cell in the 11 | network projects to the first cell. 12 | """ 13 | def __init__(self, N=5, stim_w=0.04, stim_t=9, stim_delay=1, syn_w=0.01, syn_delay=5, r=50): 14 | """ 15 | :param N: Number of cells. 16 | :param stim_w: Weight of the stimulus 17 | :param stim_t: time of the stimulus (in ms) 18 | :param stim_delay: delay of the stimulus (in ms) 19 | :param syn_w: Synaptic weight 20 | :param syn_delay: Delay of the synapse 21 | :param r: radius of the network 22 | """ 23 | self._N = N 24 | self.set_gids() ### assign gids to processors 25 | self._syn_w = syn_w 26 | self._syn_delay = syn_delay 27 | self._create_cells(r) ### changed to use self._N instead of passing in N 28 | self._connect_cells() 29 | ### the 0th cell only exists on one process... that's the only one that gets a netstim 30 | if pc.gid_exists(0): 31 | self._netstim = h.NetStim() 32 | self._netstim.number = 1 33 | self._netstim.start = stim_t 34 | self._nc = h.NetCon(self._netstim, pc.gid2cell(0).syn) ### grab cell with gid==0 wherever it exists 35 | self._nc.delay = stim_delay 36 | self._nc.weight[0] = stim_w 37 | 38 | def set_gids(self): 39 | """Set the gidlist on this host.""" 40 | #### Round-robin counting. 41 | #### Each host has an id from 0 to pc.nhost() - 1. 42 | self.gidlist = list(range(pc.id(), self._N, pc.nhost())) 43 | for gid in self.gidlist: 44 | pc.set_gid2node(gid, pc.id()) 45 | 46 | def _create_cells(self, r): 47 | self.cells = [] 48 | for i in self.gidlist: ### only create the cells that exist on this host 49 | theta = i * 2 * h.PI / self._N 50 | self.cells.append(BallAndStick(i, h.cos(theta) * r, h.sin(theta) * r, 0, theta)) 51 | ### associate the cell with this host and gid 52 | for cell in self.cells: 53 | pc.cell(cell._gid, cell._spike_detector) 54 | 55 | def _connect_cells(self): 56 | ### this method is different because we now must use ids instead of objects 57 | for target in self.cells: 58 | source_gid = (target._gid - 1 + self._N) % self._N 59 | nc = pc.gid_connect(source_gid, target.syn) 60 | nc.weight[0] = self._syn_w 61 | nc.delay = self._syn_delay 62 | target._ncs.append(nc) -------------------------------------------------------------------------------- /ballandstick.py: -------------------------------------------------------------------------------- 1 | from neuron import h, gui 2 | from neuron.units import ms, mV 3 | h.load_file('stdrun.hoc') 4 | 5 | class Cell: 6 | def __init__(self, gid, x, y, z, theta): 7 | self._gid = gid 8 | self._setup_morphology() 9 | self.all = self.soma.wholetree() 10 | self._setup_biophysics() 11 | self.x = self.y = self.z = 0 12 | h.define_shape() 13 | self._rotate_z(theta) 14 | self._set_position(x, y, z) 15 | 16 | # everything below here in this method is NEW 17 | self._spike_detector = h.NetCon(self.soma(0.5)._ref_v, None, sec=self.soma) 18 | self.spike_times = h.Vector() 19 | self._spike_detector.record(self.spike_times) 20 | 21 | self._ncs = [] 22 | 23 | self.soma_v = h.Vector().record(self.soma(0.5)._ref_v) 24 | 25 | 26 | def __repr__(self): 27 | return '{}[{}]'.format(self.name, self._gid) 28 | 29 | def _set_position(self, x, y, z): 30 | for sec in self.all: 31 | for i in range(sec.n3d()): 32 | sec.pt3dchange(i, 33 | x - self.x + sec.x3d(i), 34 | y - self.y + sec.y3d(i), 35 | z - self.z + sec.z3d(i), 36 | sec.diam3d(i)) 37 | self.x, self.y, self.z = x, y, z 38 | 39 | def _rotate_z(self, theta): 40 | """Rotate the cell about the Z axis.""" 41 | for sec in self.all: 42 | for i in range(sec.n3d()): 43 | x = sec.x3d(i) 44 | y = sec.y3d(i) 45 | c = h.cos(theta) 46 | s = h.sin(theta) 47 | xprime = x * c - y * s 48 | yprime = x * s + y * c 49 | sec.pt3dchange(i, xprime, yprime, sec.z3d(i), sec.diam3d(i)) 50 | 51 | 52 | class BallAndStick(Cell): 53 | name = 'BallAndStick' 54 | 55 | def _setup_morphology(self): 56 | self.soma = h.Section(name='soma', cell=self) 57 | self.dend = h.Section(name='dend', cell=self) 58 | self.dend.connect(self.soma) 59 | self.soma.L = self.soma.diam = 12.6157 60 | self.dend.L = 200 61 | self.dend.diam = 1 62 | 63 | def _setup_biophysics(self): 64 | for sec in self.all: 65 | sec.Ra = 100 # Axial resistance in Ohm * cm 66 | sec.cm = 1 # Membrane capacitance in micro Farads / cm^2 67 | self.soma.insert('hh') 68 | for seg in self.soma: 69 | seg.hh.gnabar = 0.12 # Sodium conductance in S/cm2 70 | seg.hh.gkbar = 0.036 # Potassium conductance in S/cm2 71 | seg.hh.gl = 0.0003 # Leak conductance in S/cm2 72 | seg.hh.el = -54.3 # Reversal potential in mV 73 | # Insert passive current in the dendrite 74 | self.dend.insert('pas') 75 | for seg in self.dend: 76 | seg.pas.g = 0.001 # Passive conductance in S/cm2 77 | seg.pas.e = -65 # Leak reversal potential mV 78 | 79 | # NEW: the synapse 80 | self.syn = h.ExpSyn(self.dend(0.5)) 81 | self.syn.tau = 2 * ms 82 | 83 | -------------------------------------------------------------------------------- /ball-and-stick-4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This is the final part of the series where we build a multicompartment cell and evolve it into a network of cells running on a parallel machine (which is basically all computers made within the last decade). On this page, we translate the classes we have previously constructed so that they operate in either a parallel or serial mode." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Note: If you followed the Quick Start instructions on the NEURON website, you should have everything you need to run parallel simulations. If not, if you do not already have an MPI installation, go to that link and follow the instructions in \"Step 4: Install MPI to enable parallel simulations.\" If you compiled NEURON yourself instead of using an installer (this is almost-never necessary), this part of the tutorial requires you to have used the `--with-paranrn` flag at configure time." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "

Parallel communication in NEURON

" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "Parallel communication takes place via logical events in network connection objects known as NetCon. NetCon sources are threshold detectors. They monitor some variable, say the membrane potential of a cell, and when the variable reaches some threshold, it triggers an event sent to the targets. Targets are frequently synapses on other cells. When they receive the event, they activate.\n", 29 | "\n", 30 | "In a parallel context across several machines, communication between hosts can be computationally inefficient when the frequency of events is high and when the message being sent is large. NEURON uses an efficient priority queueing mechanism to deliver events to targets after the delay specified by the NetCon. The message passed is succinct. It is an integer, the unique global identifier (gid) of the source. The following two figures illustrate these ideas and come from Hines M.L. and Carnevale N.T, Translating network models to parallel hardware in NEURON, Journal of Neuroscience Methods 169 (2008) 425–455. Users should also consult the ParallelContext reference." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "
" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "
" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "The main step involved in making a parallel implementation is to assign the global identifiers across the various hosts. Care should also be taken to assign cells to the various hosts such that the system is load balanced. For example, in a network with computationally complex and simple cells, several simple cells may be assigned to a host while few complex cells may be assigned to another host." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "

Test MPI and Parallel NEURON

" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "Once again, parallel NEURON requires MPI support. If this is your first time using it in a while, you should test your computer setup." 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "Create a file called `testmpi.py`:" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "from neuron import h\n", 82 | "h.nrnmpi_init() # initialize MPI\n", 83 | "pc = h.ParallelContext()\n", 84 | "print('I am {} of {}'.format(pc.id(), pc.nhost()))\n", 85 | "h.quit() # necessary to avoid a warning message on parallel exit on some systems" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "Run the script in parallel via e.g. `mpiexec -n 4 python testmpi.py` from the command line in a terminal. You should see output resembling:" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "
\n",
100 |     "numprocs=4\n",
101 |     "I am 1 of 4\n",
102 |     "I am 2 of 4\n",
103 |     "I am 3 of 4\n",
104 |     "I am 0 of 4\n",
105 |     "
" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "These could appear in any order since in theory they are running simultaneously but must print out in some order. If instead you see four processes claiming to be 0 of 1, then your copy of NEURON was not compiled with support for parallel simulation. Reconfigure with the `--with-paranrn` flag, recompile, and try again. If you get an error saying that `mpiexec` is an unknown command, then MPI is either not installed or not on your PATH; correct your MPI setup and try again." 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "

Back to the example

" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "Begin by downloading ballandstick.py into your working directory. This is equivalent to the classes we created in the previous part of the tutorial." 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "We will construct a `ring.py` based on the previous `Ring` class. Changes are indicated with `###`:" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "metadata": {}, 140 | "outputs": [], 141 | "source": [ 142 | "from neuron import h\n", 143 | "from ballandstick import BallAndStick\n", 144 | "\n", 145 | "### MPI must be initialized before we create a ParallelContext object\n", 146 | "h.nrnmpi_init()\n", 147 | "pc = h.ParallelContext()\n", 148 | "\n", 149 | "class Ring:\n", 150 | " \"\"\"A network of *N* ball-and-stick cells where cell n makes an\n", 151 | " excitatory synapse onto cell n + 1 and the last, Nth cell in the\n", 152 | " network projects to the first cell.\n", 153 | " \"\"\"\n", 154 | " def __init__(self, N=5, stim_w=0.04, stim_t=9, stim_delay=1, syn_w=0.01, syn_delay=5, r=50):\n", 155 | " \"\"\"\n", 156 | " :param N: Number of cells.\n", 157 | " :param stim_w: Weight of the stimulus\n", 158 | " :param stim_t: time of the stimulus (in ms)\n", 159 | " :param stim_delay: delay of the stimulus (in ms)\n", 160 | " :param syn_w: Synaptic weight\n", 161 | " :param syn_delay: Delay of the synapse\n", 162 | " :param r: radius of the network\n", 163 | " \"\"\" \n", 164 | " self._N = N\n", 165 | " self.set_gids() ### assign gids to processors\n", 166 | " self._syn_w = syn_w\n", 167 | " self._syn_delay = syn_delay\n", 168 | " self._create_cells(r) ### changed to use self._N instead of passing in N\n", 169 | " self._connect_cells()\n", 170 | " ### the 0th cell only exists on one process... that's the only one that gets a netstim\n", 171 | " if pc.gid_exists(0):\n", 172 | " self._netstim = h.NetStim()\n", 173 | " self._netstim.number = 1\n", 174 | " self._netstim.start = stim_t\n", 175 | " self._nc = h.NetCon(self._netstim, pc.gid2cell(0).syn) ### grab cell with gid==0 wherever it exists\n", 176 | " self._nc.delay = stim_delay\n", 177 | " self._nc.weight[0] = stim_w\n", 178 | " \n", 179 | " def set_gids(self):\n", 180 | " \"\"\"Set the gidlist on this host.\"\"\"\n", 181 | " #### Round-robin counting.\n", 182 | " #### Each host has an id from 0 to pc.nhost() - 1.\n", 183 | " self.gidlist = list(range(pc.id(), self._N, pc.nhost()))\n", 184 | " for gid in self.gidlist:\n", 185 | " pc.set_gid2node(gid, pc.id())\n", 186 | " \n", 187 | " def _create_cells(self, r):\n", 188 | " self.cells = []\n", 189 | " for i in self.gidlist: ### only create the cells that exist on this host\n", 190 | " theta = i * 2 * h.PI / self._N\n", 191 | " self.cells.append(BallAndStick(i, h.cos(theta) * r, h.sin(theta) * r, 0, theta))\n", 192 | " ### associate the cell with this host and gid\n", 193 | " for cell in self.cells:\n", 194 | " pc.cell(cell._gid, cell._spike_detector)\n", 195 | "\n", 196 | " def _connect_cells(self):\n", 197 | " ### this method is different because we now must use ids instead of objects\n", 198 | " for target in self.cells:\n", 199 | " source_gid = (target._gid - 1 + self._N) % self._N\n", 200 | " nc = pc.gid_connect(source_gid, target.syn)\n", 201 | " nc.weight[0] = self._syn_w\n", 202 | " nc.delay = self._syn_delay\n", 203 | " target._ncs.append(nc) " 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "The call to `h.nrnmpi_init()` must happen before any use of the ParallelContext class -- which forms a key part of any NEURON parallel simulation.\n", 211 | "\n", 212 | "The only conceptually new method here is the `set_gids` method where each process specifies which cells it will simulate. Here we use what is known as a round-robin approach, where the `pc.id()`th process starts at `pc.id()` and skips by however many processes are running (`pc.nhost`)." 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "In `_create_cells`, we now associate the cell `gid` with the NetCon `_spike_detector`. This allows the `_connect_cells` to make connections based on gids instead of objects, using `pc.gid_connect`." 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "

Test the model

" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "Here's a basic test, `test_ring1.py` that loads the `Ring` class and plots cell 0's membrane potential timeseries:" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "from neuron import h\n", 243 | "from neuron.units import ms, mV\n", 244 | "import matplotlib.pyplot as plt\n", 245 | "from ring import Ring\n", 246 | "\n", 247 | "cell_to_plot = 0\n", 248 | "\n", 249 | "ring = Ring()\n", 250 | "\n", 251 | "pc = h.ParallelContext()\n", 252 | "pc.set_maxstep(10 * ms)\n", 253 | "\n", 254 | "t = h.Vector().record(h._ref_t)\n", 255 | "h.finitialize(-65 * mV)\n", 256 | "pc.psolve(100 * ms)\n", 257 | "\n", 258 | "if pc.gid_exists(cell_to_plot):\n", 259 | " plt.figure()\n", 260 | " plt.plot(t, pc.gid2cell(cell_to_plot).soma_v)\n", 261 | " plt.show()\n", 262 | "\n", 263 | "pc.barrier()\n", 264 | "pc.done()\n", 265 | "h.quit()" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "The code above should look very familiar." 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "The conceptually new pieces are:\n", 280 | "" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "Start by testing this without using MPI:" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "python test_ring1.py" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "And once you are satisfied that works, you can try MPI, e.g." 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": {}, 313 | "source": [ 314 | "mpiexec -n 2 python test_ring1.py" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "

Gathering spikes

" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "Our above test runs the simulation successfully, but in the end, no single process knows when all the spikes occurred. There are a number of ways to deal with this: one solution is to have each process write its data to a file. Instead, we will use pc.py_alltoall to send all the data to node 0, at which point node 0 can plot the raster, save data, or whatever." 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "Store this in `test_ring2.py`:" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "from neuron import h\n", 345 | "from neuron.units import ms, mV\n", 346 | "import matplotlib.pyplot as plt\n", 347 | "from ring import Ring\n", 348 | "\n", 349 | "ring = Ring()\n", 350 | "\n", 351 | "pc = h.ParallelContext()\n", 352 | "pc.set_maxstep(10 * ms)\n", 353 | "\n", 354 | "t = h.Vector().record(h._ref_t)\n", 355 | "h.finitialize(-65 * mV)\n", 356 | "pc.psolve(100 * ms)\n", 357 | "\n", 358 | "# send all spike time data to node 0\n", 359 | "local_data = {cell._gid: list(cell.spike_times) for cell in ring.cells}\n", 360 | "all_data = pc.py_alltoall([local_data] + [None] * (pc.nhost() - 1))\n", 361 | "\n", 362 | "if pc.id() == 0:\n", 363 | " # combine the data from the various processes\n", 364 | " data = {}\n", 365 | " for process_data in all_data:\n", 366 | " data.update(process_data)\n", 367 | " # plot it\n", 368 | " plt.figure()\n", 369 | " for i, spike_times in data.items():\n", 370 | " plt.vlines(spike_times, i + 0.5, i + 1.5)\n", 371 | " plt.show()\n", 372 | "\n", 373 | "pc.barrier()\n", 374 | "pc.done()\n", 375 | "h.quit()" 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "Running this via e.g. `mpiexec -n 2 python test_ring2.py` displays the familiar raster plot. If you are wondering why node 0 was the one chosen to make the plot, it is because that is the only node that is guaranteed to exist (nothing else exists if there is only one process being used for the simulation)." 383 | ] 384 | } 385 | ], 386 | "metadata": { 387 | "kernelspec": { 388 | "display_name": "Python 3", 389 | "language": "python", 390 | "name": "python3" 391 | }, 392 | "language_info": { 393 | "codemirror_mode": { 394 | "name": "ipython", 395 | "version": 3 396 | }, 397 | "file_extension": ".py", 398 | "mimetype": "text/x-python", 399 | "name": "python", 400 | "nbconvert_exporter": "python", 401 | "pygments_lexer": "ipython3", 402 | "version": "3.7.3" 403 | } 404 | }, 405 | "nbformat": 4, 406 | "nbformat_minor": 2 407 | } 408 | -------------------------------------------------------------------------------- /ball-and-stick-3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This is the third part of a tutorial series where we build a multicompartment cell and evolve it into a network of cells running on a parallel machine. In this part, we take the functionality of the ring network we constructed in the previous page and encapsulate it into various classes so that the network is more extensible. We also begin parameterizing the model so that particular values are not hard-coded, but remain variable so that the model is flexible." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "

Loading libraries

" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "As before, we will begin by loading relevant NEURON libraries:" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "data": { 31 | "text/plain": [ 32 | "1.0" 33 | ] 34 | }, 35 | "execution_count": 1, 36 | "metadata": {}, 37 | "output_type": "execute_result" 38 | } 39 | ], 40 | "source": [ 41 | "from neuron import h, gui\n", 42 | "from neuron.units import ms, mV\n", 43 | "h.load_file('stdrun.hoc')" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "

Generic Cell class

" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "In the last tutorial, we created a generic `Cell` class (actually, two versions) but we can expand this to make it more powerful. For example, let's make each Cell record its spike times, some membrane potential timeseries, and keep track of NetCons." 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 29, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "class Cell:\n", 67 | " def __init__(self, gid, x, y, z, theta):\n", 68 | " self._gid = gid\n", 69 | " self._setup_morphology()\n", 70 | " self.all = self.soma.wholetree()\n", 71 | " self._setup_biophysics()\n", 72 | " self.x = self.y = self.z = 0\n", 73 | " h.define_shape()\n", 74 | " self._rotate_z(theta)\n", 75 | " self._set_position(x, y, z)\n", 76 | " \n", 77 | " # everything below here in this method is NEW\n", 78 | " self._spike_detector = h.NetCon(self.soma(0.5)._ref_v, None, sec=self.soma)\n", 79 | " self.spike_times = h.Vector()\n", 80 | " self._spike_detector.record(self.spike_times)\n", 81 | " \n", 82 | " self._ncs = []\n", 83 | " \n", 84 | " self.soma_v = h.Vector().record(self.soma(0.5)._ref_v)\n", 85 | "\n", 86 | " \n", 87 | " def __repr__(self):\n", 88 | " return '{}[{}]'.format(self.name, self._gid)\n", 89 | " \n", 90 | " def _set_position(self, x, y, z):\n", 91 | " for sec in self.all:\n", 92 | " for i in range(sec.n3d()):\n", 93 | " sec.pt3dchange(i,\n", 94 | " x - self.x + sec.x3d(i),\n", 95 | " y - self.y + sec.y3d(i),\n", 96 | " z - self.z + sec.z3d(i),\n", 97 | " sec.diam3d(i))\n", 98 | " self.x, self.y, self.z = x, y, z\n", 99 | " \n", 100 | " def _rotate_z(self, theta):\n", 101 | " \"\"\"Rotate the cell about the Z axis.\"\"\"\n", 102 | " for sec in self.all:\n", 103 | " for i in range(sec.n3d()):\n", 104 | " x = sec.x3d(i)\n", 105 | " y = sec.y3d(i)\n", 106 | " c = h.cos(theta)\n", 107 | " s = h.sin(theta)\n", 108 | " xprime = x * c - y * s\n", 109 | " yprime = x * s + y * c\n", 110 | " sec.pt3dchange(i, xprime, yprime, sec.z3d(i), sec.diam3d(i))" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "Since the `BallAndStick` has a simple geometry, we could modify it to assume that all inputs go into a single location; we will call it the `.syn`." 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 30, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "class BallAndStick(Cell):\n", 127 | " name = 'BallAndStick'\n", 128 | " \n", 129 | " def _setup_morphology(self):\n", 130 | " self.soma = h.Section(name='soma', cell=self)\n", 131 | " self.dend = h.Section(name='dend', cell=self)\n", 132 | " self.dend.connect(self.soma)\n", 133 | " self.soma.L = self.soma.diam = 12.6157\n", 134 | " self.dend.L = 200\n", 135 | " self.dend.diam = 1\n", 136 | "\n", 137 | " def _setup_biophysics(self):\n", 138 | " for sec in self.all:\n", 139 | " sec.Ra = 100 # Axial resistance in Ohm * cm\n", 140 | " sec.cm = 1 # Membrane capacitance in micro Farads / cm^2\n", 141 | " self.soma.insert('hh') \n", 142 | " for seg in self.soma:\n", 143 | " seg.hh.gnabar = 0.12 # Sodium conductance in S/cm2\n", 144 | " seg.hh.gkbar = 0.036 # Potassium conductance in S/cm2\n", 145 | " seg.hh.gl = 0.0003 # Leak conductance in S/cm2\n", 146 | " seg.hh.el = -54.3 # Reversal potential in mV\n", 147 | " # Insert passive current in the dendrite\n", 148 | " self.dend.insert('pas') \n", 149 | " for seg in self.dend:\n", 150 | " seg.pas.g = 0.001 # Passive conductance in S/cm2\n", 151 | " seg.pas.e = -65 # Leak reversal potential mV\n", 152 | "\n", 153 | " # NEW: the synapse\n", 154 | " self.syn = h.ExpSyn(self.dend(0.5))\n", 155 | " self.syn.tau = 2 * ms" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "Due to the nature of `h.ExpSyn` decay, there is mathematically no difference between having two ExpSyn objects at the same point or one synapse where multiple inputs add linearly, so it suffices to have just the one as long as we're happy with all inputs going into `dend(0.5)`." 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "

Make a Ring class

" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "Encapsulating code into discrete objects is not only conceptually useful for code management, but as we know with cell objects, it lets us make several instances of the object for use in a network. Thinking ahead, we may very well need several networks – each network configured differently. This allows scripting of several simulations en masse, either in a for loop that sequentially processes the networks, or it can be used with NEURON's subworlds architecture in a parallel context." 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 39, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "class Ring:\n", 186 | " \"\"\"A network of *N* ball-and-stick cells where cell n makes an\n", 187 | " excitatory synapse onto cell n + 1 and the last, Nth cell in the\n", 188 | " network projects to the first cell.\n", 189 | " \"\"\"\n", 190 | " def __init__(self, N=5, stim_w=0.04, stim_t=9, stim_delay=1, syn_w=0.01, syn_delay=5, r=50):\n", 191 | " \"\"\"\n", 192 | " :param N: Number of cells.\n", 193 | " :param stim_w: Weight of the stimulus\n", 194 | " :param stim_t: time of the stimulus (in ms)\n", 195 | " :param stim_delay: delay of the stimulus (in ms)\n", 196 | " :param syn_w: Synaptic weight\n", 197 | " :param syn_delay: Delay of the synapse\n", 198 | " :param r: radius of the network\n", 199 | " \"\"\" \n", 200 | " self._syn_w = syn_w\n", 201 | " self._syn_delay = syn_delay\n", 202 | " self._create_cells(N, r)\n", 203 | " self._connect_cells()\n", 204 | " # add stimulus\n", 205 | " self._netstim = h.NetStim()\n", 206 | " self._netstim.number = 1\n", 207 | " self._netstim.start = stim_t\n", 208 | " self._nc = h.NetCon(self._netstim, self.cells[0].syn)\n", 209 | " self._nc.delay = stim_delay\n", 210 | " self._nc.weight[0] = stim_w\n", 211 | " \n", 212 | " def _create_cells(self, N, r):\n", 213 | " self.cells = []\n", 214 | " for i in range(N):\n", 215 | " theta = i * 2 * h.PI / N\n", 216 | " self.cells.append(BallAndStick(i, h.cos(theta) * r, h.sin(theta) * r, 0, theta))\n", 217 | " \n", 218 | " def _connect_cells(self):\n", 219 | " for source, target in zip(self.cells, self.cells[1:] + [self.cells[0]]):\n", 220 | " nc = h.NetCon(source.soma(0.5)._ref_v, target.syn, sec=source.soma)\n", 221 | " nc.weight[0] = self._syn_w\n", 222 | " nc.delay = self._syn_delay\n", 223 | " source._ncs.append(nc) " 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "The `_create_cells` method is basically the same as the `create_n_BallAndStick` function in the previous part of the tutorial; the only difference is that the cells are stored in `self._cells` instead of being returned. `_connect_cells` is shorter than the previous version because it can take advantage of the existing synapses and lists." 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "

Test the network

" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "Let's make a `Ring` object with 5 cells, render it using NEURON's built-in graphics, and run a simulation." 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 40, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "ring = Ring(N=5)" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "Now to check that it is constructed correctly:" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 41, 266 | "metadata": {}, 267 | "outputs": [ 268 | { 269 | "data": { 270 | "text/plain": [ 271 | "1.0" 272 | ] 273 | }, 274 | "execution_count": 41, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "shape_window = h.PlotShape(True)\n", 281 | "shape_window.show(0)" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "Looks good so far; let's run the simulation and record time:" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 42, 294 | "metadata": {}, 295 | "outputs": [ 296 | { 297 | "data": { 298 | "text/plain": [ 299 | "0.0" 300 | ] 301 | }, 302 | "execution_count": 42, 303 | "metadata": {}, 304 | "output_type": "execute_result" 305 | } 306 | ], 307 | "source": [ 308 | "t = h.Vector().record(h._ref_t)\n", 309 | "h.finitialize(-65 * mV)\n", 310 | "h.continuerun(100)" 311 | ] 312 | }, 313 | { 314 | "cell_type": "markdown", 315 | "metadata": {}, 316 | "source": [ 317 | "Remember that if we are running in Jupyter to make a plot appear inline we must:" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 44, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [ 326 | "%matplotlib inline" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": {}, 332 | "source": [ 333 | "Now plot the trace of cell 0's soma:" 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": 45, 339 | "metadata": {}, 340 | "outputs": [ 341 | { 342 | "data": { 343 | "image/png": "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\n", 344 | "text/plain": [ 345 | "
" 346 | ] 347 | }, 348 | "metadata": { 349 | "needs_background": "light" 350 | }, 351 | "output_type": "display_data" 352 | } 353 | ], 354 | "source": [ 355 | "import matplotlib.pyplot as plt\n", 356 | "plt.plot(t, ring.cells[0].soma_v)\n", 357 | "plt.show()" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "Cell 0 looks good. Let's look at the raster diagram:" 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": 46, 370 | "metadata": {}, 371 | "outputs": [ 372 | { 373 | "data": { 374 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAWoAAAD4CAYAAADFAawfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAKsUlEQVR4nO3db6htCVnH8d/T3BF1lNTmIJNjXaMwRNCRjRiKnKY/WIr1IsrIkDDmjdAYhmTvfBmE6atgGCsh08Q/FEKS6BxKsIl9HSudEQpTm2FszmCT2gtr9OnF3tOM05k5+9De9zxz9+cDh7v3WesuHtZe53vXWWfte6q7A8Bc33PeAwDw+IQaYDihBhhOqAGGE2qA4S7sYqPXXnttX7x4cRebBrgiXbp06f7uPjhp2U5CffHixSyXy11sGuCKVFVffqxlLn0ADCfUAMMJNcBwQg0wnFADDCfUAMMJNcBwQg0wnFDvucPDwxweHp73GGyZ1/XKItQAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMd2GTlarqS0m+keTbSR7s7sUuhwLgYRuFeu3Hu/v+nU0CwIlc+gAYbtNQd5K/qqpLVXXTSStU1U1Vtayq5fHx8fYmBNhzm4b6Fd39kiQ/k+RNVfXKR6/Q3bd096K7FwcHB1sdEmCfbRTq7r5n/ed9ST6S5KW7HAqAh50a6qq6pqqe/tDjJD+d5HO7HgyAlU3u+nh2ko9U1UPr/2l3f2ynUwHwv04NdXd/McmLLsMsAJzA7XkAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBn+S3knJPDw8MkydHR0da3vYttcrpdvqa73C6PbZevqTNqgOGEGmA4oQYYTqgBhhNqgOGEGmA4oQYYTqgBhhNqgOGEGmA4oQYYTqgBhhNqgOGEGmA4oQYYbuNQV9VVVXVHVX10lwMB8N3OckZ9c5K7djUIACfbKNRVdX2SVye5dbfjAPBom55RvzPJW5N8Z4ezAHCCU0NdVa9Jcl93XzplvZuqallVy+Pj460NCLDvNjmjfnmS11bVl5K8P8mNVfUnj16pu2/p7kV3Lw4ODrY8JsD+OjXU3f227r6+uy8meV2ST3b363c+GQBJ3EcNMN6Fs6zc3UdJjnYyCQAnckYNMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwxX3b31jS4Wi14ul1vf7mSHh4dJkqOjo3Odg+3xml55Jr+mVXWpuxcnLXNGDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHCnhrqqnlxVf1dVf19Vn6+qt1+OwQBYubDBOt9KcmN3f7Oqrk7yqar6y+7+2x3PBkA2CHWvflfXN9dPr15/bP/3dwFwoo2uUVfVVVX12ST3Jfl4d99+wjo3VdWyqpbHx8fbnhNgb20U6u7+dne/OMn1SV5aVS88YZ1bunvR3YuDg4Ntzwmwt85010d3P5DktiSv2s04ADzaJnd9HFTVM9aPn5Lkp5J8YdeDAbCyyV0f1yV5T1VdlVXYP9DdH93tWAA8ZJO7Pv4hyQ2XYRYATuCdiQDDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcLX63bXbtVgserlcbn27/x+Hh4dJkqOjo3Odg+3xml559vk1rapL3b04aZkzaoDhhBpgOKEGGE6oAYYTaoDhhBpgOKEGGE6oAYYTaoDhhBpgOKEGGE6oAYYTaoDhhBpgOKEGGO7UUFfVc6vqtqq6s6o+X1U3X47BAFi5sME6DyZ5S3d/pqqenuRSVX28u+/c8WwAZIMz6u6+t7s/s378jSR3JXnOrgcDYOVM16ir6mKSG5LcvothAPi/Ng51VT0tyYeSvLm7v37C8puqallVy+Pj423OCLDXNgp1VV2dVaTf290fPmmd7r6luxfdvTg4ONjmjAB7bZO7PirJu5Pc1d3v2P1IADzSJmfUL0/yq0lurKrPrj9+dsdzAbB26u153f2pJHUZZgHgBN6ZCDCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMJ9QAwwk1wHBCDTCcUAMMV9299Y0uFoteLpdn/nuHh4dJkqOjo+0OxLnxml55vKa7UVWXuntx0jJn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwwn1ADDCTXAcEINMJxQAwx3aqir6g+r6r6q+tzlGAiA77bJGfUfJ3nVjucA4DGcGuru/uskX7sMswBwgq1do66qm6pqWVXL4+PjbW0WYO9tLdTdfUt3L7p7cXBwsK3NAuw9d30ADCfUAMNtcnve+5J8Osnzq+ruqnrj7scC4CEXTluhu3/5cgwCwMlc+gAYTqgBhhNqgOGEGmA4oQYYTqgBhhNqgOGEGmA4oQYYTqgBhhNqgOGEGmA4oQYYTqgBhqvu3vpGF4tFL5fLrW8X4EpVVZe6e3HSMmfUAMMJNcBwQg0wnFADDCfUAMMJNcBwQg0wnFADDCfUAMPt5J2JVXWc5Mtb3/Dlc22S+897iMHsn8dn/zw+++dkP9jdByct2Emon+iqavlYb+XE/jmN/fP47J+zc+kDYDihBhhOqE92y3kPMJz98/jsn8dn/5yRa9QAwzmjBhhOqAGG2+tQV9Vzq+q2qrqzqj5fVTevP/+sqvp4Vf3T+s9nnves56mqrqqqO6rqo+vnz6uq26vqn6vqz6rqSec943mqqmdU1Qer6gtVdVdV/Zhj6GFV9Zvrr6/PVdX7qurJjqGz2etQJ3kwyVu6+wVJXpbkTVX1giS/neQT3f0jST6xfr7Pbk5y1yOe/26S3+/uH07y70neeC5TzfGuJB/r7h9N8qKs9pVjKElVPSfJbyRZdPcLk1yV5HVxDJ3JXoe6u+/t7s+sH38jqy+w5yT5uSTvWa/2niQ/fz4Tnr+quj7Jq5Pcun5eSW5M8sH1Kvu+f743ySuTvDtJuvu/uvuBOIYe6UKSp1TVhSRPTXJvHENnstehfqSqupjkhiS3J3l2d9+7XvTVJM8+p7EmeGeStyb5zvr59yV5oLsfXD+/O6t/3PbV85IcJ/mj9eWhW6vqmjiGkiTdfU+S30vylawC/R9JLsUxdCZCnaSqnpbkQ0ne3N1ff+SyXt2/uJf3MFbVa5Lc192XznuWwS4keUmSP+juG5L8Zx51mWPPj6FnZvXdxfOSfH+Sa5K86lyHegLa+1BX1dVZRfq93f3h9af/raquWy+/Lsl95zXfOXt5ktdW1ZeSvD+rb1ffleQZ629jk+T6JPecz3gj3J3k7u6+ff38g1mF2zG08pNJ/qW7j7v7v5N8OKvjyjF0Bnsd6vX11ncnuau73/GIRX+R5A3rx29I8ueXe7YJuvtt3X19d1/M6gdAn+zuX0lyW5JfWK+2t/snSbr7q0n+taqev/7UTyS5M46hh3wlycuq6qnrr7eH9o9j6Az2+p2JVfWKJH+T5B/z8DXY38nqOvUHkvxAVv9d6y9299fOZcghquowyW9192uq6oeyOsN+VpI7kry+u791nvOdp6p6cVY/bH1Ski8m+bWsToIcQ0mq6u1Jfimru6zuSPLrWV2TdgxtaK9DDfBEsNeXPgCeCIQaYDihBhhOqAGGE2qA4YQaYDihBhjufwCfV5NrK1QG3AAAAABJRU5ErkJggg==\n", 375 | "text/plain": [ 376 | "
" 377 | ] 378 | }, 379 | "metadata": { 380 | "needs_background": "light" 381 | }, 382 | "output_type": "display_data" 383 | } 384 | ], 385 | "source": [ 386 | "plt.figure()\n", 387 | "for i, cell in enumerate(ring.cells):\n", 388 | " plt.vlines(cell.spike_times, i + 0.5, i + 1.5)\n", 389 | "plt.show()" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "

Explore effects of parameters

" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": {}, 402 | "source": [ 403 | "Let's compare two simulations: one with the same parameters as above, which we'll plot in black, and one with half the synaptic weight, which we'll plot in red:" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 49, 409 | "metadata": {}, 410 | "outputs": [ 411 | { 412 | "data": { 413 | "image/png": "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\n", 414 | "text/plain": [ 415 | "
" 416 | ] 417 | }, 418 | "metadata": { 419 | "needs_background": "light" 420 | }, 421 | "output_type": "display_data" 422 | } 423 | ], 424 | "source": [ 425 | "plt.figure()\n", 426 | "for syn_w, color in [(0.01, 'black'), (0.005, 'red')]:\n", 427 | " ring = Ring(N=5, syn_w=syn_w)\n", 428 | " h.finitialize(-65 * mV)\n", 429 | " h.continuerun(100 * ms)\n", 430 | " for i, cell in enumerate(ring.cells):\n", 431 | " plt.vlines(cell.spike_times, i + 0.5, i + 1.5, color=color)\n", 432 | "\n", 433 | "plt.show()" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "In both simulations, the first spike occurs at 10.925 ms. After that, the red spikes lag the black ones by steadily increasing amounts." 441 | ] 442 | }, 443 | { 444 | "cell_type": "markdown", 445 | "metadata": {}, 446 | "source": [ 447 | "The next part of the tutorial will translate this serial model into a parallel model. That part will not work in Jupyter and must be run from a terminal." 448 | ] 449 | } 450 | ], 451 | "metadata": { 452 | "kernelspec": { 453 | "display_name": "Python 3", 454 | "language": "python", 455 | "name": "python3" 456 | }, 457 | "language_info": { 458 | "codemirror_mode": { 459 | "name": "ipython", 460 | "version": 3 461 | }, 462 | "file_extension": ".py", 463 | "mimetype": "text/x-python", 464 | "name": "python", 465 | "nbconvert_exporter": "python", 466 | "pygments_lexer": "ipython3", 467 | "version": "3.7.3" 468 | } 469 | }, 470 | "nbformat": 4, 471 | "nbformat_minor": 2 472 | } 473 | -------------------------------------------------------------------------------- /pythontutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This page demonstrates some basic Python concepts and essentials. See the Python Tutorial and Reference for more exhaustive resources.\n", 8 | "

This page provides a brief introduction to:

\n", 9 | "
    \n", 10 | "
  • Python syntax
  • \n", 11 | "
  • Variables
  • \n", 12 | "
  • Lists and Dicts
  • \n", 13 | "
  • For loops and iterators
  • \n", 14 | "
  • Functions
  • \n", 15 | "
  • Classes
  • \n", 16 | "
  • Importing modules
  • \n", 17 | "
  • Writing and reading files with Pickling.
  • \n", 18 | "
" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "

Displaying results

" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "The following command simply prints \"Hello world!\". Run it, and then re-evaluate with a different string." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 1, 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "name": "stdout", 42 | "output_type": "stream", 43 | "text": [ 44 | "Hello world!\n" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "print(\"Hello world!\")" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Here print is a function that displays its input on the screen." 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "We can use a string's format method with {} placeholders to substitute calculated values into the output in specific locations; for example:" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 2, 69 | "metadata": {}, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": [ 75 | "3 + 4 = 7. Amazing!\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "print(\"3 + 4 = {}. Amazing!\".format(3 + 4))" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "Multiple values can be substituted:" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "name": "stdout", 97 | "output_type": "stream", 98 | "text": [ 99 | "The length of the apical dendrite is 100 microns.\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "print(\"The length of the {} is {} microns.\".format(\"apical dendrite\", 100))" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "There are more sophisticated ways of using format\n", 112 | "(see examples on the Python website)." 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "

Variables: Strings, numbers, and dynamic type casting

" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "Variables are easily assigned:" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 4, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "my_name = \"Tom\"\n", 136 | "my_age = 45" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "Let’s work with these variables." 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": {}, 150 | "outputs": [ 151 | { 152 | "name": "stdout", 153 | "output_type": "stream", 154 | "text": [ 155 | "Tom\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "print(my_name)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 6, 166 | "metadata": {}, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "45\n" 173 | ] 174 | } 175 | ], 176 | "source": [ 177 | "print(my_age)" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "Strings can be combined with the + operator." 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 7, 190 | "metadata": {}, 191 | "outputs": [ 192 | { 193 | "name": "stdout", 194 | "output_type": "stream", 195 | "text": [ 196 | "Hello, Tom\n" 197 | ] 198 | } 199 | ], 200 | "source": [ 201 | "greeting = \"Hello, \" + my_name\n", 202 | "print(greeting)" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "Let’s move on to numbers." 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 8, 215 | "metadata": {}, 216 | "outputs": [ 217 | { 218 | "name": "stdout", 219 | "output_type": "stream", 220 | "text": [ 221 | "45\n" 222 | ] 223 | } 224 | ], 225 | "source": [ 226 | "print(my_age)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "If you try using the + operator on my_name and my_age:" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 9, 239 | "metadata": {}, 240 | "outputs": [ 241 | { 242 | "ename": "TypeError", 243 | "evalue": "can only concatenate str (not \"int\") to str", 244 | "output_type": "error", 245 | "traceback": [ 246 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 247 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 248 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmy_name\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mmy_age\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 249 | "\u001b[0;31mTypeError\u001b[0m: can only concatenate str (not \"int\") to str" 250 | ] 251 | } 252 | ], 253 | "source": [ 254 | "print(my_name + my_age)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "

You will get a TypeError. What is wrong?

\n", 262 | "

my_name is a string and my_age is a number. Adding in this context does not make any sense.

\n", 263 | "

We can determine an object’s type with the type() function.

" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "type(my_name)" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [ 281 | "type(my_age)" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "

The function isinstance() is typically more useful than comparing variable types as it allows handling subclasses:

" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "isinstance(my_name, str)" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "my_valid_var = None\n", 312 | "if my_valid_var is not None:\n", 313 | " print(my_valid_var)\n", 314 | "else:\n", 315 | " print(\"The variable is None!\")" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "

Arithmetic: +, -, *, /, **, % and comparisons

" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": null, 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "2 * 6" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "Check for equality using two equal signs:" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "2 * 6 == 4 * 3" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": null, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "5 < 2" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": {}, 368 | "outputs": [], 369 | "source": [ 370 | "5 < 2 or 3 < 5" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "5 < 2 and 3 < 5" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": null, 385 | "metadata": {}, 386 | "outputs": [], 387 | "source": [ 388 | "2 * 3 != 5" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "% is the modulus operator. It returns the remainder from doing a division." 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": null, 401 | "metadata": {}, 402 | "outputs": [], 403 | "source": [ 404 | "5 % 3" 405 | ] 406 | }, 407 | { 408 | "cell_type": "markdown", 409 | "metadata": {}, 410 | "source": [ 411 | "The above is because 5 / 3 is 1 with a remainder of 2." 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "In decimal, that is:" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "metadata": {}, 425 | "outputs": [], 426 | "source": [ 427 | "5 / 3" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "
\n", 435 | "

Warning

\n", 436 | "

In older versions of Python (prior to 3.0), the / operator when used on integers performed integer division; i.e. 3/2 returned 1, but 3/2.0 returned 1.5. Beginning with Python 3.0, the / operator returns a float if integers do not divide evenly; i.e. 3/2 returns 1.5. Integer division is still available using the // operator, i.e. 3 // 2 evaluates to 1.

" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "

Making choices: if, else

" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": null, 449 | "metadata": {}, 450 | "outputs": [], 451 | "source": [ 452 | "section = 'soma'\n", 453 | "if section == 'soma':\n", 454 | " print('working on the soma')\n", 455 | "else:\n", 456 | " print('not working on the soma')" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": null, 462 | "metadata": {}, 463 | "outputs": [], 464 | "source": [ 465 | "p = 0.06\n", 466 | "if p < 0.05:\n", 467 | " print('statistically significant')\n", 468 | "else:\n", 469 | " print('not statistically significant')" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "Note that here we used a single quote instead of a double quote to indicate the beginning and end of a string. Either way is fine, as long as the beginning and end of a string match." 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": {}, 482 | "source": [ 483 | "Python also has a special object called None. This is one way you can specify whether or not an object is valid. When doing comparisons with None, it is generally recommended to use is and is not:" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": null, 489 | "metadata": {}, 490 | "outputs": [], 491 | "source": [ 492 | "postsynaptic_cell = None\n", 493 | "if postsynaptic_cell is not None:\n", 494 | " print(\"Connecting to postsynaptic cell\")\n", 495 | "else:\n", 496 | " print(\"No postsynaptic cell to connect to\")" 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": {}, 502 | "source": [ 503 | "

Lists

" 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": {}, 509 | "source": [ 510 | "Lists are comma-separated values surrounded by square brackets:" 511 | ] 512 | }, 513 | { 514 | "cell_type": "code", 515 | "execution_count": null, 516 | "metadata": {}, 517 | "outputs": [], 518 | "source": [ 519 | "my_list = [1, 3, 5, 8, 13]\n", 520 | "print(my_list)" 521 | ] 522 | }, 523 | { 524 | "cell_type": "markdown", 525 | "metadata": {}, 526 | "source": [ 527 | "Lists are zero-indexed. That is, the first element is 0." 528 | ] 529 | }, 530 | { 531 | "cell_type": "code", 532 | "execution_count": null, 533 | "metadata": {}, 534 | "outputs": [], 535 | "source": [ 536 | "my_list[0]" 537 | ] 538 | }, 539 | { 540 | "cell_type": "markdown", 541 | "metadata": {}, 542 | "source": [ 543 | "You may often find yourself wanting to know how many items are in a list." 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": null, 549 | "metadata": {}, 550 | "outputs": [], 551 | "source": [ 552 | "len(my_list)" 553 | ] 554 | }, 555 | { 556 | "cell_type": "markdown", 557 | "metadata": {}, 558 | "source": [ 559 | "Python interprets negative indices as counting backwards from the end of the list. That is, the -1 index refers to the last item, the -2 index refers to the second-to-last item, etc." 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "execution_count": null, 565 | "metadata": {}, 566 | "outputs": [], 567 | "source": [ 568 | "print(my_list)\n", 569 | "print(my_list[-1])" 570 | ] 571 | }, 572 | { 573 | "cell_type": "markdown", 574 | "metadata": {}, 575 | "source": [ 576 | "“Slicing” is extracting particular sub-elements from the list in a particular range. However, notice that the right-side is excluded, and the left is included." 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "metadata": {}, 583 | "outputs": [], 584 | "source": [ 585 | "print(my_list)\n", 586 | "print(my_list[2:4]) # Includes the range from index 2 to 3\n", 587 | "print(my_list[2:-1]) # Includes the range from index 2 to the element before -1\n", 588 | "print(my_list[:2]) # Includes everything before index 2\n", 589 | "print(my_list[2:]) # Includes everything from index 2" 590 | ] 591 | }, 592 | { 593 | "cell_type": "markdown", 594 | "metadata": {}, 595 | "source": [ 596 | "We can check if our list contains a given value using the in operator:" 597 | ] 598 | }, 599 | { 600 | "cell_type": "code", 601 | "execution_count": null, 602 | "metadata": {}, 603 | "outputs": [], 604 | "source": [ 605 | "42 in my_list" 606 | ] 607 | }, 608 | { 609 | "cell_type": "code", 610 | "execution_count": null, 611 | "metadata": {}, 612 | "outputs": [], 613 | "source": [ 614 | "5 in my_list" 615 | ] 616 | }, 617 | { 618 | "cell_type": "markdown", 619 | "metadata": {}, 620 | "source": [ 621 | "We can append an element to a list using the append method:" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "metadata": {}, 628 | "outputs": [], 629 | "source": [ 630 | "my_list.append(42)\n", 631 | "print(my_list)" 632 | ] 633 | }, 634 | { 635 | "cell_type": "markdown", 636 | "metadata": {}, 637 | "source": [ 638 | "To make a variable equal to a copy of a list, set it equal to list(the_old_list). For example:" 639 | ] 640 | }, 641 | { 642 | "cell_type": "code", 643 | "execution_count": null, 644 | "metadata": {}, 645 | "outputs": [], 646 | "source": [ 647 | "list_a = [1, 3, 5, 8, 13]\n", 648 | "list_b = list(list_a)\n", 649 | "list_b.reverse()\n", 650 | "print(\"list_a = \" + str(list_a))\n", 651 | "print(\"list_b = \" + str(list_b))" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "metadata": {}, 657 | "source": [ 658 | "In particular, note that assigning one list to another variable does not make a copy. Instead, it just gives another way of accessing the same list." 659 | ] 660 | }, 661 | { 662 | "cell_type": "code", 663 | "execution_count": null, 664 | "metadata": {}, 665 | "outputs": [], 666 | "source": [ 667 | "print('initial: list_a[0] = %g' % list_a[0])\n", 668 | "foo = list_a\n", 669 | "foo[0] = 42\n", 670 | "print('final: list_a[0] = %g' % list_a[0])" 671 | ] 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": {}, 676 | "source": [ 677 | "

If the second line in the previous example was replaced with list_b = list_a, then what would happen?

\n", 678 | "

In that case, list_b is the same list as list_a (as opposed to a copy), so when list_b was reversed so is list_a (since list_b is list_a).

" 679 | ] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "We can sort a list and get a new list using the sorted function. e.g." 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": null, 691 | "metadata": {}, 692 | "outputs": [], 693 | "source": [ 694 | "sorted(['soma', 'basal', 'apical', 'axon', 'obliques'])" 695 | ] 696 | }, 697 | { 698 | "cell_type": "markdown", 699 | "metadata": {}, 700 | "source": [ 701 | "Here we have sorted by alphabetical order. If our list had only numbers, it would by default sort by numerical order:" 702 | ] 703 | }, 704 | { 705 | "cell_type": "code", 706 | "execution_count": null, 707 | "metadata": {}, 708 | "outputs": [], 709 | "source": [ 710 | "sorted([6, 1, 165, 1.51, 4])" 711 | ] 712 | }, 713 | { 714 | "cell_type": "markdown", 715 | "metadata": {}, 716 | "source": [ 717 | "If we wanted to sort by another attribute, we can specify a function that returns that attribute value as the optional key keyword argument. For example, to sort our list of neuron parts by the length of the name (returned by the function len):" 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "execution_count": null, 723 | "metadata": {}, 724 | "outputs": [], 725 | "source": [ 726 | "sorted(['soma', 'basal', 'apical', 'axon', 'obliques'], key=len)" 727 | ] 728 | }, 729 | { 730 | "cell_type": "markdown", 731 | "metadata": {}, 732 | "source": [ 733 | "

Lists can contain arbitrary data types, but if you find yourself doing this, you should probably consider making classes or dictionaries (described below).

" 734 | ] 735 | }, 736 | { 737 | "cell_type": "code", 738 | "execution_count": null, 739 | "metadata": {}, 740 | "outputs": [], 741 | "source": [ 742 | "confusing_list = ['abc', 1.0, 2, \"another string\"]\n", 743 | "print(confusing_list)\n", 744 | "print(confusing_list[3])" 745 | ] 746 | }, 747 | { 748 | "cell_type": "markdown", 749 | "metadata": {}, 750 | "source": [ 751 | "It is sometimes convenient to assign the elements of a list with a known length to an equivalent number of variables:" 752 | ] 753 | }, 754 | { 755 | "cell_type": "code", 756 | "execution_count": null, 757 | "metadata": {}, 758 | "outputs": [], 759 | "source": [ 760 | "first, second, third = [42, 35, 25]\n", 761 | "print(second)" 762 | ] 763 | }, 764 | { 765 | "cell_type": "markdown", 766 | "metadata": {}, 767 | "source": [ 768 | "The range function can be used to create a list-like object (prior to Python 3.0, it generated lists; beginning with 3.0, it generates a more memory efficient structure) of evenly spaced integers; a list can be produced from this using the list function. With one argument, range produces integers from 0 to the argument, with two integer arguments, it produces integers between the two values, and with three arguments, the third specifies the interval between the first two. The ending value is not included." 769 | ] 770 | }, 771 | { 772 | "cell_type": "code", 773 | "execution_count": 10, 774 | "metadata": {}, 775 | "outputs": [ 776 | { 777 | "name": "stdout", 778 | "output_type": "stream", 779 | "text": [ 780 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" 781 | ] 782 | } 783 | ], 784 | "source": [ 785 | "print(list(range(10))) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": 11, 791 | "metadata": {}, 792 | "outputs": [ 793 | { 794 | "name": "stdout", 795 | "output_type": "stream", 796 | "text": [ 797 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" 798 | ] 799 | } 800 | ], 801 | "source": [ 802 | "print(list(range(0, 10))) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" 803 | ] 804 | }, 805 | { 806 | "cell_type": "code", 807 | "execution_count": 12, 808 | "metadata": {}, 809 | "outputs": [ 810 | { 811 | "name": "stdout", 812 | "output_type": "stream", 813 | "text": [ 814 | "[3, 4, 5, 6, 7, 8, 9]\n" 815 | ] 816 | } 817 | ], 818 | "source": [ 819 | "print(list(range(3, 10))) # [3, 4, 5, 6, 7, 8, 9]" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "execution_count": 13, 825 | "metadata": {}, 826 | "outputs": [ 827 | { 828 | "name": "stdout", 829 | "output_type": "stream", 830 | "text": [ 831 | "[0, 2, 4, 6, 8]\n" 832 | ] 833 | } 834 | ], 835 | "source": [ 836 | "print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]" 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": 14, 842 | "metadata": {}, 843 | "outputs": [ 844 | { 845 | "name": "stdout", 846 | "output_type": "stream", 847 | "text": [ 848 | "[]\n" 849 | ] 850 | } 851 | ], 852 | "source": [ 853 | "print(list(range(0, -10))) # []" 854 | ] 855 | }, 856 | { 857 | "cell_type": "code", 858 | "execution_count": 15, 859 | "metadata": {}, 860 | "outputs": [ 861 | { 862 | "name": "stdout", 863 | "output_type": "stream", 864 | "text": [ 865 | "[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]\n" 866 | ] 867 | } 868 | ], 869 | "source": [ 870 | "print(list(range(0, -10, -1))) # [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]" 871 | ] 872 | }, 873 | { 874 | "cell_type": "code", 875 | "execution_count": 16, 876 | "metadata": {}, 877 | "outputs": [ 878 | { 879 | "name": "stdout", 880 | "output_type": "stream", 881 | "text": [ 882 | "[0, -2, -4, -6, -8]\n" 883 | ] 884 | } 885 | ], 886 | "source": [ 887 | "print(list(range(0, -10, -2))) # [0, -2, -4, -6, -8]" 888 | ] 889 | }, 890 | { 891 | "cell_type": "markdown", 892 | "metadata": {}, 893 | "source": [ 894 | "For non-integer ranges, use numpy.arange from the numpy module." 895 | ] 896 | }, 897 | { 898 | "cell_type": "markdown", 899 | "metadata": {}, 900 | "source": [ 901 | "

List comprehensions (set theory)

" 902 | ] 903 | }, 904 | { 905 | "cell_type": "markdown", 906 | "metadata": {}, 907 | "source": [ 908 | "List comprehensions provide a rule for building a list from another list (or any other Python iterable).\n", 909 | "\n", 910 | "For example, the list of all integers from 0 to 9 inclusive is range(10) as shown above. We can get a list of the squares of all those integers via:" 911 | ] 912 | }, 913 | { 914 | "cell_type": "code", 915 | "execution_count": 17, 916 | "metadata": {}, 917 | "outputs": [ 918 | { 919 | "data": { 920 | "text/plain": [ 921 | "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" 922 | ] 923 | }, 924 | "execution_count": 17, 925 | "metadata": {}, 926 | "output_type": "execute_result" 927 | } 928 | ], 929 | "source": [ 930 | "[x ** 2 for x in range(10)]" 931 | ] 932 | }, 933 | { 934 | "cell_type": "markdown", 935 | "metadata": {}, 936 | "source": [ 937 | "Including an if condition allows us to filter the list:\n", 938 | "\n", 939 | "What are all the integers x between 0 and 29 inclusive that satisfy (x - 3) * (x - 10) == 0?" 940 | ] 941 | }, 942 | { 943 | "cell_type": "code", 944 | "execution_count": 18, 945 | "metadata": {}, 946 | "outputs": [ 947 | { 948 | "data": { 949 | "text/plain": [ 950 | "[3, 10]" 951 | ] 952 | }, 953 | "execution_count": 18, 954 | "metadata": {}, 955 | "output_type": "execute_result" 956 | } 957 | ], 958 | "source": [ 959 | "[x for x in range(30) if (x - 3) * (x - 10) == 0]" 960 | ] 961 | }, 962 | { 963 | "cell_type": "markdown", 964 | "metadata": {}, 965 | "source": [ 966 | "

For loops and iterators

" 967 | ] 968 | }, 969 | { 970 | "cell_type": "markdown", 971 | "metadata": {}, 972 | "source": [ 973 | "We can iterate over elements in a list by following the format: for element in list: Notice that indentation is important in Python! After a colon, the block needs to be indented. (Any consistent indentation will work, but the Python standard is 4 spaces)." 974 | ] 975 | }, 976 | { 977 | "cell_type": "code", 978 | "execution_count": 19, 979 | "metadata": {}, 980 | "outputs": [ 981 | { 982 | "name": "stdout", 983 | "output_type": "stream", 984 | "text": [ 985 | "soma\n", 986 | "axon\n", 987 | "dendrites\n" 988 | ] 989 | } 990 | ], 991 | "source": [ 992 | "cell_parts = ['soma', 'axon', 'dendrites']\n", 993 | "for part in cell_parts:\n", 994 | " print(part)" 995 | ] 996 | }, 997 | { 998 | "cell_type": "markdown", 999 | "metadata": {}, 1000 | "source": [ 1001 | "Note that we are iterating over the elements of a list; much of the time, the index of the items is irrelevant. If, on the other hand, we need to know the index as well, we can use enumerate:" 1002 | ] 1003 | }, 1004 | { 1005 | "cell_type": "code", 1006 | "execution_count": 20, 1007 | "metadata": {}, 1008 | "outputs": [ 1009 | { 1010 | "name": "stdout", 1011 | "output_type": "stream", 1012 | "text": [ 1013 | "0 soma\n", 1014 | "1 axon\n", 1015 | "2 dendrites\n" 1016 | ] 1017 | } 1018 | ], 1019 | "source": [ 1020 | "cell_parts = ['soma', 'axon', 'dendrites']\n", 1021 | "for part_num, part in enumerate(cell_parts):\n", 1022 | " print('%d %s' % (part_num, part))" 1023 | ] 1024 | }, 1025 | { 1026 | "cell_type": "markdown", 1027 | "metadata": {}, 1028 | "source": [ 1029 | "Multiple aligned lists (such as occurs with time series data) can be looped over simultaneously using zip:" 1030 | ] 1031 | }, 1032 | { 1033 | "cell_type": "code", 1034 | "execution_count": 21, 1035 | "metadata": {}, 1036 | "outputs": [ 1037 | { 1038 | "name": "stdout", 1039 | "output_type": "stream", 1040 | "text": [ 1041 | " soma 20\n", 1042 | " axon 2\n", 1043 | " dendrites 3\n" 1044 | ] 1045 | } 1046 | ], 1047 | "source": [ 1048 | "cell_parts = ['soma', 'axon', 'dendrites']\n", 1049 | "diams = [20, 2, 3]\n", 1050 | "for diam, part in zip(diams, cell_parts):\n", 1051 | " print('%10s %g' % (part, diam))" 1052 | ] 1053 | }, 1054 | { 1055 | "cell_type": "markdown", 1056 | "metadata": {}, 1057 | "source": [ 1058 | "Another example:" 1059 | ] 1060 | }, 1061 | { 1062 | "cell_type": "code", 1063 | "execution_count": 22, 1064 | "metadata": {}, 1065 | "outputs": [ 1066 | { 1067 | "name": "stdout", 1068 | "output_type": "stream", 1069 | "text": [ 1070 | "x = [0, 1, 2, 3, 4]\n", 1071 | "y = ['a', 'b', 'c', 'd', 'e']\n", 1072 | "[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]\n" 1073 | ] 1074 | } 1075 | ], 1076 | "source": [ 1077 | "y = ['a', 'b', 'c', 'd', 'e']\n", 1078 | "x = list(range(len(y)))\n", 1079 | "print(\"x = {}\".format(x))\n", 1080 | "print(\"y = {}\".format(y))\n", 1081 | "print(list(zip(x, y)))" 1082 | ] 1083 | }, 1084 | { 1085 | "cell_type": "markdown", 1086 | "metadata": {}, 1087 | "source": [ 1088 | "This is a list of tuples. Given a list of tuples, then we iterate with each tuple." 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "code", 1093 | "execution_count": 23, 1094 | "metadata": {}, 1095 | "outputs": [ 1096 | { 1097 | "name": "stdout", 1098 | "output_type": "stream", 1099 | "text": [ 1100 | "index 0: a\n", 1101 | "index 1: b\n", 1102 | "index 2: c\n", 1103 | "index 3: d\n", 1104 | "index 4: e\n" 1105 | ] 1106 | } 1107 | ], 1108 | "source": [ 1109 | "for x_val, y_val in zip(x, y):\n", 1110 | " print(\"index {}: {}\".format(x_val, y_val))" 1111 | ] 1112 | }, 1113 | { 1114 | "cell_type": "markdown", 1115 | "metadata": {}, 1116 | "source": [ 1117 | "Tuples are similar to lists, except they are immutable (cannot be changed). You can retrieve individual elements of a tuple, but once they are set upon creation, you cannot change them. Also, you cannot add or remove elements of a tuple." 1118 | ] 1119 | }, 1120 | { 1121 | "cell_type": "code", 1122 | "execution_count": 24, 1123 | "metadata": {}, 1124 | "outputs": [ 1125 | { 1126 | "name": "stdout", 1127 | "output_type": "stream", 1128 | "text": [ 1129 | "(1, 'two', 3)\n", 1130 | "two\n" 1131 | ] 1132 | } 1133 | ], 1134 | "source": [ 1135 | "my_tuple = (1, 'two', 3)\n", 1136 | "print(my_tuple)\n", 1137 | "print(my_tuple[1])" 1138 | ] 1139 | }, 1140 | { 1141 | "cell_type": "markdown", 1142 | "metadata": {}, 1143 | "source": [ 1144 | "Attempting to modify a tuple, e.g." 1145 | ] 1146 | }, 1147 | { 1148 | "cell_type": "code", 1149 | "execution_count": 25, 1150 | "metadata": {}, 1151 | "outputs": [ 1152 | { 1153 | "ename": "TypeError", 1154 | "evalue": "'tuple' object does not support item assignment", 1155 | "output_type": "error", 1156 | "traceback": [ 1157 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 1158 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 1159 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmy_tuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 1160 | "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" 1161 | ] 1162 | } 1163 | ], 1164 | "source": [ 1165 | "my_tuple[1] = 2" 1166 | ] 1167 | }, 1168 | { 1169 | "cell_type": "markdown", 1170 | "metadata": {}, 1171 | "source": [ 1172 | "will cause a TypeError." 1173 | ] 1174 | }, 1175 | { 1176 | "cell_type": "markdown", 1177 | "metadata": {}, 1178 | "source": [ 1179 | "Because you cannot modify an element in a tuple, or add or remove individual elements of it, it can operate in Python more efficiently than a list. A tuple can even serve as a key to a dictionary." 1180 | ] 1181 | }, 1182 | { 1183 | "cell_type": "markdown", 1184 | "metadata": {}, 1185 | "source": [ 1186 | "

Dictionaries

" 1187 | ] 1188 | }, 1189 | { 1190 | "cell_type": "markdown", 1191 | "metadata": {}, 1192 | "source": [ 1193 | "A dictionary (also called a dict or hash table) is a set of (key, value) pairs, denoted by curly brackets:" 1194 | ] 1195 | }, 1196 | { 1197 | "cell_type": "code", 1198 | "execution_count": 26, 1199 | "metadata": {}, 1200 | "outputs": [ 1201 | { 1202 | "name": "stdout", 1203 | "output_type": "stream", 1204 | "text": [ 1205 | "{'name': 'Tom', 'age': 45, 'height': \"5'8\"}\n" 1206 | ] 1207 | } 1208 | ], 1209 | "source": [ 1210 | "about_me = {'name': my_name, 'age': my_age, 'height': \"5'8\"}\n", 1211 | "print(about_me)" 1212 | ] 1213 | }, 1214 | { 1215 | "cell_type": "markdown", 1216 | "metadata": {}, 1217 | "source": [ 1218 | "You can obtain values by referencing the key:" 1219 | ] 1220 | }, 1221 | { 1222 | "cell_type": "code", 1223 | "execution_count": 27, 1224 | "metadata": {}, 1225 | "outputs": [ 1226 | { 1227 | "name": "stdout", 1228 | "output_type": "stream", 1229 | "text": [ 1230 | "5'8\n" 1231 | ] 1232 | } 1233 | ], 1234 | "source": [ 1235 | "print(about_me['height'])" 1236 | ] 1237 | }, 1238 | { 1239 | "cell_type": "markdown", 1240 | "metadata": {}, 1241 | "source": [ 1242 | "Similarly, we can modify existing values by referencing the key." 1243 | ] 1244 | }, 1245 | { 1246 | "cell_type": "code", 1247 | "execution_count": 28, 1248 | "metadata": {}, 1249 | "outputs": [ 1250 | { 1251 | "name": "stdout", 1252 | "output_type": "stream", 1253 | "text": [ 1254 | "{'name': 'Thomas', 'age': 45, 'height': \"5'8\"}\n" 1255 | ] 1256 | } 1257 | ], 1258 | "source": [ 1259 | "about_me['name'] = \"Thomas\"\n", 1260 | "print(about_me)" 1261 | ] 1262 | }, 1263 | { 1264 | "cell_type": "markdown", 1265 | "metadata": {}, 1266 | "source": [ 1267 | "We can even add new values." 1268 | ] 1269 | }, 1270 | { 1271 | "cell_type": "code", 1272 | "execution_count": 29, 1273 | "metadata": {}, 1274 | "outputs": [ 1275 | { 1276 | "name": "stdout", 1277 | "output_type": "stream", 1278 | "text": [ 1279 | "{'name': 'Thomas', 'age': 45, 'height': \"5'8\", 'eye_color': 'brown'}\n" 1280 | ] 1281 | } 1282 | ], 1283 | "source": [ 1284 | "about_me['eye_color'] = \"brown\"\n", 1285 | "print(about_me)" 1286 | ] 1287 | }, 1288 | { 1289 | "cell_type": "markdown", 1290 | "metadata": {}, 1291 | "source": [ 1292 | "We can use curly braces with keys to indicate dictionary fields when using format. e.g." 1293 | ] 1294 | }, 1295 | { 1296 | "cell_type": "code", 1297 | "execution_count": 30, 1298 | "metadata": {}, 1299 | "outputs": [ 1300 | { 1301 | "name": "stdout", 1302 | "output_type": "stream", 1303 | "text": [ 1304 | "I am a 45 year old person named Thomas. Again, my name is Thomas.\n" 1305 | ] 1306 | } 1307 | ], 1308 | "source": [ 1309 | "print('I am a {age} year old person named {name}. Again, my name is {name}.'.format(**about_me))" 1310 | ] 1311 | }, 1312 | { 1313 | "cell_type": "markdown", 1314 | "metadata": {}, 1315 | "source": [ 1316 | "Important: note the use of the ** inside the format call." 1317 | ] 1318 | }, 1319 | { 1320 | "cell_type": "markdown", 1321 | "metadata": {}, 1322 | "source": [ 1323 | "We can iterate keys (.keys()), values (.values()) or key-value value pairs (.items())in the dict. Here is an example of key-value pairs." 1324 | ] 1325 | }, 1326 | { 1327 | "cell_type": "code", 1328 | "execution_count": 31, 1329 | "metadata": {}, 1330 | "outputs": [ 1331 | { 1332 | "name": "stdout", 1333 | "output_type": "stream", 1334 | "text": [ 1335 | "key = name val = Thomas\n", 1336 | "key = age val = 45\n", 1337 | "key = height val = 5'8\n", 1338 | "key = eye_color val = brown\n" 1339 | ] 1340 | } 1341 | ], 1342 | "source": [ 1343 | "for k, v in about_me.items():\n", 1344 | " print('key = {:10s} val = {}'.format(k, v))" 1345 | ] 1346 | }, 1347 | { 1348 | "cell_type": "markdown", 1349 | "metadata": {}, 1350 | "source": [ 1351 | "To test for the presence of a key in a dict, we just ask:" 1352 | ] 1353 | }, 1354 | { 1355 | "cell_type": "code", 1356 | "execution_count": 32, 1357 | "metadata": {}, 1358 | "outputs": [ 1359 | { 1360 | "name": "stdout", 1361 | "output_type": "stream", 1362 | "text": [ 1363 | "No. 'hair_color' is NOT a key in the dict\n" 1364 | ] 1365 | } 1366 | ], 1367 | "source": [ 1368 | "if 'hair_color' in about_me:\n", 1369 | " print(\"Yes. 'hair_color' is a key in the dict\")\n", 1370 | "else:\n", 1371 | " print(\"No. 'hair_color' is NOT a key in the dict\")" 1372 | ] 1373 | }, 1374 | { 1375 | "cell_type": "markdown", 1376 | "metadata": {}, 1377 | "source": [ 1378 | "Dictionaries can be nested, e.g." 1379 | ] 1380 | }, 1381 | { 1382 | "cell_type": "code", 1383 | "execution_count": 33, 1384 | "metadata": {}, 1385 | "outputs": [ 1386 | { 1387 | "name": "stdout", 1388 | "output_type": "stream", 1389 | "text": [ 1390 | "cerebellum\n" 1391 | ] 1392 | } 1393 | ], 1394 | "source": [ 1395 | "neurons = {\n", 1396 | " 'purkinje cells': {\n", 1397 | " 'location': 'cerebellum',\n", 1398 | " 'role': 'motor movement'\n", 1399 | " },\n", 1400 | " 'ca1 pyramidal cells': {\n", 1401 | " 'location': 'hippocampus',\n", 1402 | " 'role': 'learning and memory'\n", 1403 | " }\n", 1404 | "}\n", 1405 | "print(neurons['purkinje cells']['location'])" 1406 | ] 1407 | }, 1408 | { 1409 | "cell_type": "markdown", 1410 | "metadata": {}, 1411 | "source": [ 1412 | "

Functions

" 1413 | ] 1414 | }, 1415 | { 1416 | "cell_type": "markdown", 1417 | "metadata": {}, 1418 | "source": [ 1419 | "Functions are defined with a “def” keyword in front of them, end with a colon, and the next line is indented. Indentation of 4-spaces (again, any non-zero consistent amount will do) demarcates functional blocks." 1420 | ] 1421 | }, 1422 | { 1423 | "cell_type": "code", 1424 | "execution_count": 34, 1425 | "metadata": {}, 1426 | "outputs": [], 1427 | "source": [ 1428 | "def print_hello():\n", 1429 | " print(\"Hello\")" 1430 | ] 1431 | }, 1432 | { 1433 | "cell_type": "markdown", 1434 | "metadata": {}, 1435 | "source": [ 1436 | "Now let's call our function." 1437 | ] 1438 | }, 1439 | { 1440 | "cell_type": "code", 1441 | "execution_count": 35, 1442 | "metadata": {}, 1443 | "outputs": [ 1444 | { 1445 | "name": "stdout", 1446 | "output_type": "stream", 1447 | "text": [ 1448 | "Hello\n" 1449 | ] 1450 | } 1451 | ], 1452 | "source": [ 1453 | "print_hello()" 1454 | ] 1455 | }, 1456 | { 1457 | "cell_type": "markdown", 1458 | "metadata": {}, 1459 | "source": [ 1460 | "We can also pass in an argument." 1461 | ] 1462 | }, 1463 | { 1464 | "cell_type": "code", 1465 | "execution_count": 36, 1466 | "metadata": {}, 1467 | "outputs": [], 1468 | "source": [ 1469 | "def my_print(the_arg):\n", 1470 | " print(the_arg)" 1471 | ] 1472 | }, 1473 | { 1474 | "cell_type": "markdown", 1475 | "metadata": {}, 1476 | "source": [ 1477 | "Now try passing various things to the my_print() function." 1478 | ] 1479 | }, 1480 | { 1481 | "cell_type": "code", 1482 | "execution_count": 37, 1483 | "metadata": {}, 1484 | "outputs": [ 1485 | { 1486 | "name": "stdout", 1487 | "output_type": "stream", 1488 | "text": [ 1489 | "Hello\n" 1490 | ] 1491 | } 1492 | ], 1493 | "source": [ 1494 | "my_print(\"Hello\")" 1495 | ] 1496 | }, 1497 | { 1498 | "cell_type": "markdown", 1499 | "metadata": {}, 1500 | "source": [ 1501 | "We can even make default arguments." 1502 | ] 1503 | }, 1504 | { 1505 | "cell_type": "code", 1506 | "execution_count": 38, 1507 | "metadata": {}, 1508 | "outputs": [ 1509 | { 1510 | "name": "stdout", 1511 | "output_type": "stream", 1512 | "text": [ 1513 | "Hello\n", 1514 | "[0, 1, 2, 3]\n" 1515 | ] 1516 | } 1517 | ], 1518 | "source": [ 1519 | "def my_print(message=\"Hello\"):\n", 1520 | " print(message)\n", 1521 | "\n", 1522 | "my_print()\n", 1523 | "my_print(list(range(4)))" 1524 | ] 1525 | }, 1526 | { 1527 | "cell_type": "markdown", 1528 | "metadata": {}, 1529 | "source": [ 1530 | "And we can also return values." 1531 | ] 1532 | }, 1533 | { 1534 | "cell_type": "code", 1535 | "execution_count": 39, 1536 | "metadata": {}, 1537 | "outputs": [ 1538 | { 1539 | "name": "stdout", 1540 | "output_type": "stream", 1541 | "text": [ 1542 | "[0, 1, 1, 2, 3]\n" 1543 | ] 1544 | } 1545 | ], 1546 | "source": [ 1547 | "def fib(n=5):\n", 1548 | " \"\"\"Get a Fibonacci series up to n.\"\"\"\n", 1549 | " a, b = 0, 1\n", 1550 | " series = [a]\n", 1551 | " while b < n:\n", 1552 | " a, b = b, a + b\n", 1553 | " series.append(a)\n", 1554 | " return series\n", 1555 | "\n", 1556 | "print(fib())" 1557 | ] 1558 | }, 1559 | { 1560 | "cell_type": "markdown", 1561 | "metadata": {}, 1562 | "source": [ 1563 | "Note the assignment line for a and b inside the while loop. That line says that a becomes the old value of b and that b becomes the old value of a plus the old value of b. The ability to calculate multiple values before assigning them allows Python to do things like swapping the values of two variables in one line while many other programming languages would require the introduction of a temporary variable.\n", 1564 | "\n", 1565 | "When a function begins with a string as in the above, that string is known as a doc string, and is shown whenever help is invoked on the function (this, by the way, is a way to learn more about Python's many functions):" 1566 | ] 1567 | }, 1568 | { 1569 | "cell_type": "code", 1570 | "execution_count": 40, 1571 | "metadata": {}, 1572 | "outputs": [ 1573 | { 1574 | "name": "stdout", 1575 | "output_type": "stream", 1576 | "text": [ 1577 | "Help on function fib in module __main__:\n", 1578 | "\n", 1579 | "fib(n=5)\n", 1580 | " Get a Fibonacci series up to n.\n", 1581 | "\n" 1582 | ] 1583 | } 1584 | ], 1585 | "source": [ 1586 | "help(fib)" 1587 | ] 1588 | }, 1589 | { 1590 | "cell_type": "markdown", 1591 | "metadata": {}, 1592 | "source": [ 1593 | "You may have noticed the string beginning the fib function was triple-quoted. This enables a string to span multiple lines." 1594 | ] 1595 | }, 1596 | { 1597 | "cell_type": "code", 1598 | "execution_count": 41, 1599 | "metadata": {}, 1600 | "outputs": [ 1601 | { 1602 | "name": "stdout", 1603 | "output_type": "stream", 1604 | "text": [ 1605 | "This is the first line\n", 1606 | "This is the second,\n", 1607 | "and a third.\n" 1608 | ] 1609 | } 1610 | ], 1611 | "source": [ 1612 | "multi_line_str = \"\"\"This is the first line\n", 1613 | "This is the second,\n", 1614 | "and a third.\"\"\"\n", 1615 | "\n", 1616 | "print(multi_line_str)" 1617 | ] 1618 | }, 1619 | { 1620 | "cell_type": "markdown", 1621 | "metadata": {}, 1622 | "source": [ 1623 | "

Classes

" 1624 | ] 1625 | }, 1626 | { 1627 | "cell_type": "markdown", 1628 | "metadata": {}, 1629 | "source": [ 1630 | "Objects are instances of a class. They are useful for encapsulating ideas, and mostly for having multiple instances of a structure. (In NEURON, for example, one might use a class to represent a neuron type and create many instances.) Usually you will have an __init__() method. Also note that every method of the class will have self as the first argument. While self has to be listed in the argument list of a class's method, you do not pass a self argument when calling any of the class’s methods; instead, you refer to those methods as self.method_name." 1631 | ] 1632 | }, 1633 | { 1634 | "cell_type": "code", 1635 | "execution_count": 42, 1636 | "metadata": {}, 1637 | "outputs": [], 1638 | "source": [ 1639 | "class Contact(object):\n", 1640 | " \"\"\"A given person for my database of friends.\"\"\"\n", 1641 | "\n", 1642 | " def __init__(self, first_name=None, last_name=None, email=None, phone=None):\n", 1643 | " self.first_name = first_name\n", 1644 | " self.last_name = last_name\n", 1645 | " self.email = email\n", 1646 | " self.phone = phone\n", 1647 | "\n", 1648 | " def print_info(self):\n", 1649 | " \"\"\"Print all of the information of this contact.\"\"\"\n", 1650 | " my_str = \"Contact info:\"\n", 1651 | " if self.first_name:\n", 1652 | " my_str += \" \" + self.first_name\n", 1653 | " if self.last_name:\n", 1654 | " my_str += \" \" + self.last_name\n", 1655 | " if self.email:\n", 1656 | " my_str += \" \" + self.email\n", 1657 | " if self.phone:\n", 1658 | " my_str += \" \" + self.phone\n", 1659 | " print(my_str)" 1660 | ] 1661 | }, 1662 | { 1663 | "cell_type": "markdown", 1664 | "metadata": {}, 1665 | "source": [ 1666 | "By convention, the first letter of a class name is capitalized. Notice in the class definition above that the object can contain fields, which are used within the class as self.field. This field can be another method in the class, or another object of another class." 1667 | ] 1668 | }, 1669 | { 1670 | "cell_type": "markdown", 1671 | "metadata": {}, 1672 | "source": [ 1673 | "Let's make a couple instances of Contact." 1674 | ] 1675 | }, 1676 | { 1677 | "cell_type": "code", 1678 | "execution_count": 43, 1679 | "metadata": {}, 1680 | "outputs": [], 1681 | "source": [ 1682 | "bob = Contact('Bob','Smith')\n", 1683 | "joe = Contact(email='someone@somewhere.com')" 1684 | ] 1685 | }, 1686 | { 1687 | "cell_type": "markdown", 1688 | "metadata": {}, 1689 | "source": [ 1690 | "Notice that in the first case, if we are filling each argument, we do not need to explicitly denote “first_name” and “last_name”. However, in the second case, since “first” and “last” are omitted, the first parameter passed in would be assigned to the first_name field so we have to explicitly set it to “email”." 1691 | ] 1692 | }, 1693 | { 1694 | "cell_type": "markdown", 1695 | "metadata": {}, 1696 | "source": [ 1697 | "Let’s set a field." 1698 | ] 1699 | }, 1700 | { 1701 | "cell_type": "code", 1702 | "execution_count": 44, 1703 | "metadata": {}, 1704 | "outputs": [], 1705 | "source": [ 1706 | "joe.first_name = \"Joe\"" 1707 | ] 1708 | }, 1709 | { 1710 | "cell_type": "markdown", 1711 | "metadata": {}, 1712 | "source": [ 1713 | "Similarly, we can retrieve fields from the object." 1714 | ] 1715 | }, 1716 | { 1717 | "cell_type": "code", 1718 | "execution_count": 45, 1719 | "metadata": {}, 1720 | "outputs": [ 1721 | { 1722 | "name": "stdout", 1723 | "output_type": "stream", 1724 | "text": [ 1725 | "Joe\n" 1726 | ] 1727 | } 1728 | ], 1729 | "source": [ 1730 | "the_name = joe.first_name\n", 1731 | "print(the_name)" 1732 | ] 1733 | }, 1734 | { 1735 | "cell_type": "markdown", 1736 | "metadata": {}, 1737 | "source": [ 1738 | "And we call methods of the object using the format instance.method()." 1739 | ] 1740 | }, 1741 | { 1742 | "cell_type": "code", 1743 | "execution_count": 46, 1744 | "metadata": {}, 1745 | "outputs": [ 1746 | { 1747 | "name": "stdout", 1748 | "output_type": "stream", 1749 | "text": [ 1750 | "Contact info: Joe someone@somewhere.com\n" 1751 | ] 1752 | } 1753 | ], 1754 | "source": [ 1755 | "joe.print_info()" 1756 | ] 1757 | }, 1758 | { 1759 | "cell_type": "markdown", 1760 | "metadata": {}, 1761 | "source": [ 1762 | "Remember the importance of docstrings!" 1763 | ] 1764 | }, 1765 | { 1766 | "cell_type": "code", 1767 | "execution_count": 47, 1768 | "metadata": {}, 1769 | "outputs": [ 1770 | { 1771 | "name": "stdout", 1772 | "output_type": "stream", 1773 | "text": [ 1774 | "Help on class Contact in module __main__:\n", 1775 | "\n", 1776 | "class Contact(builtins.object)\n", 1777 | " | Contact(first_name=None, last_name=None, email=None, phone=None)\n", 1778 | " | \n", 1779 | " | A given person for my database of friends.\n", 1780 | " | \n", 1781 | " | Methods defined here:\n", 1782 | " | \n", 1783 | " | __init__(self, first_name=None, last_name=None, email=None, phone=None)\n", 1784 | " | Initialize self. See help(type(self)) for accurate signature.\n", 1785 | " | \n", 1786 | " | print_info(self)\n", 1787 | " | Print all of the information of this contact.\n", 1788 | " | \n", 1789 | " | ----------------------------------------------------------------------\n", 1790 | " | Data descriptors defined here:\n", 1791 | " | \n", 1792 | " | __dict__\n", 1793 | " | dictionary for instance variables (if defined)\n", 1794 | " | \n", 1795 | " | __weakref__\n", 1796 | " | list of weak references to the object (if defined)\n", 1797 | "\n" 1798 | ] 1799 | } 1800 | ], 1801 | "source": [ 1802 | "help(Contact)" 1803 | ] 1804 | }, 1805 | { 1806 | "cell_type": "markdown", 1807 | "metadata": {}, 1808 | "source": [ 1809 | "

Importing modules

" 1810 | ] 1811 | }, 1812 | { 1813 | "cell_type": "markdown", 1814 | "metadata": {}, 1815 | "source": [ 1816 | "Extensions to core Python are made by importing modules, which may contain more variables, objects, methods, and functions. Many modules come with Python, but are not part of its core. Other packages and modules have to be installed.\n", 1817 | "\n", 1818 | "The numpy module contains a function called arange() that is similar to Python’s range() function, but permits non-integer steps." 1819 | ] 1820 | }, 1821 | { 1822 | "cell_type": "code", 1823 | "execution_count": 48, 1824 | "metadata": {}, 1825 | "outputs": [ 1826 | { 1827 | "name": "stdout", 1828 | "output_type": "stream", 1829 | "text": [ 1830 | "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]\n" 1831 | ] 1832 | } 1833 | ], 1834 | "source": [ 1835 | "import numpy\n", 1836 | "my_vec = numpy.arange(0, 1, 0.1)\n", 1837 | "print(my_vec)" 1838 | ] 1839 | }, 1840 | { 1841 | "cell_type": "markdown", 1842 | "metadata": {}, 1843 | "source": [ 1844 | "Note: numpy is available in many distributions of Python, but it is not part of Python itself. If the import numpy line gave an error message, you either do not have numpy installed or Python cannot find it for some reason. You should resolve this issue before proceeding because we will use numpy in some of the examples in other parts of the tutorial. The standard tool for installing Python modules is called pip; other options may be available depending on your platform." 1845 | ] 1846 | }, 1847 | { 1848 | "cell_type": "markdown", 1849 | "metadata": {}, 1850 | "source": [ 1851 | "We can get 20 evenly spaced values between 0 and $\\pi$ using numpy.linspace:" 1852 | ] 1853 | }, 1854 | { 1855 | "cell_type": "code", 1856 | "execution_count": 49, 1857 | "metadata": {}, 1858 | "outputs": [], 1859 | "source": [ 1860 | "x = numpy.linspace(0, numpy.pi, 20)" 1861 | ] 1862 | }, 1863 | { 1864 | "cell_type": "code", 1865 | "execution_count": 50, 1866 | "metadata": {}, 1867 | "outputs": [ 1868 | { 1869 | "name": "stdout", 1870 | "output_type": "stream", 1871 | "text": [ 1872 | "[0. 0.16534698 0.33069396 0.49604095 0.66138793 0.82673491\n", 1873 | " 0.99208189 1.15742887 1.32277585 1.48812284 1.65346982 1.8188168\n", 1874 | " 1.98416378 2.14951076 2.31485774 2.48020473 2.64555171 2.81089869\n", 1875 | " 2.97624567 3.14159265]\n" 1876 | ] 1877 | } 1878 | ], 1879 | "source": [ 1880 | "print(x)" 1881 | ] 1882 | }, 1883 | { 1884 | "cell_type": "markdown", 1885 | "metadata": {}, 1886 | "source": [ 1887 | "NumPy provides vectorized trig (and other) functions. For example, we can get another array with the sines of all those x values via:" 1888 | ] 1889 | }, 1890 | { 1891 | "cell_type": "code", 1892 | "execution_count": 51, 1893 | "metadata": {}, 1894 | "outputs": [], 1895 | "source": [ 1896 | "y = numpy.sin(x)" 1897 | ] 1898 | }, 1899 | { 1900 | "cell_type": "code", 1901 | "execution_count": 52, 1902 | "metadata": {}, 1903 | "outputs": [ 1904 | { 1905 | "name": "stdout", 1906 | "output_type": "stream", 1907 | "text": [ 1908 | "[0.00000000e+00 1.64594590e-01 3.24699469e-01 4.75947393e-01\n", 1909 | " 6.14212713e-01 7.35723911e-01 8.37166478e-01 9.15773327e-01\n", 1910 | " 9.69400266e-01 9.96584493e-01 9.96584493e-01 9.69400266e-01\n", 1911 | " 9.15773327e-01 8.37166478e-01 7.35723911e-01 6.14212713e-01\n", 1912 | " 4.75947393e-01 3.24699469e-01 1.64594590e-01 1.22464680e-16]\n" 1913 | ] 1914 | } 1915 | ], 1916 | "source": [ 1917 | "print(y)" 1918 | ] 1919 | }, 1920 | { 1921 | "cell_type": "markdown", 1922 | "metadata": {}, 1923 | "source": [ 1924 | "The bokeh module is one way to plot graphics that works especially well in a Jupyter notebook environment. To use this library in a Jupyter notebook, we first load it and tell it to display in the notebook:" 1925 | ] 1926 | }, 1927 | { 1928 | "cell_type": "code", 1929 | "execution_count": 53, 1930 | "metadata": {}, 1931 | "outputs": [ 1932 | { 1933 | "data": { 1934 | "text/html": [ 1935 | "\n", 1936 | "
\n", 1937 | " \n", 1938 | " Loading BokehJS ...\n", 1939 | "
" 1940 | ] 1941 | }, 1942 | "metadata": {}, 1943 | "output_type": "display_data" 1944 | }, 1945 | { 1946 | "data": { 1947 | "application/javascript": [ 1948 | "\n", 1949 | "(function(root) {\n", 1950 | " function now() {\n", 1951 | " return new Date();\n", 1952 | " }\n", 1953 | "\n", 1954 | " var force = true;\n", 1955 | "\n", 1956 | " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", 1957 | " root._bokeh_onload_callbacks = [];\n", 1958 | " root._bokeh_is_loading = undefined;\n", 1959 | " }\n", 1960 | "\n", 1961 | " var JS_MIME_TYPE = 'application/javascript';\n", 1962 | " var HTML_MIME_TYPE = 'text/html';\n", 1963 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 1964 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 1965 | "\n", 1966 | " /**\n", 1967 | " * Render data to the DOM node\n", 1968 | " */\n", 1969 | " function render(props, node) {\n", 1970 | " var script = document.createElement(\"script\");\n", 1971 | " node.appendChild(script);\n", 1972 | " }\n", 1973 | "\n", 1974 | " /**\n", 1975 | " * Handle when an output is cleared or removed\n", 1976 | " */\n", 1977 | " function handleClearOutput(event, handle) {\n", 1978 | " var cell = handle.cell;\n", 1979 | "\n", 1980 | " var id = cell.output_area._bokeh_element_id;\n", 1981 | " var server_id = cell.output_area._bokeh_server_id;\n", 1982 | " // Clean up Bokeh references\n", 1983 | " if (id != null && id in Bokeh.index) {\n", 1984 | " Bokeh.index[id].model.document.clear();\n", 1985 | " delete Bokeh.index[id];\n", 1986 | " }\n", 1987 | "\n", 1988 | " if (server_id !== undefined) {\n", 1989 | " // Clean up Bokeh references\n", 1990 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 1991 | " cell.notebook.kernel.execute(cmd, {\n", 1992 | " iopub: {\n", 1993 | " output: function(msg) {\n", 1994 | " var id = msg.content.text.trim();\n", 1995 | " if (id in Bokeh.index) {\n", 1996 | " Bokeh.index[id].model.document.clear();\n", 1997 | " delete Bokeh.index[id];\n", 1998 | " }\n", 1999 | " }\n", 2000 | " }\n", 2001 | " });\n", 2002 | " // Destroy server and session\n", 2003 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 2004 | " cell.notebook.kernel.execute(cmd);\n", 2005 | " }\n", 2006 | " }\n", 2007 | "\n", 2008 | " /**\n", 2009 | " * Handle when a new output is added\n", 2010 | " */\n", 2011 | " function handleAddOutput(event, handle) {\n", 2012 | " var output_area = handle.output_area;\n", 2013 | " var output = handle.output;\n", 2014 | "\n", 2015 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 2016 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 2017 | " return\n", 2018 | " }\n", 2019 | "\n", 2020 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 2021 | "\n", 2022 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 2023 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 2024 | " // store reference to embed id on output_area\n", 2025 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 2026 | " }\n", 2027 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 2028 | " var bk_div = document.createElement(\"div\");\n", 2029 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 2030 | " var script_attrs = bk_div.children[0].attributes;\n", 2031 | " for (var i = 0; i < script_attrs.length; i++) {\n", 2032 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 2033 | " }\n", 2034 | " // store reference to server id on output_area\n", 2035 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 2036 | " }\n", 2037 | " }\n", 2038 | "\n", 2039 | " function register_renderer(events, OutputArea) {\n", 2040 | "\n", 2041 | " function append_mime(data, metadata, element) {\n", 2042 | " // create a DOM node to render to\n", 2043 | " var toinsert = this.create_output_subarea(\n", 2044 | " metadata,\n", 2045 | " CLASS_NAME,\n", 2046 | " EXEC_MIME_TYPE\n", 2047 | " );\n", 2048 | " this.keyboard_manager.register_events(toinsert);\n", 2049 | " // Render to node\n", 2050 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 2051 | " render(props, toinsert[toinsert.length - 1]);\n", 2052 | " element.append(toinsert);\n", 2053 | " return toinsert\n", 2054 | " }\n", 2055 | "\n", 2056 | " /* Handle when an output is cleared or removed */\n", 2057 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 2058 | " events.on('delete.Cell', handleClearOutput);\n", 2059 | "\n", 2060 | " /* Handle when a new output is added */\n", 2061 | " events.on('output_added.OutputArea', handleAddOutput);\n", 2062 | "\n", 2063 | " /**\n", 2064 | " * Register the mime type and append_mime function with output_area\n", 2065 | " */\n", 2066 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 2067 | " /* Is output safe? */\n", 2068 | " safe: true,\n", 2069 | " /* Index of renderer in `output_area.display_order` */\n", 2070 | " index: 0\n", 2071 | " });\n", 2072 | " }\n", 2073 | "\n", 2074 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 2075 | " if (root.Jupyter !== undefined) {\n", 2076 | " var events = require('base/js/events');\n", 2077 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 2078 | "\n", 2079 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 2080 | " register_renderer(events, OutputArea);\n", 2081 | " }\n", 2082 | " }\n", 2083 | "\n", 2084 | " \n", 2085 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 2086 | " root._bokeh_timeout = Date.now() + 5000;\n", 2087 | " root._bokeh_failed_load = false;\n", 2088 | " }\n", 2089 | "\n", 2090 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 2091 | " \"
\\n\"+\n", 2092 | " \"

\\n\"+\n", 2093 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 2094 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 2095 | " \"

\\n\"+\n", 2096 | " \"
    \\n\"+\n", 2097 | " \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n", 2098 | " \"
  • use INLINE resources instead, as so:
  • \\n\"+\n", 2099 | " \"
\\n\"+\n", 2100 | " \"\\n\"+\n", 2101 | " \"from bokeh.resources import INLINE\\n\"+\n", 2102 | " \"output_notebook(resources=INLINE)\\n\"+\n", 2103 | " \"\\n\"+\n", 2104 | " \"
\"}};\n", 2105 | "\n", 2106 | " function display_loaded() {\n", 2107 | " var el = document.getElementById(\"1001\");\n", 2108 | " if (el != null) {\n", 2109 | " el.textContent = \"BokehJS is loading...\";\n", 2110 | " }\n", 2111 | " if (root.Bokeh !== undefined) {\n", 2112 | " if (el != null) {\n", 2113 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 2114 | " }\n", 2115 | " } else if (Date.now() < root._bokeh_timeout) {\n", 2116 | " setTimeout(display_loaded, 100)\n", 2117 | " }\n", 2118 | " }\n", 2119 | "\n", 2120 | "\n", 2121 | " function run_callbacks() {\n", 2122 | " try {\n", 2123 | " root._bokeh_onload_callbacks.forEach(function(callback) {\n", 2124 | " if (callback != null)\n", 2125 | " callback();\n", 2126 | " });\n", 2127 | " } finally {\n", 2128 | " delete root._bokeh_onload_callbacks\n", 2129 | " }\n", 2130 | " console.debug(\"Bokeh: all callbacks have finished\");\n", 2131 | " }\n", 2132 | "\n", 2133 | " function load_libs(css_urls, js_urls, callback) {\n", 2134 | " if (css_urls == null) css_urls = [];\n", 2135 | " if (js_urls == null) js_urls = [];\n", 2136 | "\n", 2137 | " root._bokeh_onload_callbacks.push(callback);\n", 2138 | " if (root._bokeh_is_loading > 0) {\n", 2139 | " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 2140 | " return null;\n", 2141 | " }\n", 2142 | " if (js_urls == null || js_urls.length === 0) {\n", 2143 | " run_callbacks();\n", 2144 | " return null;\n", 2145 | " }\n", 2146 | " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 2147 | " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", 2148 | "\n", 2149 | " function on_load() {\n", 2150 | " root._bokeh_is_loading--;\n", 2151 | " if (root._bokeh_is_loading === 0) {\n", 2152 | " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", 2153 | " run_callbacks()\n", 2154 | " }\n", 2155 | " }\n", 2156 | "\n", 2157 | " function on_error() {\n", 2158 | " console.error(\"failed to load \" + url);\n", 2159 | " }\n", 2160 | "\n", 2161 | " for (var i = 0; i < css_urls.length; i++) {\n", 2162 | " var url = css_urls[i];\n", 2163 | " const element = document.createElement(\"link\");\n", 2164 | " element.onload = on_load;\n", 2165 | " element.onerror = on_error;\n", 2166 | " element.rel = \"stylesheet\";\n", 2167 | " element.type = \"text/css\";\n", 2168 | " element.href = url;\n", 2169 | " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", 2170 | " document.body.appendChild(element);\n", 2171 | " }\n", 2172 | "\n", 2173 | " for (var i = 0; i < js_urls.length; i++) {\n", 2174 | " var url = js_urls[i];\n", 2175 | " var element = document.createElement('script');\n", 2176 | " element.onload = on_load;\n", 2177 | " element.onerror = on_error;\n", 2178 | " element.async = false;\n", 2179 | " element.src = url;\n", 2180 | " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 2181 | " document.head.appendChild(element);\n", 2182 | " }\n", 2183 | " };var element = document.getElementById(\"1001\");\n", 2184 | " if (element == null) {\n", 2185 | " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n", 2186 | " return false;\n", 2187 | " }\n", 2188 | "\n", 2189 | " function inject_raw_css(css) {\n", 2190 | " const element = document.createElement(\"style\");\n", 2191 | " element.appendChild(document.createTextNode(css));\n", 2192 | " document.body.appendChild(element);\n", 2193 | " }\n", 2194 | "\n", 2195 | " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.2.0.min.js\"];\n", 2196 | " var css_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.2.0.min.css\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.2.0.min.css\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.2.0.min.css\"];\n", 2197 | "\n", 2198 | " var inline_js = [\n", 2199 | " function(Bokeh) {\n", 2200 | " Bokeh.set_log_level(\"info\");\n", 2201 | " },\n", 2202 | " \n", 2203 | " function(Bokeh) {\n", 2204 | " \n", 2205 | " },\n", 2206 | " function(Bokeh) {} // ensure no trailing comma for IE\n", 2207 | " ];\n", 2208 | "\n", 2209 | " function run_inline_js() {\n", 2210 | " \n", 2211 | " if ((root.Bokeh !== undefined) || (force === true)) {\n", 2212 | " for (var i = 0; i < inline_js.length; i++) {\n", 2213 | " inline_js[i].call(root, root.Bokeh);\n", 2214 | " }if (force === true) {\n", 2215 | " display_loaded();\n", 2216 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 2217 | " setTimeout(run_inline_js, 100);\n", 2218 | " } else if (!root._bokeh_failed_load) {\n", 2219 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 2220 | " root._bokeh_failed_load = true;\n", 2221 | " } else if (force !== true) {\n", 2222 | " var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n", 2223 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 2224 | " }\n", 2225 | "\n", 2226 | " }\n", 2227 | "\n", 2228 | " if (root._bokeh_is_loading === 0) {\n", 2229 | " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 2230 | " run_inline_js();\n", 2231 | " } else {\n", 2232 | " load_libs(css_urls, js_urls, function() {\n", 2233 | " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", 2234 | " run_inline_js();\n", 2235 | " });\n", 2236 | " }\n", 2237 | "}(window));" 2238 | ], 2239 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1001\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.2.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.2.0.min.js\"];\n var css_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.2.0.min.css\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.2.0.min.css\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.2.0.min.css\"];\n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n \n function(Bokeh) {\n \n },\n function(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n \n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 2240 | }, 2241 | "metadata": {}, 2242 | "output_type": "display_data" 2243 | } 2244 | ], 2245 | "source": [ 2246 | "from bokeh.io import output_notebook\n", 2247 | "import bokeh.plotting as plt\n", 2248 | "output_notebook() # skip this line if not working in Jupyter" 2249 | ] 2250 | }, 2251 | { 2252 | "cell_type": "markdown", 2253 | "metadata": {}, 2254 | "source": [ 2255 | "Here we plot y = sin(x) vs x:" 2256 | ] 2257 | }, 2258 | { 2259 | "cell_type": "code", 2260 | "execution_count": 54, 2261 | "metadata": {}, 2262 | "outputs": [ 2263 | { 2264 | "data": { 2265 | "text/html": [ 2266 | "\n", 2267 | "\n", 2268 | "\n", 2269 | "\n", 2270 | "\n", 2271 | "\n", 2272 | "
\n" 2273 | ] 2274 | }, 2275 | "metadata": {}, 2276 | "output_type": "display_data" 2277 | }, 2278 | { 2279 | "data": { 2280 | "application/javascript": [ 2281 | "(function(root) {\n", 2282 | " function embed_document(root) {\n", 2283 | " \n", 2284 | " var docs_json = {\"94b429f1-3d0e-450e-b888-d10ccfe425f5\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1011\",\"type\":\"LinearAxis\"}],\"center\":[{\"id\":\"1015\",\"type\":\"Grid\"},{\"id\":\"1020\",\"type\":\"Grid\"}],\"left\":[{\"id\":\"1016\",\"type\":\"LinearAxis\"}],\"renderers\":[{\"id\":\"1037\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"1040\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"1027\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"1003\",\"type\":\"DataRange1d\"},\"x_scale\":{\"id\":\"1007\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"1005\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"1009\",\"type\":\"LinearScale\"}},\"id\":\"1002\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1026\",\"type\":\"HelpTool\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1021\",\"type\":\"PanTool\"},{\"id\":\"1022\",\"type\":\"WheelZoomTool\"},{\"id\":\"1023\",\"type\":\"BoxZoomTool\"},{\"id\":\"1024\",\"type\":\"SaveTool\"},{\"id\":\"1025\",\"type\":\"ResetTool\"},{\"id\":\"1026\",\"type\":\"HelpTool\"}]},\"id\":\"1027\",\"type\":\"Toolbar\"},{\"attributes\":{\"callback\":null,\"data\":{\"x\":{\"__ndarray__\":\"AAAAAAAAAAAvupcDFyrFPy+6lwMXKtU/RpdjhSK/3z8vupcDFyrlP7uofcScdOo/RpdjhSK/7z/pwiQj1ITyPy+6lwMXKvU/dbEK5FnP9z+7qH3EnHT6PwGg8KTfGf0/RpdjhSK//z9GR+uyMjIBQOnCJCPUhAJAjD5ek3XXA0AvupcDFyoFQNI10XO4fAZAdbEK5FnPB0AYLURU+yEJQA==\",\"dtype\":\"float64\",\"shape\":[20]},\"y\":{\"__ndarray__\":\"AAAAAAAAAADFWC1/bxHFP6nPUEjgx9Q/7XvtDex13j8jRDlroafjPw8+594Mi+c/5D2uWhHK6j8ICxHdA07tPxgu3rRTBe8/qkKlKQXk7z+qQqUpBeTvPxgu3rRTBe8/CQsR3QNO7T/mPa5aEcrqPxA+594Mi+c/JEQ5a6Gn4z/we+0N7HXeP6zPUEjgx9Q/ylgtf28RxT8HXBQzJqahPA==\",\"dtype\":\"float64\",\"shape\":[20]}},\"selected\":{\"id\":\"1046\",\"type\":\"Selection\"},\"selection_policy\":{\"id\":\"1047\",\"type\":\"UnionRenderers\"}},\"id\":\"1034\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"source\":{\"id\":\"1034\",\"type\":\"ColumnDataSource\"}},\"id\":\"1038\",\"type\":\"CDSView\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"line_width\":2,\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1036\",\"type\":\"Line\"},{\"attributes\":{\"axis_label\":\"x\",\"formatter\":{\"id\":\"1043\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1012\",\"type\":\"BasicTicker\"}},\"id\":\"1011\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1041\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1012\",\"type\":\"BasicTicker\"},{\"attributes\":{\"callback\":null},\"id\":\"1003\",\"type\":\"DataRange1d\"},{\"attributes\":{\"ticker\":{\"id\":\"1012\",\"type\":\"BasicTicker\"}},\"id\":\"1015\",\"type\":\"Grid\"},{\"attributes\":{\"axis_label\":\"sin(x)\",\"formatter\":{\"id\":\"1041\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1017\",\"type\":\"BasicTicker\"}},\"id\":\"1016\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1043\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1017\",\"type\":\"BasicTicker\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1045\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"dimension\":1,\"ticker\":{\"id\":\"1017\",\"type\":\"BasicTicker\"}},\"id\":\"1020\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1046\",\"type\":\"Selection\"},{\"attributes\":{\"data_source\":{\"id\":\"1034\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"1035\",\"type\":\"Line\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1036\",\"type\":\"Line\"},\"selection_glyph\":null,\"view\":{\"id\":\"1038\",\"type\":\"CDSView\"}},\"id\":\"1037\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1047\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1040\",\"type\":\"Title\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"line_width\":2,\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1035\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1007\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1009\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1021\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1022\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1045\",\"type\":\"BoxAnnotation\"}},\"id\":\"1023\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"SaveTool\"},{\"attributes\":{\"callback\":null},\"id\":\"1005\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"ResetTool\"}],\"root_ids\":[\"1002\"]},\"title\":\"Bokeh Application\",\"version\":\"1.2.0\"}};\n", 2285 | " var render_items = [{\"docid\":\"94b429f1-3d0e-450e-b888-d10ccfe425f5\",\"roots\":{\"1002\":\"09746ada-162d-4d10-a87a-de1c180e2c0d\"}}];\n", 2286 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 2287 | "\n", 2288 | " }\n", 2289 | " if (root.Bokeh !== undefined) {\n", 2290 | " embed_document(root);\n", 2291 | " } else {\n", 2292 | " var attempts = 0;\n", 2293 | " var timer = setInterval(function(root) {\n", 2294 | " if (root.Bokeh !== undefined) {\n", 2295 | " embed_document(root);\n", 2296 | " clearInterval(timer);\n", 2297 | " }\n", 2298 | " attempts++;\n", 2299 | " if (attempts > 100) {\n", 2300 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 2301 | " clearInterval(timer);\n", 2302 | " }\n", 2303 | " }, 10, root)\n", 2304 | " }\n", 2305 | "})(window);" 2306 | ], 2307 | "application/vnd.bokehjs_exec.v0+json": "" 2308 | }, 2309 | "metadata": { 2310 | "application/vnd.bokehjs_exec.v0+json": { 2311 | "id": "1002" 2312 | } 2313 | }, 2314 | "output_type": "display_data" 2315 | } 2316 | ], 2317 | "source": [ 2318 | "f = plt.figure(x_axis_label='x', y_axis_label='sin(x)')\n", 2319 | "f.line(x, y, line_width=2)\n", 2320 | "plt.show(f)" 2321 | ] 2322 | }, 2323 | { 2324 | "cell_type": "markdown", 2325 | "metadata": {}, 2326 | "source": [ 2327 | "

Pickling objects

" 2328 | ] 2329 | }, 2330 | { 2331 | "cell_type": "markdown", 2332 | "metadata": {}, 2333 | "source": [ 2334 | "There are various file io operations in Python, but one of the easiest is “Pickling”, which attempts to save a Python object to a file for later restoration with the load command." 2335 | ] 2336 | }, 2337 | { 2338 | "cell_type": "code", 2339 | "execution_count": 55, 2340 | "metadata": {}, 2341 | "outputs": [ 2342 | { 2343 | "name": "stdout", 2344 | "output_type": "stream", 2345 | "text": [ 2346 | "Contact info: Joe someone@somewhere.com\n", 2347 | "Contact info: Bob Smith\n" 2348 | ] 2349 | } 2350 | ], 2351 | "source": [ 2352 | "import pickle\n", 2353 | "contacts = [joe, bob] # Make a list of contacts\n", 2354 | "\n", 2355 | "with open('contacts.p', 'wb') as pickle_file: # Make a new file\n", 2356 | " pickle.dump(contacts, pickle_file) # Write contact list\n", 2357 | "\n", 2358 | "with open('contacts.p', 'rb') as pickle_file: # Open the file for reading\n", 2359 | " contacts2 = pickle.load(pickle_file) # Load the pickled contents\n", 2360 | "\n", 2361 | "for elem in contacts2:\n", 2362 | " elem.print_info()" 2363 | ] 2364 | }, 2365 | { 2366 | "cell_type": "markdown", 2367 | "metadata": {}, 2368 | "source": [ 2369 | "The next part of this tutorial introduces basic NEURON commands." 2370 | ] 2371 | }, 2372 | { 2373 | "cell_type": "code", 2374 | "execution_count": null, 2375 | "metadata": {}, 2376 | "outputs": [], 2377 | "source": [] 2378 | } 2379 | ], 2380 | "metadata": { 2381 | "kernelspec": { 2382 | "display_name": "Python 3", 2383 | "language": "python", 2384 | "name": "python3" 2385 | }, 2386 | "language_info": { 2387 | "codemirror_mode": { 2388 | "name": "ipython", 2389 | "version": 3 2390 | }, 2391 | "file_extension": ".py", 2392 | "mimetype": "text/x-python", 2393 | "name": "python", 2394 | "nbconvert_exporter": "python", 2395 | "pygments_lexer": "ipython3", 2396 | "version": "3.7.3" 2397 | } 2398 | }, 2399 | "nbformat": 4, 2400 | "nbformat_minor": 2 2401 | } 2402 | --------------------------------------------------------------------------------