Skip to content

Commit 9ea8dca

Browse files
committed
Update 017_Functions.ipynb
1 parent de4648f commit 9ea8dca

File tree

1 file changed

+228
-16
lines changed

1 file changed

+228
-16
lines changed

notebooks/017_Functions.ipynb

Lines changed: 228 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,14 +1618,16 @@
16181618
"cell_type": "markdown",
16191619
"metadata": {},
16201620
"source": [
1621-
"## `lambda` functions\n",
1621+
"## `map()` functions\n",
1622+
"\n",
1623+
"In Python, the `map()` function is a special function that allows you to apply some function to all the items in a list or similar *iterable* without using an explicit loop. \n",
16221624
"\n",
1623-
"Sometimes the function we want to write is very simple, and might consist of only a few statements on a single line. As an example, consider the function `y(x)` here:"
1625+
"As an example, consider the function `y(x)` here:"
16241626
]
16251627
},
16261628
{
16271629
"cell_type": "code",
1628-
"execution_count": 32,
1630+
"execution_count": 30,
16291631
"metadata": {},
16301632
"outputs": [],
16311633
"source": [
@@ -1634,24 +1636,107 @@
16341636
" a function y(x) with\n",
16351637
" zeros at x=4,x=3\n",
16361638
" '''\n",
1637-
" return 2.0*(x-3)*(x-4)"
1639+
" return 2*(x-3)*(x-4)"
16381640
]
16391641
},
16401642
{
16411643
"cell_type": "markdown",
16421644
"metadata": {},
16431645
"source": [
1644-
"Whilst it is fine to write a function in this way, and it has the advantage of being quite explicit about what it is doing, it is not very efficient: there is a computational cost to calling a function, especially in a high-level language like Python. \n",
1646+
"Now, suppose we want to apply that function to each item in a list. We could do this quite neatly with a list comprehension:"
1647+
]
1648+
},
1649+
{
1650+
"cell_type": "code",
1651+
"execution_count": 31,
1652+
"metadata": {},
1653+
"outputs": [
1654+
{
1655+
"name": "stdout",
1656+
"output_type": "stream",
1657+
"text": [
1658+
"[12, -0.0, 24]\n"
1659+
]
1660+
}
1661+
],
1662+
"source": [
1663+
"# some values in an array -- some are float \n",
1664+
"# some are int\n",
1665+
"xvals = [1, 3.0, 7]\n",
16451666
"\n",
1646-
"If we were only ever going to use this function in places in the code where performance is unimportant, then we can go ahead with the above.\n",
1667+
"result = [y(x) for x in xvals]\n",
1668+
"print(result)"
1669+
]
1670+
},
1671+
{
1672+
"cell_type": "markdown",
1673+
"metadata": {},
1674+
"source": [
1675+
"Or, as we shall see later in the course, we could do this also more directly in numpy arrays if the data type is the same for every item in the list/array. \n",
16471676
"\n",
1648-
"If we are worried that it might be used in a case when we would not want to slow it down unnecessarily with as function call, we can use a special type of function, called a [`lambda` function](https://www.w3schools.com/python/python_lambda.asp). The syntax is:\n",
1677+
"Another approach to this is to use a [`map()`](https://realpython.com/python-map-function/) function, which has syntax `map(function,iterable1[,iterable12,...])`, whereby `function(iterable1)` is applied to each iten in turn in `iterable1` (then `iterable12` ...). This can be very efficient as it does not have all of the memory overheads associated with an explicit loop. It is also a very compact way of coding.\n",
1678+
"\n",
1679+
"For example:"
1680+
]
1681+
},
1682+
{
1683+
"cell_type": "code",
1684+
"execution_count": 32,
1685+
"metadata": {},
1686+
"outputs": [
1687+
{
1688+
"data": {
1689+
"text/plain": [
1690+
"[12, -0.0, 24]"
1691+
]
1692+
},
1693+
"execution_count": 32,
1694+
"metadata": {},
1695+
"output_type": "execute_result"
1696+
}
1697+
],
1698+
"source": [
1699+
"xvals = [1, 3.0, 7]\n",
1700+
"\n",
1701+
"result = map(y,xvals)\n",
1702+
"list(result)"
1703+
]
1704+
},
1705+
{
1706+
"cell_type": "markdown",
1707+
"metadata": {},
1708+
"source": [
1709+
"## `lambda` functions\n",
1710+
"\n",
1711+
"A lambda function is a special function definition that has a lower overhead than a *full* function definition.\n",
1712+
"\n",
1713+
"The syntax is:\n",
1714+
"\n",
1715+
" function_name = lambda args : function_code\n",
1716+
"\n",
1717+
"where `function_name` is the name of the function, `args` is a list of arguments, and `function_code` here represents the (short!) code inside the function."
1718+
]
1719+
},
1720+
{
1721+
"cell_type": "markdown",
1722+
"metadata": {},
1723+
"source": [
1724+
"A function that is often used alongside the `map()` operation, is `reduce()`. This is another operation on an iterable, but this time it has just a single output that is the reduction of the iterable. It has syntax `reduce(function,iterable,initializer=None)`."
1725+
]
1726+
},
1727+
{
1728+
"cell_type": "markdown",
1729+
"metadata": {},
1730+
"source": [
1731+
"\n",
1732+
"\n",
1733+
"In such a case, we might consider using a [`lambda` function](https://www.w3schools.com/python/python_lambda.asp). The syntax is:\n",
16491734
"\n",
16501735
" function_name = lambda args : function_code\n",
16511736
"\n",
16521737
"where `function_name` is the name of the function, `args` is a list of arguments, and `function_code` here represents the (short!) code inside the function.\n",
16531738
"\n",
1654-
"Our example above translates to:"
1739+
"Our example for `y(x)` above translates to:"
16551740
]
16561741
},
16571742
{
@@ -1667,14 +1752,109 @@
16671752
"cell_type": "markdown",
16681753
"metadata": {},
16691754
"source": [
1670-
"which is a much *neater* and more efficient code than a full function. Use these when appropriate.\n",
1755+
"although [PEP008](https://www.python.org/dev/peps/pep-0008/) suggests that you don't use it in that way. \n",
16711756
"\n",
1672-
"Generally, you are not expected to use a docstring with a `lambda` function, as it should be a simple statement that is self-evident from the code. If you do feel the need, you can add one with:\n",
1757+
"Suppose though that we wanted to apply this function to each item in a list, e.g. in a `map()` function. The function is so small and simple that we don't really need to define a full *named* function for this in our code.\n",
16731758
"\n",
1674-
" y.__doc__ = '''\n",
1675-
" a function y(x) with\n",
1676-
" zeros at x=4,x=3\n",
1677-
" '''\n"
1759+
"We could instead use:"
1760+
]
1761+
},
1762+
{
1763+
"cell_type": "code",
1764+
"execution_count": 34,
1765+
"metadata": {},
1766+
"outputs": [
1767+
{
1768+
"data": {
1769+
"text/plain": [
1770+
"[12.0, -0.0, 24.0]"
1771+
]
1772+
},
1773+
"execution_count": 34,
1774+
"metadata": {},
1775+
"output_type": "execute_result"
1776+
}
1777+
],
1778+
"source": [
1779+
"xvals = [1, 3.0, 7]\n",
1780+
"\n",
1781+
"result = map(lambda x : 2.0*(x-3)*(x-4),xvals)\n",
1782+
"list(result)"
1783+
]
1784+
},
1785+
{
1786+
"cell_type": "markdown",
1787+
"metadata": {},
1788+
"source": [
1789+
"which is simpler, more efficient, and clearer code to achieve this task."
1790+
]
1791+
},
1792+
{
1793+
"cell_type": "markdown",
1794+
"metadata": {},
1795+
"source": [
1796+
"## `reduce()` functions\n",
1797+
"\n",
1798+
"A `reduce` function is a special type of function that is often used alongside the `map()` function, particularly within the context of processing environments such as [Google Earth Engine](https://developers.google.com/earth-engine/guides/reducers_intro). Like `map()`, it is an efficient coding method that is applied to each item in an iterable. But unlike `map()`, information is passed in pairs. The first time a function is called within `reduce`, the function receives the first item from the iterator and an `initializer` value if given. If not given, it receives teh first two values. It then calculates the result of this function (with these two inputs). The function is then called with the next item from the iterator and that first result, and so on. At the end of the iterators, the final function output is returned.\n",
1799+
"\n",
1800+
"To illustrate this:"
1801+
]
1802+
},
1803+
{
1804+
"cell_type": "code",
1805+
"execution_count": 38,
1806+
"metadata": {},
1807+
"outputs": [
1808+
{
1809+
"name": "stdout",
1810+
"output_type": "stream",
1811+
"text": [
1812+
"I am adding 1 and 3.0\n",
1813+
"I am adding 4.0 and 7\n",
1814+
"final value: 11.0\n"
1815+
]
1816+
}
1817+
],
1818+
"source": [
1819+
"from functools import reduce\n",
1820+
"# import the reduce function\n",
1821+
"\n",
1822+
"# define a function of 2 variables, a & b\n",
1823+
"def my_add(a,b):\n",
1824+
" print(f'I am adding {a} and {b}')\n",
1825+
" return a + b\n",
1826+
"\n",
1827+
"xvals = [1, 3.0, 7]\n",
1828+
"\n",
1829+
"print(f'final value: {reduce(my_add, xvals)}')"
1830+
]
1831+
},
1832+
{
1833+
"cell_type": "markdown",
1834+
"metadata": {},
1835+
"source": [
1836+
"Dropping the `print` this is neater using a `lambda` function:"
1837+
]
1838+
},
1839+
{
1840+
"cell_type": "code",
1841+
"execution_count": 44,
1842+
"metadata": {},
1843+
"outputs": [
1844+
{
1845+
"name": "stdout",
1846+
"output_type": "stream",
1847+
"text": [
1848+
"final value: 11.0\n"
1849+
]
1850+
}
1851+
],
1852+
"source": [
1853+
"from functools import reduce\n",
1854+
"\n",
1855+
"xvals = [1, 3.0, 7]\n",
1856+
"\n",
1857+
"print(f'final value: {reduce(lambda a,b:a+b, xvals)}')"
16781858
]
16791859
},
16801860
{
@@ -1713,7 +1893,37 @@
17131893
"\n",
17141894
"\n",
17151895
"* Test this function, inputting a list of integers from 0 to 4 inclusive\n",
1716-
"* Write a more Pythonic version of this, making use of list comprehensions and `lambda` functions"
1896+
"* Write a more Pythonic version of this, making use of list comprehensions, `map`, `reduce` or `lambda` functions as appropriate."
1897+
]
1898+
},
1899+
{
1900+
"cell_type": "code",
1901+
"execution_count": 42,
1902+
"metadata": {
1903+
"solution2": "hidden"
1904+
},
1905+
"outputs": [
1906+
{
1907+
"name": "stdout",
1908+
"output_type": "stream",
1909+
"text": [
1910+
"2**[1, 2, 3, 4] = [2, 4, 8, 16]\n",
1911+
"2**[1, 2, 3, 4] = [2, 4, 8, 16]\n"
1912+
]
1913+
}
1914+
],
1915+
"source": [
1916+
"# ANSWER\n",
1917+
"\n",
1918+
"# probably a map function is most appropriate here\n",
1919+
"# with a lambda function to define the 2**x\n",
1920+
"\n",
1921+
"ilist = [1,2,3,4]\n",
1922+
"\n",
1923+
"print(f'2**{ilist} = {list(map(lambda x: 2**x,ilist))}')\n",
1924+
"\n",
1925+
"## but a list comprehension would do very well too\n",
1926+
"print(f'2**{ilist} = {[2**x for x in ilist]}')"
17171927
]
17181928
},
17191929
{
@@ -1929,7 +2139,9 @@
19292139
"\n",
19302140
"We have also seen `lambda` functions, used for short functions:\n",
19312141
"\n",
1932-
" function_name = lambda args : function_code\n"
2142+
" function_name = lambda args : function_code\n",
2143+
"\n",
2144+
"We also learned about `map()`, `reduce()` and `lambda` functions."
19332145
]
19342146
},
19352147
{

0 commit comments

Comments
 (0)