diff --git a/getting_started/basics.ipynb b/getting_started/basics.ipynb index 9facb873c7f07175e4c0508cfb7b244ce956e607..7ab8b678019fbaf1c5021c4d218eb8b7de66b506 100644 --- a/getting_started/basics.ipynb +++ b/getting_started/basics.ipynb @@ -4,6 +4,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "# Basic python\n", + "\n", + "This tutorial is aimed at briefly introducing you to the main language\n", + "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_.\n", + "\n", + "---\n", + "\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", @@ -26,14 +38,15 @@ "b = 3.6\n", "c = 'abc'\n", "d = [10,20,30]\n", - "e = {'a' : 10, 'b': 20}" + "e = {'a' : 10, 'b': 20}\n", + "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Any variable can be printed using the function `print()`:" + "Any variable or combination of variables can be printed using the function `print()`:" ] }, { @@ -43,7 +56,8 @@ "outputs": [], "source": [ "print(d)\n", - "print(e)" + "print(e)\n", + "print(a,b,c)" ] }, { @@ -100,10 +114,134 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### Format\n", + "\n", + "More interesting strings can be created using the `format` statement, which is very useful in print statements:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x=1\n", + "y='PyTreat'\n", + "s='The numerical value is {} and a name is {}'.format(x,y)\n", + "print(s)\n", + "print('A name is {} and a number is {}'.format(y,x))" + ] + }, + { + "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", + "\n", + "### String manipulation\n", + "\n", + "The methods `lower()` and `upper()` are useful for strings. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s='This is a Test String'\n", + "print(s.upper())\n", + "print(s.lower())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another useful method is:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s='This is a Test String'\n", + "s2=s.replace('Test','Better')\n", + "print(s2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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\" you need to `import` it). For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is an example of an example String\n" + ] + } + ], + "source": [ + "import re\n", + "s='This is a test of a Test String'\n", + "s1=re.sub(r'a [Tt]est', \"an example\", s)\n", + "print(s1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where the `r` before the quote is used to force the regular expression specification to be a `raw string`.\n", + "\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:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['This', 'is', 'a', 'test', 'of', 'a', 'Test', 'String']\n" + ] + } + ], + "source": [ + "print(s.split())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> 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", "---\n", "\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", + "arrays (or matrices), which are covered in a later tutorial.\n", + "\n", "A tuple is like a list or a vector, but with less flexibility than a full list, however anything can be stored in either a list or tuple, without any consistency being required. For example:" ] }, @@ -114,7 +252,9 @@ "outputs": [], "source": [ "xtuple=(3, 7.6, 'str')\n", - "xlist=[1,'mj',-5.4]" + "xlist=[1,'mj',-5.4]\n", + "print(xtuple)\n", + "print(xlist)" ] }, { @@ -132,8 +272,8 @@ "source": [ "x2=(xtuple,xlist)\n", "x3=[xtuple,xlist]\n", - "print(x2)\n", - "print(x3)" + "print('x2 is: ',x2)\n", + "print('x3 is: ',x3)" ] }, { @@ -161,9 +301,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Dereferencing\n", + "### Indexing\n", "\n", - "Square brackets are used to dereference tuples, lists, dictionaries, etc. For example:" + "Square brackets are used to index tuples, lists, dictionaries, etc. For example:" ] }, { @@ -257,7 +397,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Nested lists can have nested dereferences:" + "Nested lists can have nested indexing:" ] }, { @@ -275,10 +415,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "but *not* a dereference like b[0,1].\n", + "but *not* an index like b[0,1].\n", "\n", "> Note that `len` will only give the length of the top level.\n", - "> In general arrays should be preferred to nested lists when the contents are numerical.\n", + "> In general, numpy arrays should be preferred to nested lists when the contents are numerical.\n", "\n", "### Slicing\n", "\n", @@ -300,7 +440,8 @@ "source": [ "> _*Pitfall:*_\n", ">\n", - "> Slicing syntax is different from MATLAB in that second number is one plus final index - this is in addition to the zero index difference." + "> Slicing syntax is different from MATLAB in that second number is\n", + "> exclusive (i.e., one plus final index) - this is in addition to the zero index difference." ] }, { @@ -320,7 +461,8 @@ "source": [ "> _*Pitfall:*_\n", ">\n", - "> Unlike in MATLAB, you cannot use a list as indices instead of an integer or a slice" + "> Unlike in MATLAB, you cannot use a list as indices instead of an\n", + "> integer or a slice (although these can be done in _numpy_)." ] }, { @@ -399,7 +541,23 @@ "\n", "### Getting help\n", "\n", - "The function `dir()` can be used to get information about any variable/object/function in python. It lists the possible operations." + "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:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "help(d)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is also a `dir()` function that gives a basic listing of the operations:" ] }, { @@ -441,7 +599,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The keys and values can take on any type, even dictionaries! Python is nothing if not flexible. However, each key must be unique.\n", + "The keys and values can take on almost any type, even dictionaries!\n", + "Python is nothing if not flexible. However, each key must be unique\n", + "and the dictionary must be \"hashable\".\n", "\n", "### Adding to a dictionary\n", "\n", @@ -523,6 +683,8 @@ "metadata": {}, "source": [ "> Note that in both cases the order is arbitrary. The `sorted` function can be used if you want keys in a sorted order; e.g. `for k in sorted(e):` ...\n", + ">\n", + "> There are also other options if you want a dictionary with ordering.\n", "\n", "---\n", "\n", @@ -585,7 +747,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If an explicit copy is necessary then this can be made using the `copy()` function:" + "If an explicit copy is necessary then this can be made using the `list()` constructor:" ] }, { @@ -595,7 +757,7 @@ "outputs": [], "source": [ "a = [7]\n", - "b = a.copy()\n", + "b = list(a)\n", "a[0] = 8888\n", "print(b)" ] @@ -604,7 +766,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "> When writing functions this is something to be particularly careful about." + "There is a constructor for each type and this con be useful for converting between types:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "xt=(2,5,7)\n", + "xl=list(xt)\n", + "print(xt)\n", + "print(xl)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> _*Pitfall:*_\n", + ">\n", + "> When writing functions you need to be particularly careful about references and copies." ] }, { @@ -638,14 +821,280 @@ "\n", "## Control flow\n", "\n", - " - boolean operators\n", - " - if/else/for\n", - " - a if condition else b\n", - " - introduce range/enumerate" + "### 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", + "\n", + "Relevant boolean and comparison operators include: `not`, `and`, `or`, `==` and `!=`\n", + "\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a=True\n", + "print('Not a is:', not a)\n", + "print('Not 1 is:', not 1)\n", + "print('Not 0 is:', not 0)\n", + "print('Not {} is:', not {})\n", + "print('{}==0 is:', {}==0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is also the `in` test for strings, lists, etc:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('the' in 'a number of words')\n", + "print('of' in 'a number of words')\n", + "print(3 in [1,2,3,4])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 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:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "a=random.uniform(-1,1)\n", + "print(a)\n", + "if a>0:\n", + " print('Positive')\n", + "elif a<0:\n", + " print('Negative')\n", + "else:\n", + " print('Zero')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or more generally:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a=[] # just one of many examples\n", + "if not a:\n", + " print('Variable is true, or at least not empty')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This can be useful for functions where a variety of possible input types are being dealt with. \n", + "\n", + "---\n", + "\n", + "### For loops\n", + "\n", + "The `for` loop works like in bash:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in [2, 'is', 'more', 'than', 1]:\n", + " print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where a list or any other sequence (e.g. tuple) can be used.\n", + "\n", + "If you want a numerical range then use:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in range(2,9):\n", + " print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that, like slicing, the maximum value is one less than the value specified. Also, `range` actually returns an object that can be iterated over but is not just a list of numbers. If you want a list of numbers then `list(range(2,9))` will give you this.\n", + "\n", + "A very nice feature of python is that multiple variables can be assigned from a tuple or list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x,y = [4, 7]\n", + "print(x)\n", + "print(y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and this can be combined with a function called `zip` to make very convenient dual variable loops:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "alist=['Some', 'set', 'of', 'items']\n", + "blist=list(range(len(alist)))\n", + "print(list(zip(alist,blist)))\n", + "for x,y in zip(alist,blist):\n", + " print(y,x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This type of loop can be used with any two lists (or similar) to iterate over them jointly.\n", + "\n", + "### While loops\n", + "\n", + "The syntax for this is pretty standard:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "n=0\n", + "x=0\n", + "while n<100:\n", + " x+=random.uniform(0,1)**2 # where ** is a power operation\n", + " if x>50:\n", + " break\n", + " n+=1\n", + "print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use `continue` as in other languages.\n", + "\n", + "---\n", + "\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", + "#### Conditional expressions\n", + "\n", + "A general expression that can be used in python is: A `if` condition `else` B\n", + "\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "x = random.uniform(0,1)\n", + "y = x**2 if x<0.5 else (1-x)**2\n", + "print(x,y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 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:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "v1 = [ x**2 for x in range(10) ]\n", + "print(v1)\n", + "v2 = [ x**2 for x in range(10) if x!=7 ]\n", + "print(v2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You'll find that python programmers use this kind of construction _*a lot*_." ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, "nbformat": 4, "nbformat_minor": 2 } diff --git a/getting_started/basics.md b/getting_started/basics.md index 44da7c938fa178738cde1f5884ed43049e1f85f3..022c4b96ebfe21b568d7042dcf2b0d4be59f309e 100644 --- a/getting_started/basics.md +++ b/getting_started/basics.md @@ -1,3 +1,15 @@ +# Basic python + +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_. + +--- + # 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: @@ -15,12 +27,14 @@ b = 3.6 c = 'abc' d = [10,20,30] e = {'a' : 10, 'b': 20} +print(a) ``` -Any variable can be printed using the function `print()`: +Any variable or combination of variables can be printed using the function `print()`: ``` print(d) print(e) +print(a,b,c) ``` > _*Python 3 versus python 2*_: @@ -51,22 +65,76 @@ multiple lines print(s3) ``` +### Format + +More interesting strings can be created using the `format` statement, which is very useful in print statements: +``` +x=1 +y='PyTreat' +s='The numerical value is {} and a name is {}'.format(x,y) +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). + +### String manipulation + +The methods `lower()` and `upper()` are useful for strings. For example: +``` +s='This is a Test String' +print(s.upper()) +print(s.lower()) +``` + +Another useful method is: +``` +s='This is a Test String' +s2=s.replace('Test','Better') +print(s2) +``` + +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" you need to `import` it). For example: +``` +import re +s='This is a test of a Test String' +s1=re.sub(r'a [Tt]est', "an example", s) +print(s1) +``` +where the `r` before the quote is used to force the regular expression specification to be a `raw string`. + +For more information on matching and substitutions, look up the regular expression module on the web. + + +You can also split, or tokenize, a string (to turn it into a list) like this: +``` +print(s.split()) +``` + +> 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. + --- ## 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_ +arrays (or matrices), which are covered in a later tutorial. + A tuple is like a list or a vector, but with less flexibility than a full list, however anything can be stored in either a list or tuple, without any consistency being required. For example: ``` xtuple=(3, 7.6, 'str') xlist=[1,'mj',-5.4] +print(xtuple) +print(xlist) ``` They can also be nested: ``` x2=(xtuple,xlist) x3=[xtuple,xlist] -print(x2) -print(x3) +print('x2 is: ',x2) +print('x3 is: ',x3) ``` ### Adding to a list @@ -79,9 +147,9 @@ a += [80] print(a) ``` -### Dereferencing +### Indexing -Square brackets are used to dereference tuples, lists, dictionaries, etc. For example: +Square brackets are used to index tuples, lists, dictionaries, etc. For example: ``` d = [10,20,30] print(d[1]) @@ -115,16 +183,16 @@ Length of a tuple or list is given by the `len()` function: print(len(a)) ``` -Nested lists can have nested dereferences: +Nested lists can have nested indexing: ``` b=[[10,20,30],[40,50,60]] print(b[0][1]) print(b[1][0]) ``` -but *not* a dereference like b[0,1]. +but *not* an index like b[0,1]. > Note that `len` will only give the length of the top level. -> In general arrays should be preferred to nested lists when the contents are numerical. +> In general, numpy arrays should be preferred to nested lists when the contents are numerical. ### Slicing @@ -135,7 +203,8 @@ print(a[0:3]) > _*Pitfall:*_ > -> Slicing syntax is different from MATLAB in that second number is one plus final index - this is in addition to the zero index difference. +> Slicing syntax is different from MATLAB in that second number is +> exclusive (i.e., one plus final index) - this is in addition to the zero index difference. ``` a=[10,20,30,40,50,60] @@ -145,7 +214,8 @@ print(a[1:3]) # same as a(2:3) in MATLAB > _*Pitfall:*_ > -> Unlike in MATLAB, you cannot use a list as indices instead of an integer or a slice +> Unlike in MATLAB, you cannot use a list as indices instead of an +> integer or a slice (although these can be done in _numpy_). ``` b=[3,4] @@ -183,12 +253,20 @@ for x in d: ### Getting help -The function `dir()` can be used to get information about any variable/object/function in python. It lists the possible operations. +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: + +``` +help(d) +``` + + +There is also a `dir()` function that gives a basic listing of the operations: ``` dir(d) ``` + > Note that google is often more helpful! --- @@ -204,7 +282,9 @@ print(e.values()) print(e['a']) ``` -The keys and values can take on any type, even dictionaries! Python is nothing if not flexible. However, each key must be unique. +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". ### Adding to a dictionary @@ -243,6 +323,8 @@ for k in e: ``` > Note that in both cases the order is arbitrary. The `sorted` function can be used if you want keys in a sorted order; e.g. `for k in sorted(e):` ... +> +> There are also other options if you want a dictionary with ordering. --- @@ -272,15 +354,25 @@ a[0] = 8888 print(b) ``` -If an explicit copy is necessary then this can be made using the `copy()` function: +If an explicit copy is necessary then this can be made using the `list()` constructor: ``` a = [7] -b = a.copy() +b = list(a) a[0] = 8888 print(b) ``` -> When writing functions this is something to be particularly careful about. +There is a constructor for each type and this con be useful for converting between types: +``` +xt=(2,5,7) +xl=list(xt) +print(xt) +print(xl) +``` + +> _*Pitfall:*_ +> +> When writing functions you need to be particularly careful about references and copies. ``` def foo1(x): @@ -304,8 +396,138 @@ print(a) ## Control flow - - boolean operators - - if/else/for - - a if condition else b - - introduce range/enumerate +### 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. + +Relevant boolean and comparison operators include: `not`, `and`, `or`, `==` and `!=` + +For example: +``` +a=True +print('Not a is:', not a) +print('Not 1 is:', not 1) +print('Not 0 is:', not 0) +print('Not {} is:', not {}) +print('{}==0 is:', {}==0) +``` + +There is also the `in` test for strings, lists, etc: +``` +print('the' in 'a number of words') +print('of' in 'a number of words') +print(3 in [1,2,3,4]) +``` + + +### 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: +``` +import random +a=random.uniform(-1,1) +print(a) +if a>0: + print('Positive') +elif a<0: + print('Negative') +else: + print('Zero') +``` + +Or more generally: +``` +a=[] # just one of many examples +if not a: + print('Variable is true, or at least not empty') +``` +This can be useful for functions where a variety of possible input types are being dealt with. + +--- + +### For loops + +The `for` loop works like in bash: +``` +for x in [2, 'is', 'more', 'than', 1]: + print(x) +``` +where a list or any other sequence (e.g. tuple) can be used. + +If you want a numerical range then use: +``` +for x in range(2,9): + print(x) +``` +Note that, like slicing, the maximum value is one less than the value specified. Also, `range` actually returns an object that can be iterated over but is not just a list of numbers. If you want a list of numbers then `list(range(2,9))` will give you this. + +A very nice feature of python is that multiple variables can be assigned from a tuple or list: +``` +x,y = [4, 7] +print(x) +print(y) +``` + +and this can be combined with a function called `zip` to make very convenient dual variable loops: +``` +alist=['Some', 'set', 'of', 'items'] +blist=list(range(len(alist))) +print(list(zip(alist,blist))) +for x,y in zip(alist,blist): + print(y,x) +``` + +This type of loop can be used with any two lists (or similar) to iterate over them jointly. + +### While loops + +The syntax for this is pretty standard: +``` +import random +n=0 +x=0 +while n<100: + x+=random.uniform(0,1)**2 # where ** is a power operation + if x>50: + break + n+=1 +print(x) +``` + +You can also use `continue` as in other languages. + +--- + +### 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. + +#### Conditional expressions + +A general expression that can be used in python is: A `if` condition `else` B + +For example: +``` +import random +x = random.uniform(0,1) +y = x**2 if x<0.5 else (1-x)**2 +print(x,y) +``` + + +#### 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: +``` +v1 = [ x**2 for x in range(10) ] +print(v1) +v2 = [ x**2 for x in range(10) if x!=7 ] +print(v2) +``` + +You'll find that python programmers use this kind of construction _*a lot*_. + + + + diff --git a/getting_started/nifti.md b/getting_started/nifti.md new file mode 100644 index 0000000000000000000000000000000000000000..b733366acb81becfb57d7ddc6e2b90062fb9f768 --- /dev/null +++ b/getting_started/nifti.md @@ -0,0 +1,3 @@ +# NIFTI images and python + + diff --git a/getting_started/scripts.ipynb b/getting_started/scripts.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f68391d1405ee1c8e483f7cc8cb9780342a92ef4 --- /dev/null +++ b/getting_started/scripts.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Callable scripts in python\n", + "\n", + "In this tutorial we will cover how to write simple stand-alone scripts in python that can be used as alternatives to bash scripts.\n", + "\n", + "There are some code blocks within this webpage, but we recommend that you write the code in an IDE or editor instead and then run the scripts from a terminal.\n", + "\n", + "## Basic script\n", + "\n", + "The first line of a python script is usually:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "which invokes whichever version of python can be found by `/usr/bin/env` since python can be located in many different places.\n", + "\n", + "For FSL scripts we use an alternative, to ensure that we pick up the version of python (and associated packages) that we ship with FSL. To do this we use the line:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env fslpython" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After this line the rest of the file just uses regular python syntax, as in the other tutorials.\n", + "\n", + "## Calling other executables\n", + "\n", + "The most essential call that you need to use to replicate the way a bash script calls executables is `subprocess.run()`. A simple call looks like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "subprocess.run([\"ls\",\"-la\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To suppress the output do this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sp=subprocess.run([\"ls\"],stdout=subprocess.PIPE)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To store the output do this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sp=subprocess.run(\"ls -la\".split(),stdout=subprocess.PIPE)\n", + "sout=sp.stdout.decode('utf-8')\n", + "print(sout)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the `decode` call in the middle line converts the string from a byte string to a normal string.\n", + "\n", + "If the output is numerical then this can be extracted like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "fsldir=os.getenv('FSLDIR')\n", + "sp=subprocess.run([fsldir+'/bin/fslstats',fsldir+'/data/standard/MNI152_T1_1mm_brain','-V'],stdout=subprocess.PIPE)\n", + "sout=sp.stdout.decode('utf-8')\n", + "vol_vox=float(sout.split()[0])\n", + "vol_mm=float(sout.split()[1])\n", + "print('Volumes are: ',vol_vox,' in voxels and ',vol_mm,' in mm')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Command line arguments\n", + "\n", + "The simplest way of dealing with command line arguments is use the module `sys`, which gives access to an `argv` list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "print(len(sys.argv))\n", + "print(sys.argv[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are also some modules that are useful for parsing arguments:\n", + " - `getopt`\n", + " - `argparse`\n", + " and you can find good documentation and examples of these on the web.\n", + "\n", + "\n", + "## Example script\n", + "\n", + "Here is a simple bash script (it masks an image and calculates volumes - just as random examples):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/bin/bash\n", + "if [ $# -lt 2 ] ; then\n", + " echo \"Usage: $0 <input image> <output image>\"\n", + " exit 1\n", + "fi\n", + "infile=$1\n", + "outfile=$2\n", + "# mask input image with MNI\n", + "$FSLDIR/bin/fslmaths $infile -mas $FSLDIR/data/standard/MNI152_T1_1mm_brain $outfile\n", + "# calculate volumes of masked image \n", + "vv=`$FSLDIR/bin/fslstats $outfile -V`\n", + "vol_vox=`echo $vv | awk '{ print $1 }'`\n", + "vol_mm=`echo $vv | awk '{ print $2 }'`\n", + "echo \"Volumes are: $vol_vox in voxels and $vol_mm in mm\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And an alternative in python:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env fslpython\n", + "import os, sys, subprocess\n", + "fsldir=os.getenv('FSLDIR')\n", + "if len(sys.argv)<2:\n", + " print('Usage: ',sys.argv[0],' <input image> <output image>')\n", + " sys.exit(1)\n", + "infile=sys.argv[1]\n", + "outfile=sys.argv[2]\n", + "# mask input image with MNI\n", + "sp=subprocess.run([fsldir+'/bin/fslmaths',infile,'-mas',fsldir+'/data/standard/MNI152_T1_1mm_brain',outfile],stdout=subprocess.PIPE)\n", + "# calculate volumes of masked image \n", + "sp=subprocess.run([fsldir+'/bin/fslstats',outfile,'-V'],stdout=subprocess.PIPE)\n", + "sout=sp.stdout.decode('utf-8')\n", + "vol_vox=float(sout.split()[0])\n", + "vol_mm=float(sout.split()[1])\n", + "print('Volumes are: ',vol_vox,' in voxels and ',vol_mm,' in mm')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/getting_started/scripts.md b/getting_started/scripts.md new file mode 100644 index 0000000000000000000000000000000000000000..b206b1606d45dd6d55c0660f0869f70a3357ada3 --- /dev/null +++ b/getting_started/scripts.md @@ -0,0 +1,117 @@ +# Callable scripts in python + +In this tutorial we will cover how to write simple stand-alone scripts in python that can be used as alternatives to bash scripts. + +There are some code blocks within this webpage, but we recommend that you write the code in an IDE or editor instead and then run the scripts from a terminal. + +## Basic script + +The first line of a python script is usually: +``` +#!/usr/bin/env python +``` +which invokes whichever version of python can be found by `/usr/bin/env` since python can be located in many different places. + +For FSL scripts we use an alternative, to ensure that we pick up the version of python (and associated packages) that we ship with FSL. To do this we use the line: +``` +#!/usr/bin/env fslpython +``` + +After this line the rest of the file just uses regular python syntax, as in the other tutorials. + +## Calling other executables + +The most essential call that you need to use to replicate the way a bash script calls executables is `subprocess.run()`. A simple call looks like this: + +``` +import subprocess +subprocess.run(["ls","-la"]) +``` + + +To suppress the output do this: + +``` +sp=subprocess.run(["ls"],stdout=subprocess.PIPE) +``` + +To store the output do this: + +``` +sp=subprocess.run("ls -la".split(),stdout=subprocess.PIPE) +sout=sp.stdout.decode('utf-8') +print(sout) +``` + +Note that the `decode` call in the middle line converts the string from a byte string to a normal string. + +If the output is numerical then this can be extracted like this: +``` +import os +fsldir=os.getenv('FSLDIR') +sp=subprocess.run([fsldir+'/bin/fslstats',fsldir+'/data/standard/MNI152_T1_1mm_brain','-V'],stdout=subprocess.PIPE) +sout=sp.stdout.decode('utf-8') +vol_vox=float(sout.split()[0]) +vol_mm=float(sout.split()[1]) +print('Volumes are: ',vol_vox,' in voxels and ',vol_mm,' in mm') +``` + + +## Command line arguments + +The simplest way of dealing with command line arguments is use the module `sys`, which gives access to an `argv` list: +``` +import sys +print(len(sys.argv)) +print(sys.argv[0]) +``` + +There are also some modules that are useful for parsing arguments: + - `getopt` + - `argparse` + and you can find good documentation and examples of these on the web. + + +## Example script + +Here is a simple bash script (it masks an image and calculates volumes - just as random examples): + +``` +#!/bin/bash +if [ $# -lt 2 ] ; then + echo "Usage: $0 <input image> <output image>" + exit 1 +fi +infile=$1 +outfile=$2 +# mask input image with MNI +$FSLDIR/bin/fslmaths $infile -mas $FSLDIR/data/standard/MNI152_T1_1mm_brain $outfile +# calculate volumes of masked image +vv=`$FSLDIR/bin/fslstats $outfile -V` +vol_vox=`echo $vv | awk '{ print $1 }'` +vol_mm=`echo $vv | awk '{ print $2 }'` +echo "Volumes are: $vol_vox in voxels and $vol_mm in mm" +``` + + +And an alternative in python: + +``` +#!/usr/bin/env fslpython +import os, sys, subprocess +fsldir=os.getenv('FSLDIR') +if len(sys.argv)<2: + print('Usage: ',sys.argv[0],' <input image> <output image>') + sys.exit(1) +infile=sys.argv[1] +outfile=sys.argv[2] +# mask input image with MNI +sp=subprocess.run([fsldir+'/bin/fslmaths',infile,'-mas',fsldir+'/data/standard/MNI152_T1_1mm_brain',outfile],stdout=subprocess.PIPE) +# calculate volumes of masked image +sp=subprocess.run([fsldir+'/bin/fslstats',outfile,'-V'],stdout=subprocess.PIPE) +sout=sp.stdout.decode('utf-8') +vol_vox=float(sout.split()[0]) +vol_mm=float(sout.split()[1]) +print('Volumes are: ',vol_vox,' in voxels and ',vol_mm,' in mm') +``` +