diff --git a/getting_started/01_basics.ipynb b/getting_started/01_basics.ipynb
index a3210a7e20a0249eb372f786bdbd83688074a0d2..81389368f81daa9fa4cd1e982a9c301731bc9e6d 100644
--- a/getting_started/01_basics.ipynb
+++ b/getting_started/01_basics.ipynb
@@ -10,12 +10,46 @@
     "features of python, with emphasis on some of the common difficulties\n",
     "and pitfalls that are commonly encountered when moving to python.\n",
     "\n",
-    "When going through this make sure that you _run_ each code block\n",
-    "and look at the output, as these are crucial for understanding the\n",
-    "explanations. You can run each block by using _shift + enter_ (including the text blocks, so you can just move down the document with shift + enter).\n",
+    "When going through this make sure that you _run_ each code block and\n",
+    "look at the output, as these are crucial for understanding the\n",
+    "explanations. You can run each block by using _shift + enter_\n",
+    "(including the text blocks, so you can just move down the document\n",
+    "with shift + enter).\n",
+    "\n",
+    "It is also possible to _change_ the contents of each code block (these pages are completely interactive) so do experiment with the code you see and try some variations!\n",
+    "\n",
+    "## Contents\n",
+    "\n",
+    "* [Basic types](#Basic-types)\n",
+    " - [Strings](#Strings)\n",
+    "   + [Format](#Format)\n",
+    "   + [String manipulation](#String-manipulation)\n",
+    " - [Tuples and lists](#Tuples-and-lists)\n",
+    "   + [Adding to a list](#Adding-to-a-list)\n",
+    "   + [Indexing](#Indexing)\n",
+    "   + [Slicing](#Slicing)\n",
+    " - [List operations](#List-operations)\n",
+    "   + [Looping over elements in a list (or tuple)](#Looping)\n",
+    "   + [Getting help](#Getting-help)\n",
+    " - [Dictionaries](#Dictionaries)\n",
+    "   + [Adding to a dictionary](#Adding-to-a-dictionary)\n",
+    "   + [Removing elements from a dictionary](#Removing-elements-dictionary)\n",
+    "   + [Looping over everything in a dictionary](#Looping-dictionary)\n",
+    " - [Copying and references](#Copying-and-references)\n",
+    "* [Control flow](#Control-flow)\n",
+    "  - [Boolean operators](#Boolean-operators)\n",
+    "  - [If statements](#If-statements)\n",
+    "  - [For loops](#For-loops)\n",
+    "  - [While loops](#While-loops)\n",
+    "  - [A quick intro to conditional expressions and list comprehensions](#quick-intro)\n",
+    "   + [Conditional expressions](#Conditional-expressions)\n",
+    "   + [List comprehensions](#List-comprehensions)\n",
+    "* [Functions](#functions)\n",
+    "* [Exercise](#exercise)\n",
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"Basic-types\"></a>\n",
     "# Basic types\n",
     "\n",
     "Python has many different types and variables are dynamic and can change types (like MATLAB).  Some of the most commonly used in-built types are:\n",
@@ -72,10 +106,11 @@
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"Strings\"></a>\n",
     "## Strings\n",
     "\n",
     "Strings can be specified using single quotes *or* double quotes - as long as they are matched.\n",
-    "Strings can be dereferenced like lists (see later).\n",
+    "Strings can be indexed like lists (see later).\n",
     "\n",
     "For example:"
    ]
@@ -115,6 +150,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Format\"></a>\n",
     "### Format\n",
     "\n",
     "More interesting strings can be created using the `format` statement, which is very useful in print statements:"
@@ -137,8 +173,9 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "There are also other options along these lines, but this is the more modern version, although you will see plenty of the other alternatives in old code (i.e., code written before last week). \n",
+    "There are also other options along these lines, but this is the more modern version, although you will see plenty of the other alternatives in \"old\" code (to python coders this means anything written before last week).\n",
     "\n",
+    "<a class=\"anchor\" id=\"String-manipulation\"></a>\n",
     "### String manipulation\n",
     "\n",
     "The methods `lower()` and `upper()` are useful for strings.  For example:"
@@ -173,6 +210,23 @@
     "print(s2)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Strings can be concatenated just by using the `+` operator:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s3 = s + ' :: ' + s2\n",
+    "print(s3)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -200,8 +254,7 @@
     "\n",
     "For more information on matching and substitutions, look up the regular expression module on the web.\n",
     "\n",
-    "\n",
-    "You can also split, or tokenize, a string (to turn it into a list) like this:"
+    "Two common and convenient string methods are `strip()` and `split()`.  The first will remove any whitespace at the beginning and end of a string:"
    ]
   },
   {
@@ -210,18 +263,79 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "print(s.split())"
+    "s2 = '   A very    spacy   string       '\n",
+    "print('*' + s2 + '*')\n",
+    "print('*' + s2.strip() + '*')"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "With `split()` we can tokenize a string (to turn it into a list of strings) like this:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print(s.split())\n",
+    "print(s2.split())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "By default it splits at whitespace, but it can also split at a specified delimiter:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s4 = '  This is,  as you can    see ,   a very  weirdly spaced and punctuated    string ...  '\n",
+    "print(s4.split(','))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There are more powerful ways of dealing with this like csv files/strings, which are covered in later practicals, but even this can get you a long way.\n",
+    "\n",
     "> Note that strings in python 3 are _unicode_ so can represent Chinese characters, etc, and is therefore very flexible.  However, in general you can just be blissfully ignorant of this fact.\n",
     "\n",
+    "Strings can be converted to integer or floating-point values by using the `int()` and `float()` calls:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "sint='23'\n",
+    "sfp='2.03'\n",
+    "print(sint + sfp)\n",
+    "print(int(sint) + float(sfp))\n",
+    "print(float(sint) + float(sfp))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "> Note that calling `int()` on a non-integer (e.g., on `sfp` above) will raise an error.\n",
+    "\n",
     "---\n",
     "\n",
-    "## Tuples and Lists\n",
+    "<a class=\"anchor\" id=\"Tuples-and-lists\"></a>\n",
+    "## Tuples and lists\n",
     "\n",
     "Both tuples and lists are builtin python types and are like vectors, \n",
     "but for numerical vectors and arrays it is much better to use _numpy_\n",
@@ -265,6 +379,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Adding-to-a-list\"></a>\n",
     "### Adding to a list\n",
     "\n",
     "This is easy:"
@@ -286,6 +401,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Indexing\"></a>\n",
     "### Indexing\n",
     "\n",
     "Square brackets are used to index tuples, lists, dictionaries, etc.  For example:"
@@ -405,6 +521,7 @@
     "> Note that `len` will only give the length of the top level.\n",
     "> In general, numpy arrays should be preferred to nested lists when the contents are numerical.\n",
     "\n",
+    "<a class=\"anchor\" id=\"Slicing\"></a>\n",
     "### Slicing\n",
     "\n",
     "A range of values for the indices can be specified to extract values from a list.  For example:"
@@ -464,6 +581,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"List-operations\"></a>\n",
     "### List operations\n",
     "\n",
     "Multiplication can be used with lists, where multiplication implements replication."
@@ -504,6 +622,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Looping\"></a>\n",
     "### Looping over elements in a list (or tuple)"
    ]
   },
@@ -524,6 +643,7 @@
    "source": [
     "> Note that the indentation within the loop is _*crucial*_.  All python control blocks are delineated purely by indentation.\n",
     "\n",
+    "<a class=\"anchor\" id=\"Getting-help\"></a>\n",
     "### Getting help\n",
     "\n",
     "The function `help()` can be used to get information about any variable/object/function in python.   It lists the possible operations. In `ipython` you can also just type `?<blah>` or `<blah>?` instead:"
@@ -562,6 +682,7 @@
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"Dictionaries\"></a>\n",
     "## Dictionaries\n",
     "\n",
     "These store key-value pairs.  For example:"
@@ -588,6 +709,7 @@
     "Python is nothing if not flexible.  However, each key must be unique\n",
     "and the dictionary must be \"hashable\".\n",
     "\n",
+    "<a class=\"anchor\" id=\"Adding-to-a-dictionary\"></a>\n",
     "### Adding to a dictionary\n",
     "\n",
     "This is very easy:"
@@ -607,6 +729,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Removing-elements-dictionary\"></a>\n",
     "### Removing elements from a dictionary\n",
     "\n",
     "There are two main approaches - `pop` and `del`:"
@@ -628,6 +751,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"Looping-dictionary\"></a>\n",
     "### Looping over everything in a dictionary\n",
     "\n",
     "Several variables can jointly work as loop variables in python, which is very convenient.  For example:"
@@ -673,6 +797,7 @@
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"Copying-and-references\"></a>\n",
     "## Copying and references \n",
     "\n",
     "In python there are immutable types (e.g. numbers) and mutable types (e.g. lists). The main thing to know is that assignment can sometimes create separate copies and sometimes create references (as in C++). In general, the more complicated types are assigned via references. For example:"
@@ -802,10 +927,16 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "> Note that we have defined some functions here - and the syntax\n",
+    "> should be relatively intuitive.  See <a href=\"#functions\">below</a>\n",
+    "> for a bit more detail on function definitions.\n",
+    "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"Control-flow\"></a>\n",
     "## Control flow\n",
     "\n",
+    "<a class=\"anchor\" id=\"Boolean-operators\"></a>\n",
     "### Boolean operators\n",
     "\n",
     "There is a boolean type in python that can be `True` or `False` (note the capitals). Other values can also be used for True or False (e.g., 1 for True; 0 or None or [] or {} or \"\") although they are not considered 'equal' in the sense that the operator `==` would consider them the same.\n",
@@ -851,6 +982,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"If-statements\"></a>\n",
     "### If statements\n",
     "\n",
     "The basic syntax of `if` statements is fairly standard, though don't forget that you _*must*_ indent the statements within the conditional/loop block as this is the way of delineating blocks of code in python.  For example:"
@@ -899,6 +1031,7 @@
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"For-loops\"></a>\n",
     "### For loops\n",
     "\n",
     "The `for` loop works like in bash:"
@@ -979,6 +1112,7 @@
    "source": [
     "This type of loop can be used with any two lists (or similar) to iterate over them jointly.\n",
     "\n",
+    "<a class=\"anchor\" id=\"While-loops\"></a>\n",
     "### While loops\n",
     "\n",
     "The syntax for this is pretty standard:"
@@ -1009,10 +1143,12 @@
     "\n",
     "---\n",
     "\n",
+    "<a class=\"anchor\" id=\"quick-intro\"></a>\n",
     "### A quick intro to conditional expressions and list comprehensions\n",
     "\n",
     "These are more advanced bits of python but are really useful and common, so worth having a little familiarity with at this stage.\n",
     "\n",
+    "<a class=\"anchor\" id=\"Conditional-expressions\"></a>\n",
     "#### Conditional expressions\n",
     "\n",
     "A general expression that can be used in python is: A `if` condition `else` B\n",
@@ -1036,6 +1172,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "<a class=\"anchor\" id=\"List-comprehensions\"></a>\n",
     "#### List comprehensions\n",
     "\n",
     "This is a shorthand syntax for building a list like a for loop but doing it in one line, and is very popular in python.  It is quite similar to mathematical set notation.  For example:"
@@ -1057,7 +1194,113 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "You'll find that python programmers use this kind of construction _*a lot*_."
+    "You'll find that python programmers use this kind of construction _*a lot*_.\n",
+    "\n",
+    "\n",
+    "---\n",
+    "\n",
+    "<a class=\"anchor\" id=\"functions\"></a>\n",
+    "## Functions\n",
+    "\n",
+    "You will find functions pretty familiar in python to start with,\n",
+    "although they have a few options which are really handy and different\n",
+    "from C++ or matlab (to be covered in a later practical).  To start\n",
+    "with we'll look at a simple function but note a few key points:\n",
+    " * you _must_ indent everything inside the function (it is a code\n",
+    "   block and indentation is the only way of determining this - just\n",
+    "   like for the guts of a loop)\n",
+    " * you can return _whatever you want_ from a python function, but only\n",
+    " a single object - it is usual to package up multiple things in a\n",
+    " tuple or list, which is easily unpacked by the calling invocation:\n",
+    " e.g., `a, b, c = myfunc(x)`\n",
+    " * parameters are passed by reference (see section on <a\n",
+    "    href=\"#Copying-and-references\">copying and references</a>)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def myfunc(x, y, z=0):\n",
+    "    r2 = x*x + y*y + z*z\n",
+    "    r = r2**0.5\n",
+    "    return (r,  r2)\n",
+    "\n",
+    "rad = myfunc(10, 20)\n",
+    "print(rad)\n",
+    "rad, dummy = myfunc(10, 20, 30)\n",
+    "print(rad)\n",
+    "rad, _ = myfunc(10,20,30)\n",
+    "print(rad)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "> Note that the `_` is used as shorthand here for a dummy variable\n",
+    "> that you want to throw away.\n",
+    "\n",
+    "One nice feature of python functions is that you can name the\n",
+    "arguments when you call them, rather than only doing it by position.\n",
+    "For example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def myfunc(x, y, z=0, flag=''):\n",
+    "   if flag=='L1':\n",
+    "       r = abs(x) + abs(y) + abs(z)\n",
+    "   else:\n",
+    "       r = (x*x + y*y + z*z)**0.5\n",
+    "   return r\n",
+    "\n",
+    "rA = myfunc(10, 20)\n",
+    "rB = myfunc(10, 20, flag='L1')\n",
+    "rC = myfunc(10, 20, flag='L1', z=30)\n",
+    "print(rA, rB, rC)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "You will often see python functions called with these named arguments.\n",
+    "\n",
+    "---\n",
+    "\n",
+    "<a class=\"anchor\" id=\"exercise\"></a>\n",
+    "## Exercises\n",
+    "\n",
+    "Let's say you are given a single string with comma separated elements\n",
+    "that represent filenames and ID codes: e.g., `/vols/Data/pytreat/AAC, 165873, /vols/Data/pytreat/AAG, 170285, ...`\n",
+    "\n",
+    "Write some code to do the following: \n",
+    " * separate out the filenames and ID codes into separate lists (ID's\n",
+    " should be numerical values, not strings)\n",
+    " * loop over the two and generate a _string_ that could be used to\n",
+    "   rename the directories (e.g., `mv /vols/Data/pytreat/AAC /vols/Data/pytreat/S165873`) - we will cover how to actually execute these in a later practical\n",
+    " * convert your dual lists into a dictionary, with ID as the key\n",
+    " * write a small function to determine if an ID is present in this\n",
+    " set of not, and also return the filename if it is\n",
+    " * write a for loop to create a list of all the odd-numbered IDs (you can use the `%` operator for modulus - i.e., `5 % 2` is 1)\n",
+    " * re-write the for loop as a list comprehension\n",
+    " * now generate a list of the filenames corresponding to these odd-numbered IDs"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mstr = '/vols/Data/pytreat/AAC, 165873,  /vols/Data/pytreat/AAG,   170285, /vols/Data/pytreat/AAH, 196792, /vols/Data/pytreat/AAK, 212577, /vols/Data/pytreat/AAQ, 385376, /vols/Data/pytreat/AB, 444600, /vols/Data/pytreat/AC6, 454578, /vols/Data/pytreat/V8, 501502,   /vols/Data/pytreat/2YK, 667688, /vols/Data/pytreat/C3PO, 821971'"
    ]
   }
  ],
diff --git a/getting_started/01_basics.md b/getting_started/01_basics.md
index 2a670d419defbca067d5f750357f2eca3dbded04..8f8394001dbcfdcb191824f418780e4ac2bbb832 100644
--- a/getting_started/01_basics.md
+++ b/getting_started/01_basics.md
@@ -4,12 +4,46 @@ This tutorial is aimed at briefly introducing you to the main language
 features of python, with emphasis on some of the common difficulties
 and pitfalls that are commonly encountered when moving to python.
 
-When going through this make sure that you _run_ each code block
-and look at the output, as these are crucial for understanding the
-explanations. You can run each block by using _shift + enter_ (including the text blocks, so you can just move down the document with shift + enter).
+When going through this make sure that you _run_ each code block and
+look at the output, as these are crucial for understanding the
+explanations. You can run each block by using _shift + enter_
+(including the text blocks, so you can just move down the document
+with shift + enter).
+
+It is also possible to _change_ the contents of each code block (these pages are completely interactive) so do experiment with the code you see and try some variations!
+
+## Contents
+
+* [Basic types](#Basic-types)
+ - [Strings](#Strings)
+   + [Format](#Format)
+   + [String manipulation](#String-manipulation)
+ - [Tuples and lists](#Tuples-and-lists)
+   + [Adding to a list](#Adding-to-a-list)
+   + [Indexing](#Indexing)
+   + [Slicing](#Slicing)
+ - [List operations](#List-operations)
+   + [Looping over elements in a list (or tuple)](#Looping)
+   + [Getting help](#Getting-help)
+ - [Dictionaries](#Dictionaries)
+   + [Adding to a dictionary](#Adding-to-a-dictionary)
+   + [Removing elements from a dictionary](#Removing-elements-dictionary)
+   + [Looping over everything in a dictionary](#Looping-dictionary)
+ - [Copying and references](#Copying-and-references)
+* [Control flow](#Control-flow)
+  - [Boolean operators](#Boolean-operators)
+  - [If statements](#If-statements)
+  - [For loops](#For-loops)
+  - [While loops](#While-loops)
+  - [A quick intro to conditional expressions and list comprehensions](#quick-intro)
+   + [Conditional expressions](#Conditional-expressions)
+   + [List comprehensions](#List-comprehensions)
+* [Functions](#functions)
+* [Exercise](#exercise)
 
 ---
 
+<a class="anchor" id="Basic-types"></a>
 # Basic types
 
 Python has many different types and variables are dynamic and can change types (like MATLAB).  Some of the most commonly used in-built types are:
@@ -45,6 +79,7 @@ print(a, b, c)
 
 ---
 
+<a class="anchor" id="Strings"></a>
 ## Strings
 
 Strings can be specified using single quotes *or* double quotes - as long as they are matched.
@@ -66,6 +101,7 @@ multiple lines
 print(s3)
 ```
 
+<a class="anchor" id="Format"></a>
 ### Format
 
 More interesting strings can be created using the `format` statement, which is very useful in print statements:
@@ -77,8 +113,9 @@ print(s)
 print('A name is {} and a number is {}'.format(y, x))
 ```
 
-There are also other options along these lines, but this is the more modern version, although you will see plenty of the other alternatives in old code (i.e., code written before last week). 
+There are also other options along these lines, but this is the more modern version, although you will see plenty of the other alternatives in "old" code (to python coders this means anything written before last week).
 
+<a class="anchor" id="String-manipulation"></a>
 ### String manipulation
 
 The methods `lower()` and `upper()` are useful for strings.  For example:
@@ -95,6 +132,13 @@ s2 = s.replace('Test', 'Better')
 print(s2)
 ```
 
+Strings can be concatenated just by using the `+` operator:
+
+```
+s3 = s + ' :: ' + s2
+print(s3)
+```
+
 If you like regular expressions then you're in luck as these are well supported in python using the `re` module.  To use this (like many other "extensions" - called _modules_ in Python - you need to `import` it).  For example:
 ```
 import re
@@ -106,17 +150,46 @@ where the `r` before the quote is used to force the regular expression specifica
 
 For more information on matching and substitutions, look up the regular expression module on the web.
 
+Two common and convenient string methods are `strip()` and `split()`.  The first will remove any whitespace at the beginning and end of a string:
 
-You can also split, or tokenize, a string (to turn it into a list) like this:
+```
+s2 = '   A very    spacy   string       '
+print('*' + s2 + '*')
+print('*' + s2.strip() + '*')
+```
+
+With `split()` we can tokenize a string (to turn it into a list of strings) like this:
 ```
 print(s.split())
+print(s2.split())
+```
+
+By default it splits at whitespace, but it can also split at a specified delimiter:
+```
+s4 = '  This is,  as you can    see ,   a very  weirdly spaced and punctuated    string ...  '
+print(s4.split(','))
 ```
 
+There are more powerful ways of dealing with this like csv files/strings, which are covered in later practicals, but even this can get you a long way.
+
 > Note that strings in python 3 are _unicode_ so can represent Chinese characters, etc, and is therefore very flexible.  However, in general you can just be blissfully ignorant of this fact.
 
+Strings can be converted to integer or floating-point values by using the `int()` and `float()` calls:
+
+```
+sint='23'
+sfp='2.03'
+print(sint + sfp)
+print(int(sint) + float(sfp))
+print(float(sint) + float(sfp))
+```
+
+> Note that calling `int()` on a non-integer (e.g., on `sfp` above) will raise an error.
+
 ---
 
-## Tuples and Lists
+<a class="anchor" id="Tuples-and-lists"></a>
+## Tuples and lists
 
 Both tuples and lists are builtin python types and are like vectors, 
 but for numerical vectors and arrays it is much better to use _numpy_
@@ -138,6 +211,7 @@ print('x2 is: ', x2)
 print('x3 is: ', x3)
 ```
 
+<a class="anchor" id="Adding-to-a-list"></a>
 ### Adding to a list
 
 This is easy:
@@ -148,6 +222,7 @@ a +=  [80]
 print(a)
 ```
 
+<a class="anchor" id="Indexing"></a>
 ### Indexing
 
 Square brackets are used to index tuples, lists, dictionaries, etc.  For example:
@@ -195,6 +270,7 @@ but *not* an index like b[0, 1].
 > Note that `len` will only give the length of the top level.
 > In general, numpy arrays should be preferred to nested lists when the contents are numerical.
 
+<a class="anchor" id="Slicing"></a>
 ### Slicing
 
 A range of values for the indices can be specified to extract values from a list.  For example:
@@ -223,6 +299,7 @@ b = [3, 4]
 print(a[b])
 ```
 
+<a class="anchor" id="List-operations"></a>
 ### List operations
 
 Multiplication can be used with lists, where multiplication implements replication.
@@ -242,6 +319,7 @@ d.pop(0)
 print(d)
 ```
 
+<a class="anchor" id="Looping"></a>
 ### Looping over elements in a list (or tuple)
 
 ```
@@ -252,6 +330,7 @@ for x in d:
 
 > Note that the indentation within the loop is _*crucial*_.  All python control blocks are delineated purely by indentation.
 
+<a class="anchor" id="Getting-help"></a>
 ### Getting help
 
 The function `help()` can be used to get information about any variable/object/function in python.   It lists the possible operations. In `ipython` you can also just type `?<blah>` or `<blah>?` instead:
@@ -272,6 +351,7 @@ dir(d)
 
 ---
 
+<a class="anchor" id="Dictionaries"></a>
 ## Dictionaries
 
 These store key-value pairs.  For example:
@@ -287,6 +367,7 @@ The keys and values can take on almost any type, even dictionaries!
 Python is nothing if not flexible.  However, each key must be unique
 and the dictionary must be "hashable".
 
+<a class="anchor" id="Adding-to-a-dictionary"></a>
 ### Adding to a dictionary
 
 This is very easy:
@@ -295,7 +376,7 @@ e['c'] = 555   # just like in Biobank!  ;)
 print(e)
 ```
 
-
+<a class="anchor" id="Removing-elements-dictionary"></a>
 ### Removing elements from a dictionary
 
 There are two main approaches - `pop` and `del`:
@@ -306,6 +387,7 @@ del e['c']
 print(e)
 ```
 
+<a class="anchor" id="Looping-dictionary"></a>
 ### Looping over everything in a dictionary
 
 Several variables can jointly work as loop variables in python, which is very convenient.  For example:
@@ -329,6 +411,7 @@ for k in e:
 
 ---
 
+<a class="anchor" id="Copying-and-references"></a>
 ## Copying and references 
 
 In python there are immutable types (e.g. numbers) and mutable types (e.g. lists). The main thing to know is that assignment can sometimes create separate copies and sometimes create references (as in C++). In general, the more complicated types are assigned via references. For example:
@@ -393,10 +476,16 @@ foo3(a)
 print(a)
 ```
 
+> Note that we have defined some functions here - and the syntax
+> should be relatively intuitive.  See <a href="#functions">below</a>
+> for a bit more detail on function definitions.
+
 ---
 
+<a class="anchor" id="Control-flow"></a>
 ## Control flow
 
+<a class="anchor" id="Boolean-operators"></a>
 ### Boolean operators
 
 There is a boolean type in python that can be `True` or `False` (note the capitals). Other values can also be used for True or False (e.g., 1 for True; 0 or None or [] or {} or "") although they are not considered 'equal' in the sense that the operator `==` would consider them the same.
@@ -420,7 +509,7 @@ print('of' in 'a number of words')
 print(3 in [1, 2, 3, 4])
 ```
 
-
+<a class="anchor" id="If-statements"></a>
 ### If statements
 
 The basic syntax of `if` statements is fairly standard, though don't forget that you _*must*_ indent the statements within the conditional/loop block as this is the way of delineating blocks of code in python.  For example:
@@ -446,6 +535,7 @@ This can be useful for functions where a variety of possible input types are bei
 
 ---
 
+<a class="anchor" id="For-loops"></a>
 ### For loops
 
 The `for` loop works like in bash:
@@ -480,6 +570,7 @@ for x, y in zip(alist, blist):
 
 This type of loop can be used with any two lists (or similar) to iterate over them jointly.
 
+<a class="anchor" id="While-loops"></a>
 ### While loops
 
 The syntax for this is pretty standard:
@@ -499,10 +590,12 @@ You can also use `continue` as in other languages.
 
 ---
 
+<a class="anchor" id="quick-intro"></a>
 ### A quick intro to conditional expressions and list comprehensions
 
 These are more advanced bits of python but are really useful and common, so worth having a little familiarity with at this stage.
 
+<a class="anchor" id="Conditional-expressions"></a>
 #### Conditional expressions
 
 A general expression that can be used in python is: A `if` condition `else` B
@@ -515,7 +608,7 @@ y = x**2 if x<0.5 else (1 - x)**2
 print(x, y)
 ```
 
-
+<a class="anchor" id="List-comprehensions"></a>
 #### List comprehensions
 
 This is a shorthand syntax for building a list like a for loop but doing it in one line, and is very popular in python.  It is quite similar to mathematical set notation.  For example:
@@ -529,6 +622,83 @@ print(v2)
 You'll find that python programmers use this kind of construction _*a lot*_.
 
 
+---
+
+<a class="anchor" id="functions"></a>
+## Functions
+
+You will find functions pretty familiar in python to start with,
+although they have a few options which are really handy and different
+from C++ or matlab (to be covered in a later practical).  To start
+with we'll look at a simple function but note a few key points:
+ * you _must_ indent everything inside the function (it is a code
+   block and indentation is the only way of determining this - just
+   like for the guts of a loop)
+ * you can return _whatever you want_ from a python function, but only
+ a single object - it is usual to package up multiple things in a
+ tuple or list, which is easily unpacked by the calling invocation:
+ e.g., `a, b, c = myfunc(x)`
+ * parameters are passed by reference (see section on <a
+    href="#Copying-and-references">copying and references</a>)
+
+```
+def myfunc(x, y, z=0):
+    r2 = x*x + y*y + z*z
+    r = r2**0.5
+    return (r,  r2)
+
+rad = myfunc(10, 20)
+print(rad)
+rad, dummy = myfunc(10, 20, 30)
+print(rad)
+rad, _ = myfunc(10,20,30)
+print(rad)
+```
+
+> Note that the `_` is used as shorthand here for a dummy variable
+> that you want to throw away.
+
+One nice feature of python functions is that you can name the
+arguments when you call them, rather than only doing it by position.
+For example:
+```
+def myfunc(x, y, z=0, flag=''):
+   if flag=='L1':
+       r = abs(x) + abs(y) + abs(z)
+   else:
+       r = (x*x + y*y + z*z)**0.5
+   return r
+
+rA = myfunc(10, 20)
+rB = myfunc(10, 20, flag='L1')
+rC = myfunc(10, 20, flag='L1', z=30)
+print(rA, rB, rC)
+```
 
+You will often see python functions called with these named arguments.
+
+---
+
+<a class="anchor" id="exercise"></a>
+## Exercises
+
+Let's say you are given a single string with comma separated elements
+that represent filenames and ID codes: e.g., `/vols/Data/pytreat/AAC, 165873, /vols/Data/pytreat/AAG, 170285, ...`
+
+Write some code to do the following: 
+ * separate out the filenames and ID codes into separate lists (ID's
+ should be numerical values, not strings)
+ * loop over the two and generate a _string_ that could be used to
+   rename the directories (e.g., `mv /vols/Data/pytreat/AAC /vols/Data/pytreat/S165873`) - we will cover how to actually execute these in a later practical
+ * convert your dual lists into a dictionary, with ID as the key
+ * write a small function to determine if an ID is present in this
+ set of not, and also return the filename if it is
+ * write a for loop to create a list of all the odd-numbered IDs (you can use the `%` operator for modulus - i.e., `5 % 2` is 1)
+ * re-write the for loop as a list comprehension
+ * now generate a list of the filenames corresponding to these odd-numbered IDs
+
+```
+mstr = '/vols/Data/pytreat/AAC, 165873,  /vols/Data/pytreat/AAG,   170285, /vols/Data/pytreat/AAH, 196792, /vols/Data/pytreat/AAK, 212577, /vols/Data/pytreat/AAQ, 385376, /vols/Data/pytreat/AB, 444600, /vols/Data/pytreat/AC6, 454578, /vols/Data/pytreat/V8, 501502,   /vols/Data/pytreat/2YK, 667688, /vols/Data/pytreat/C3PO, 821971'
+```