diff --git a/tutorials/OOB_EC/OOP_1.ipynb b/tutorials/OOB_EC/OOP_1.ipynb
index e6d3661..f52ad8c 100644
--- a/tutorials/OOB_EC/OOP_1.ipynb
+++ b/tutorials/OOB_EC/OOP_1.ipynb
@@ -5,7 +5,7 @@
"metadata": {},
"source": [
"## Introduction to OOP in Python, Part 1\n",
- "An Object is a type of data structure that contains both attributes (or properties) and methods (essencially functions that act upon the object's attributes). Attributes themselves can be other objects of the same or different class. A class definition is basically the template for object's attributes and methods. A specific object is said to be an instance of its class."
+ "An Object is a type of data structure that contains both attributes (or properties) and methods - essentially functions that act upon the object's attributes. Attributes themselves can be other objects of the same or different class. A class definition is basically the template for object's attributes and methods. A specific object is said to be an instance of its class."
]
},
{
@@ -17,13 +17,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The task we will set ouselves is to build Python classes that enable us to represent algebraic expression trees, for example, $ ((5 + z) / -8) * (4 ^ 2) $ is represented as: \n",
+ "The task we will set ourselves is to build Python classes that enable us to represent algebraic expression trees, for example, $ ((5 + z) / -8) * (4 ^ 2) $ is represented as: \n",
"\n",
"\n",
"\n",
- "From wikipedia : https://en.wikipedia.org/wiki/Binary_expression_tree\n",
+ "From Wikipedia : https://en.wikipedia.org/wiki/Binary_expression_tree\n",
"\n",
- "We may go on to see how we can use such trees for *Evolutionary Computing* in later tutorials."
+ "We may go on to see how we can use such trees for *Evolutionary Computing* in later tutorial set."
]
},
{
@@ -52,21 +52,25 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Function names that begin and end with double underscore are used by Python to define a protocol1;\n",
- "in this case \\_\\_init\\_\\_ is part of the class protocol and defines how a specific instance of the new\n",
+ "Function names that begin and end with double underscore are used by Python to define a *protocol*1;\n",
+ "in this case `__init__` is part of the class protocol and defines how a specific instance of the new\n",
"class will be initialized, and is called automatically when you use a statement like:\n",
"\n",
- " myTN = TreeNode('My name is Joe')\n",
+ "```python\n",
+ "myTN = TreeNode('My name is Joe')\n",
+ "```\n",
" \n",
"The first argument, conventionally called 'self', refers to the specific instance of the object, and is required for all instance methods; it is automatically filled-in when methods are called using 'dot' notation:\n",
"\n",
- " myTN.value()\n",
+ "```python\n",
+ "myTN.value()\n",
+ "```\n",
"\n",
- "Notice that the multi-line comment following the class definition will become the classes \"Doc-String\".\n",
+ "Notice that the multi-line comment following the class definition will become the class's \"Doc-String\".\n",
"\n",
"Instances of above class have five properties: **name, left, right, parent** and **myValue**. We may also think of instances of the class as *containing* these five objects.\n",
"\n",
- "The instances also has one *user* method: **value()** and one *internal* method: **\\_\\_init\\_\\_() **"
+ "The instances also has one *user* method: `value()` and one *internal* method: `__init__()`"
]
},
{
@@ -110,7 +114,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "<__main__.TreeNode object at 0x0000026E3E88F048>\n"
+ "<__main__.TreeNode object at 0x000002092B848608>\n"
]
}
],
@@ -126,8 +130,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Not particuarly usefull! Let's fix that.\n",
- "\\_\\_str\\_\\_ is the protocol for defining how an object will print or be represented as a string.\n",
+ "Not particularly useful! Let's fix that.\n",
+ "`__str__` is the protocol for defining how an object will print or be represented as a string.\n",
"Let's redefine our class..."
]
},
@@ -167,7 +171,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Notice that we are using \\_\\_str\\_\\_ recursively as as the str function calls \\_\\_str\\_\\_ -- we we frequently make use of recursive functions when dealing with trees"
+ "Notice that we are using `__str__` recursively as as the `str` function calls `__str__` -- we we frequently make use of recursive functions when dealing with trees"
]
},
{
@@ -194,7 +198,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The value method is really acting as an alias for the myValue property, it would be nice if we could just access it like any other property. We can do this with the @property decorator. A decorator is a function that modifies the behaviour of the method or function that follows it. In this case it allow us to access the *value* method as if it was a property rather than a method, e.g. nd1.value instead of nd1.value()"
+ "The `value` method is really acting as an alias for the `myValue` property, it would be nice if we could just access it like any other property. We can do this with the `@property` decorator. A decorator is a function that modifies the behaviour of the method or function that follows it. In this case it allow us to access the `value` method as if it was a property rather than a method, e.g. `nd1.value` instead of `nd1.value()`"
]
},
{
@@ -266,18 +270,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Property methods are usefull for properties that are calculated 'on-the-fly'; we will see later that they can also be used for data validation."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
+ "Property methods are useful for properties that are calculated 'on-the-fly'; we will see later that they can also be used for data validation.\n",
+ "\n",
"Notice that we are unable to assign a value using the name.\n",
"\n",
"We will see how to overcome this later.\n",
"\n",
- "Although we can still use myValue"
+ "Although we can still use `myValue`"
]
},
{
@@ -393,7 +392,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Let's see what's going on..? The **dir** function will list an object's properties and methods (including those inherited from the superclass2)\n",
+ "Let's see what's going on..? The `dir` function will list an object's properties and methods (including those inherited from the superclass2)\n",
"\n",
"2: Explained more fully later."
]
@@ -447,22 +446,14 @@
},
{
"cell_type": "markdown",
- "metadata": {},
- "source": [
- "This is an example of Python's _name mangling_, in this case it is being used to hide the private property. The upshot is that you don't really wan't to mess with private properties."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The fact that in Python you can create a new property on-the-fly outside of the class definition (also works for methods) is called _Monkey Patching_."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
+ "This is an example of Python's _name mangling_, in this case it is being used to hide the private property. The upshot is that you don't really want to mess with private properties.\n",
+ "\n",
+ "The fact that in Python you can create a new property on-the-fly outside of the class definition (also works for methods) is called _Monkey Patching_.\n",
+ "\n",
"Now let us see how can modify the value property so that we can update it."
]
},
@@ -548,14 +539,18 @@
"\n",
"Perhaps a more useful use of the setter decorator is to enforce a *side effect*. Consider the need to set the parent property, currently we might do something like:\n",
"\n",
- " p = TreeNode('P')\n",
- " p.left = TreeNode('LC')\n",
- " p.left.parent = p\n",
+ "```python\n",
+ "p = TreeNode('P')\n",
+ "p.left = TreeNode('LC')\n",
+ "p.left.parent = p\n",
+ "```\n",
" \n",
"although we can shorten this to:\n",
"\n",
- " p = TreeNode('P')\n",
- " p.left = TreeNode('LC',p)\n",
+ "```python\n",
+ "p = TreeNode('P')\n",
+ "p.left = TreeNode('LC',p)\n",
+ "```\n",
" \n",
"It is possible that the user may forget to set the parent property. We can fix this as follows:"
]
@@ -656,7 +651,7 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
@@ -674,7 +669,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 19,
"metadata": {},
"outputs": [
{
@@ -737,10 +732,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 34,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -753,33 +748,18 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Leveraging GraphViz to render our custom trees"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Notice that GraphViz uses the _name_ as a unique identifier; we will therefore implement a unique identifier for each node. We will use a _class property_ to keep track of the UIDs created. Similarly, we can have @classmethod which we have used to fetch the next UID. Notice that the class method's first parameter is *cls* rather than *self* - it is a reference to this class rather than a specific instance."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We generate the graph to be plotted using a recursive helper function which for illustration has been defined as a @staticmethod. A static method is one that does not depend on the instance i.e. it does not require _self_. In this case we could have just used a normal method..."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
+ "## Leveraging GraphViz to render our custom trees\n",
+ "\n",
+ "Notice that GraphViz uses the _name_ as a unique identifier; we will therefore implement a unique identifier for each node. We will use a _class property_ to keep track of the UIDs created. Similarly, we can have `@classmethod` which we have used to fetch the next UID. Notice that the class method's first parameter is `cls` rather than `self` - it is a reference to this class rather than a specific instance.\n",
+ "\n",
+ "We generate the graph to be plotted using a recursive helper function which for illustration has been defined as a `@staticmethod`. A static method is one that does not depend on the instance i.e. it does not require `_self_`. In this case we could have just used a normal method...\n",
+ "\n",
"Code adapted from: https://h1ros.github.io/posts/introduction-to-graphviz-in-jupyter-notebook/"
]
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
@@ -880,7 +860,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 21,
"metadata": {},
"outputs": [
{
@@ -955,10 +935,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 36,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -981,7 +961,7 @@
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
@@ -990,7 +970,7 @@
"6"
]
},
- "execution_count": 37,
+ "execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@@ -1001,7 +981,7 @@
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
@@ -1010,7 +990,7 @@
"6"
]
},
- "execution_count": 38,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
@@ -1023,7 +1003,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "For simplicity, we draw the links as single arrows, although in reality they should be double arrows [because of the link from child to parent via the parent property]"
+ "For simplicity, we draw the links as single arrows, although in reality they should be double arrows because of the link from *child* to *parent* via the `parent` property."
]
},
{
diff --git a/tutorials/OOB_EC/OOP_2.ipynb b/tutorials/OOB_EC/OOP_2.ipynb
index 5def4ec..98073b6 100644
--- a/tutorials/OOB_EC/OOP_2.ipynb
+++ b/tutorials/OOB_EC/OOP_2.ipynb
@@ -125,7 +125,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 3,
@@ -151,9 +151,9 @@
"\n",
"To do this we do not need to build new classes from scratch; we can create a new class that is a *subclass* of one or more existing classes. Such classes will inherit the properties and methods of their *superclasses*, although we can modify or *override* them as well as add new ones. \n",
"\n",
- "In fact, all classes implicitly are subclasses of the **object class**.\n",
+ "In fact, all classes are implicitly subclasses of the **object class**.\n",
"\n",
- "We are switching from thinking of object as *containers* of other objects to thinking of objects as being *hierarchically related*. Think that rather than dogs and cats as *having* legs, but that dogs and cats *are* animals, and all animals share certain properties and behaviors [methods], although details may differ: dogs go 'woof' and cats go 'meow', both use the *makenoise* method."
+ "We are switching from thinking of object as *containers* of other objects to thinking of objects as being *hierarchically related*. Think that rather than dogs and cats as *having* legs, but that dogs and cats *are* animals, and all animals share certain properties and behaviors aka methods, although details may differ: dogs go 'woof' and cats go 'meow', both use the *makenoise* method."
]
},
{
@@ -182,7 +182,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "It makes good sense to implement a basic ExpressionTree class that includes the properties and methods common to all algebraic tree nodes. We will create place-holders for methods that we know we will need for later implementation."
+ "It makes good sense to implement a basic `ExpressionTree` class that includes the properties and methods common to all algebraic tree nodes. We will create place-holders for methods that we know we will need for later implementation."
]
},
{
@@ -291,7 +291,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 7,
@@ -529,7 +529,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 10,
@@ -605,7 +605,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Two methods of our class, *clone* and *mutate* are not complete; we may have used **pass**, the do nothing keyword, but using **raise NotImplementedError** ensures we won't forget to actually complete these methods:"
+ "Two methods of our class, `clone` and `mutate` are not complete; we may have used `pass`, the Python *do nothing* keyword, but instead we are using `raise NotImplementedError` to ensure that we don't forget to actually complete these methods:"
]
},
{
@@ -642,7 +642,7 @@
"metadata": {},
"source": [
"## When a copy is not a copy?\n",
- "Certain Python classes such as strings and numbers are *immutable*. Once created, they cannot be changed. Any operation that apppears to alter them in fact involves making a new copy of the object."
+ "Certain Python classes such as strings and numbers are *immutable*. Once created, they cannot be changed. Any operation that appears to alter them in fact involves making a new copy of the object."
]
},
{
@@ -711,7 +711,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "*Mutable* objects such as lists, dictionaries, sets and instances our ExpressionTree class behave differently:"
+ "*Mutable* objects such as lists, dictionaries, sets and instances our `ExpressionTree` class behave differently:"
]
},
{
@@ -776,7 +776,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 19,
@@ -793,7 +793,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "When we 'copy' et to et2, and alter et2, we also alter et:"
+ "When we 'copy' `et` to `et2`, and alter `et2`, we also alter `et`:"
]
},
{
@@ -849,7 +849,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 20,
@@ -916,7 +916,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 21,
@@ -990,7 +990,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 23,
@@ -1058,7 +1058,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 24,
@@ -1074,7 +1074,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "This is a **shallow copy** the object is copied, but any contained objects are still references the originals. We would have expected **X** rather than **Z**."
+ "This is a **shallow copy** the object is copied, but any contained objects are still references to the originals. We would have expected `X` rather than `Z`."
]
},
{
@@ -1130,7 +1130,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 25,
@@ -1198,7 +1198,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 26,
@@ -1214,27 +1214,19 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Now working as expected. W is a new copy of Z."
+ "Now working as expected. `W` is a new copy of `Z`."
]
},
{
"cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Deep Copy pitfalls\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "An object that has *circular references*, that is implicit or explicit references to itself, may cause an infinite recursion loop with any naive implementation of deepcopy. Fortunately, deepcopy from the copy module circumvents this by keeping track of objects it has already seen."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
+ "### Deep Copy pitfalls\n",
+ "\n",
+ "An object that has *circular references*, that is implicit or explicit references to itself, may cause an infinite recursion loop with any naive implementation of deepcopy. Fortunately, deepcopy from the copy module circumvents this by keeping track of objects it has already seen.\n",
+ "\n",
"It is still possible that circular references may throw up unexpected behaviour with deepcopy."
]
},
@@ -1267,7 +1259,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 27,
@@ -1347,7 +1339,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 29,
@@ -1412,7 +1404,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 30,
@@ -1428,12 +1420,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Deep copying **Z** has copied the entire tree! NewRoot is copy of Root."
+ "Deep copying `Z` has copied the entire tree! `NewRoot` is copy of `Root`."
]
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 31,
"metadata": {},
"outputs": [
{
@@ -1458,7 +1450,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 32,
"metadata": {},
"outputs": [
{
@@ -1467,7 +1459,7 @@
"False"
]
},
- "execution_count": 34,
+ "execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
@@ -1480,12 +1472,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The other issue with deepcopy is that it will blithly copy large component objects with the resulting impact on speed and memory usage. We will illustrate this with a quick monkey patch."
+ "The other issue with deepcopy is that it will blithely copy large component objects with the resulting impact on speed and memory usage. We will illustrate this with a quick *monkey patch*."
]
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 33,
"metadata": {},
"outputs": [],
"source": [
@@ -1494,7 +1486,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
@@ -1503,7 +1495,7 @@
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 35,
"metadata": {},
"outputs": [
{
@@ -1512,7 +1504,7 @@
"False"
]
},
- "execution_count": 37,
+ "execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
@@ -1523,14 +1515,14 @@
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "1.79 ms ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
+ "1.7 ms ± 27.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
@@ -1541,14 +1533,14 @@
},
{
"cell_type": "code",
- "execution_count": 39,
+ "execution_count": 37,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "2.37 µs ± 83.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
+ "2.38 µs ± 136 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
]
}
],
@@ -1559,7 +1551,7 @@
},
{
"cell_type": "code",
- "execution_count": 40,
+ "execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
@@ -1568,7 +1560,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 39,
"metadata": {},
"outputs": [
{
@@ -1577,7 +1569,7 @@
"True"
]
},
- "execution_count": 41,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
@@ -1591,8 +1583,8 @@
"metadata": {},
"source": [
"Two options to get round this \n",
- "- avoid using bigdata in object properties, i.e. **myobj.processdata(data)** rather than **myobj.data=data; myobj.processdata()**\n",
- "- define **\\_\\_deepcopy\\_\\_** and implement a customised deepcopy, ignoring properties that do not need to be deep copied."
+ "- avoid using bigdata in object properties, i.e. `myobj.processdata(data)` rather than `myobj.data=data; myobj.processdata()` - which, however, breaks the OOP concept of encapsulation.\n",
+ "- define `__deepcopy__` and implement a customised deepcopy, ignoring properties that do not need to be deep copied."
]
},
{
diff --git a/tutorials/OOB_EC/OOP_3a.ipynb b/tutorials/OOB_EC/OOP_3a.ipynb
index eccf0fe..17e109e 100644
--- a/tutorials/OOB_EC/OOP_3a.ipynb
+++ b/tutorials/OOB_EC/OOP_3a.ipynb
@@ -4,28 +4,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Introduction to OOP in Python, Part 3a"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
+ "# Introduction to OOP in Python, Part 3a\n",
+ "\n",
"### More problems with *circular references*\n",
- "How does the *garbage collector* clean up memory?"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In languages like **C**, the programmer is responsible for correctly allocating an deallocating memory. This often lead to memory leaks and buffer over-runs due to human error. More modern programming languages, including Python, use automatic dynamic memory management generally referred to as *garbage collection*"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
+ "How does the *garbage collector* clean up memory?\n",
+ "\n",
+ "In languages like **C**, the programmer is responsible for correctly allocating an deallocating memory. This often lead to memory leaks and buffer over-runs due to human error. More modern programming languages, including Python, use automatic dynamic memory management generally referred to as *garbage collection*\n",
+ "\n",
"In the simplest scheme, the *garbage collector* keeps track of the number of references to each object in memory. Once the reference count reaches zero, the object can be deleted and the memory freed. This is both fast and efficient, but cannot deal with circular references, i.e. when an object references itself either directly or indirectly."
]
},
@@ -45,21 +30,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "As well as ref. counting, the Python GC, uses another, more complex, algorithm to detect circular references and free up memory. As this is rather slow and much less efficient, it is only called periodically. Thus there is potential for large chunks of memory to left waiting for garbage collection."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can monitor memory usage using the memory profiler. Using a double linked list test class which can exhibit circular references, we will illustrate the problem."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Notice the use of the \\_\\_del\\_\\_ method; this is called just before an instance is deleted, and is usually used to clean-up before object deletion e.g. deleting temporary files. Here we're using it to notify us that the object is being deleted."
+ "As well as ref. counting, the Python GC, uses another, more complex, algorithm to detect circular references and free up memory. As this is rather slow and much less efficient, it is only called periodically. Thus there is potential for large chunks of memory to be left waiting for garbage collection.\n",
+ "\n",
+ "We can monitor memory usage using the memory profiler. Using a double linked list test class which can exhibit circular references, we will illustrate the problem.\n",
+ "\n",
+ "Notice the use of the `__del__` method; this is called just before an instance is deleted, and is usually used to clean-up before object deletion e.g. deleting temporary files. Here we're using it to notify us that the object is being deleted."
]
},
{
@@ -109,21 +84,11 @@
"metadata": {},
"source": [
"Install memory_profiler if needed:\n",
- "!conda install memory_profiler\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The code to be profiled needs to be saved in a seperate file, in this case *testmem.py*"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The memory profiler *IPython magic* **%mprun** is not loaded by default."
+ "`!conda install memory_profiler`\n",
+ "\n",
+ "The code to be profiled needs to be saved in a separate file, in this case *testmem.py*. Above, we use the handy *IPython magic* `%%file` to save the code cell to the named file.\n",
+ "\n",
+ "The memory profiler *IPython magic* `%mprun` is not loaded by default."
]
},
{
@@ -160,16 +125,16 @@
{
"data": {
"text/plain": [
- "Filename: D:\\PythonClub\\Python-Club\\Tutorials\\OOB_EC\\testmem.py\n",
+ "Filename: D:\\PythonClub\\Python-Club\\tutorials\\OOB_EC\\testmem.py\n",
"\n",
"Line # Mem usage Increment Line Contents\n",
"================================================\n",
- " 20 52.5 MiB 52.5 MiB def test():\n",
- " 21 3892.7 MiB 0.0 MiB for _ in range(10):\n",
- " 22 3636.7 MiB 128.1 MiB dll = DLL() #any previous 'dll' should be free to be deleted\n",
- " 23 3892.7 MiB 256.0 MiB dll.grow(2)\n",
- " 24 3892.7 MiB 0.0 MiB del(dll)\n",
- " 25 3892.7 MiB 0.0 MiB print('End')"
+ " 20 52.7 MiB 52.7 MiB def test():\n",
+ " 21 3892.9 MiB 0.0 MiB for _ in range(10):\n",
+ " 22 3636.9 MiB 128.1 MiB dll = DLL() #any previous 'dll' should be free to be deleted\n",
+ " 23 3892.9 MiB 256.0 MiB dll.grow(2)\n",
+ " 24 3892.9 MiB 0.0 MiB del(dll)\n",
+ " 25 3892.9 MiB 0.0 MiB print('End')"
]
},
"metadata": {},
@@ -184,14 +149,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Curiously, it appears that \\_\\_del\\_\\_ is never called and the memory usage is nearly 4 GB."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The weakref module allows the creation of references that **do not** count towards the garbage collector's ref count. Objects are therefore cleared from memory more quicky."
+ "Curiously, it appears that `__del__` is never called and the memory usage is nearly 4 GB.\n",
+ "\n",
+ "The weakref module allows the creation of references that **do not** count towards the garbage collector's ref count. Objects are therefore cleared from memory more quickly."
]
},
{
@@ -313,7 +273,7 @@
{
"data": {
"text/plain": [
- "133"
+ "188"
]
},
"execution_count": 7,
@@ -372,16 +332,16 @@
{
"data": {
"text/plain": [
- "Filename: D:\\PythonClub\\Python-Club\\Tutorials\\OOB_EC\\testmem2.py\n",
+ "Filename: D:\\PythonClub\\Python-Club\\tutorials\\OOB_EC\\testmem2.py\n",
"\n",
"Line # Mem usage Increment Line Contents\n",
"================================================\n",
- " 36 52.7 MiB 52.7 MiB def test2():\n",
- " 37 52.7 MiB 0.0 MiB for _ in range(10):\n",
- " 38 180.7 MiB 128.0 MiB dll = DLL2()\n",
- " 39 436.7 MiB 256.0 MiB dll.grow(2)\n",
- " 40 52.7 MiB 0.0 MiB del(dll)\n",
- " 41 52.7 MiB 0.0 MiB print('End')"
+ " 36 52.9 MiB 52.9 MiB def test2():\n",
+ " 37 52.9 MiB 0.0 MiB for _ in range(10):\n",
+ " 38 180.9 MiB 128.0 MiB dll = DLL2()\n",
+ " 39 436.9 MiB 256.0 MiB dll.grow(2)\n",
+ " 40 52.9 MiB 0.0 MiB del(dll)\n",
+ " 41 52.9 MiB 0.0 MiB print('End')"
]
},
"metadata": {},
@@ -403,14 +363,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Use of weakref however has an effect on deepcopy:"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Treenode2 contains a version of TreeNode that uses weak references to *parent*."
+ "### However use of weakref has an effect on deepcopy:\n",
+ "\n",
+ "`Treenode2` contains a version of TreeNode that uses weak references to *parent*."
]
},
{
@@ -504,7 +459,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 11,
@@ -561,7 +516,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 12,
@@ -641,7 +596,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 13,
@@ -657,7 +612,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Deepcopy *does not* follow weak references and **NewR1** points to the *wrong* parent, similarly for **NewR2**:"
+ "Deepcopy *does not* follow weak references and `NewR1` points to the *wrong* parent, similarly for `NewR2`:"
]
},
{
@@ -684,7 +639,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Still the old parent.."
+ "Still the old parent!"
]
},
{
@@ -709,7 +664,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
@@ -720,28 +675,28 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Here we use a 'dunder' method, \\_\\_deepcopy\\_\\_ to re-define how *deepcopy* works, and we make use of a dunder attribute \\_\\_dict\\_\\_ that keeps track of an objects attributes.\n",
+ "Here we use a 'dunder' method, `__deepcopy__` to re-define how *deepcopy* works for our class, and we make use of a dunder attribute `__dict__` that keeps track of an objects attributes.\n",
"\n",
- "Notice that **deepcopy** uses the **memo** parameter to keep track of objects it has 'already seen'; this prevent infinite loops."
+ "Notice that `deepcopy` uses the `memo` parameter to keep track of objects it has 'already seen'; this prevents infinite loops."
]
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'T1',\n",
- " '_left': <__main__.TreeNode2 at 0x1ba691e8f48>,\n",
- " '_right': <__main__.TreeNode2 at 0x1ba691e8f88>,\n",
+ " '_left': ,\n",
+ " '_right': ,\n",
" '_parent': None,\n",
" '_value': 42,\n",
- " '_uid': '13'}"
+ " '_uid': '1'}"
]
},
- "execution_count": 32,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -753,7 +708,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
@@ -776,7 +731,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 19,
"metadata": {},
"outputs": [
{
@@ -793,45 +748,45 @@
"\r\n",
"%3\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17\r\n",
+ "5\r\n",
"\r\n",
"T1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "18\r\n",
+ "6\r\n",
"\r\n",
"L1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17->18\r\n",
+ "5->6\r\n",
"\r\n",
"\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19\r\n",
+ "7\r\n",
"\r\n",
"R1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17->19\r\n",
+ "5->7\r\n",
"\r\n",
"\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "20\r\n",
+ "8\r\n",
"\r\n",
"R2\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19->20\r\n",
+ "7->8\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -839,10 +794,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 34,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -857,7 +812,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 20,
"metadata": {},
"outputs": [
{
@@ -874,21 +829,21 @@
"\r\n",
"%3\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19\r\n",
+ "7\r\n",
"\r\n",
"NewR1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "20\r\n",
+ "8\r\n",
"\r\n",
"NewR2\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19->20\r\n",
+ "7->8\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -896,10 +851,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 35,
+ "execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
@@ -913,7 +868,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 21,
"metadata": {},
"outputs": [
{
@@ -930,45 +885,45 @@
"\r\n",
"%3\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17\r\n",
+ "5\r\n",
"\r\n",
"T1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "18\r\n",
+ "6\r\n",
"\r\n",
"L1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17->18\r\n",
+ "5->6\r\n",
"\r\n",
"\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19\r\n",
+ "7\r\n",
"\r\n",
"R1\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "17->19\r\n",
+ "5->7\r\n",
"\r\n",
"\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "20\r\n",
+ "8\r\n",
"\r\n",
"R2\r\n",
"\r\n",
- "\r\n",
+ "\r\n",
"\r\n",
- "19->20\r\n",
+ "7->8\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -976,10 +931,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 36,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -992,12 +947,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "With the custom Deepcopy, it *does not* follow weak references above the copied node and **NewR1** points to the *wrong* parent, however, weakrefernces at higher depth are copied properly. **NewR2** now points to the correct parent:"
+ "With the custom `Deepcopy`, it *does not* follow weak references above the copied node and `NewR1` points to the *wrong* parent, however, weakreferences at higher depth are copied properly. `NewR2` now points to the correct parent:"
]
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
@@ -1006,7 +961,7 @@
"True"
]
},
- "execution_count": 37,
+ "execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@@ -1019,12 +974,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "No longer the old parent.."
+ "No longer the old parent."
]
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
@@ -1033,7 +988,7 @@
"False"
]
},
- "execution_count": 38,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
@@ -1046,7 +1001,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "We now have a deepcopy that properly copies a sub-branch of the tree."
+ "We now have a deepcopy that properly copies a **sub-branch** of the tree."
]
},
{
diff --git a/tutorials/OOB_EC/OOP_3b.ipynb b/tutorials/OOB_EC/OOP_3b.ipynb
index 5b3b848..c8ed66d 100644
--- a/tutorials/OOB_EC/OOP_3b.ipynb
+++ b/tutorials/OOB_EC/OOP_3b.ipynb
@@ -14,29 +14,25 @@
"### Abstract Base Classes\n",
"There are occasions when you need to write a class definition that is only partially complete such that it needs to be sub-classed before it becomes usable.\n",
" \n",
- "One can enforce such *Abstract Base Class* behaviour by utilizing the **ABCMeta** metaclass and the **@abstractmethod** decorator from the **abc** module as follows:\n",
+ "One can enforce such *Abstract Base Class* behaviour by utilizing the `ABCMeta` metaclass and the `@abstractmethod` decorator from the `abc` module as follows:\n",
+ "```python\n",
+ "from abc import ABCMeta, abstractmethod\n",
"\n",
- " from abc import ABCMeta, abstractmethod\n",
- "\n",
- " class ExpressionTree(TreeNode,metaclass=ABCMeta):\n",
- " .\n",
- " .\n",
- " #other code here\n",
- " .\n",
- " .\n",
- " @abstractmethod\n",
- " def evaluate(self,data=None):\n",
- " pass\n",
+ " class ExpressionTree(TreeNode,metaclass=ABCMeta):\n",
+ " .\n",
+ " .\n",
+ " #other code here\n",
+ " .\n",
+ " .\n",
+ " @abstractmethod\n",
+ " def evaluate(self,data=None):\n",
+ " pass\n",
" \n",
- " @abstractmethod\n",
- " def mutate(self):\n",
- " pass\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
+ " @abstractmethod\n",
+ " def mutate(self):\n",
+ " pass\n",
+ "```\n",
+ "\n",
"*Metaclasses* are beyond the scope of this tutorial, but they essentially control the behaviour of or act as templates for class definitions in the same way that class definitions act as templates for objects.\n",
" \n",
"Notice that we are now prevented from instantiating the ExpressionTree class:"
@@ -117,7 +113,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "we're using **kwargs to allow an arbitrary number of named parameters"
+ "we're using `**kwargs` to allow an arbitrary number of named parameters"
]
},
{
@@ -158,7 +154,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 5,
@@ -233,13 +229,13 @@
"\r\n",
"1\r\n",
"\r\n",
- "-0.12\r\n",
+ "-0.86\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 8,
@@ -260,7 +256,7 @@
{
"data": {
"text/plain": [
- "-0.11540706818074176"
+ "-0.8642967736584777"
]
},
"execution_count": 9,
@@ -315,13 +311,13 @@
"\r\n",
"1\r\n",
"\r\n",
- "-0.12\r\n",
+ "-0.86\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 11,
@@ -354,6 +350,13 @@
"ExpressionTree.__subclasses__()"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We might as well create a *variable* leaf node as well."
+ ]
+ },
{
"cell_type": "code",
"execution_count": 13,
@@ -416,13 +419,13 @@
"\r\n",
"2\r\n",
"\r\n",
- "x2\r\n",
+ "x3\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 15,
@@ -436,7 +439,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 19,
"metadata": {},
"outputs": [
{
@@ -457,16 +460,16 @@
"\r\n",
"2\r\n",
"\r\n",
- "x1\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 17,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -479,7 +482,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
@@ -488,16 +491,16 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "1"
+ "2"
]
},
- "execution_count": 19,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -508,16 +511,16 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "3"
+ "2"
]
},
- "execution_count": 20,
+ "execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@@ -529,7 +532,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
@@ -550,16 +553,16 @@
"\r\n",
"2\r\n",
"\r\n",
- "x3\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 21,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
@@ -568,9 +571,16 @@
"v1.plot()"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It's not that difficult to define non-leaf nodes too:"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
@@ -597,7 +607,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
@@ -606,7 +616,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 26,
"metadata": {},
"outputs": [
{
@@ -633,7 +643,7 @@
"\r\n",
"2\r\n",
"\r\n",
- "x3\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -645,7 +655,7 @@
"\r\n",
"1\r\n",
"\r\n",
- "-0.12\r\n",
+ "-0.86\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -657,10 +667,10 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 24,
+ "execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -673,16 +683,16 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "2.8845929318192582"
+ "1.1357032263415223"
]
},
- "execution_count": 25,
+ "execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
@@ -693,7 +703,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -705,49 +715,49 @@
"\r\n",
"\r\n",
- "\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 6,
@@ -235,6 +205,13 @@
"b1.plot()"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's see how our class handles `DataFrame` input data. We assume this will always be numerical data."
+ ]
+ },
{
"cell_type": "code",
"execution_count": 7,
@@ -418,7 +395,7 @@
"source": [
"### Note that for V1, the variable must exist in the data.\n",
"\n",
- "Probably should alter to return NaN."
+ "Probably should alter to return `NaN`."
]
},
{
@@ -465,13 +442,17 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Make a better *grow method* that can cope with the new subclasses"
+ "We see that we did not need to do anything special to make use of `DataFrame` data as Python itself is aware of vector and matrix operations."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
+ "### Make a better *grow method* that can cope with the new subclasses\n",
+ "\n",
+ "We have implemented these changes in `ExpressionTree`\n",
+ "\n",
"Classes 'know' about their own sub-classes, and this list is created dynamically."
]
},
@@ -599,38 +580,35 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "We can now design a function that can grow trees using arbitrary subclasses of **ExpressionTree**, we don't grow a new node if it already exists. The *maxd* parameter makes sure that growth will stop!\n",
+ "We can now design a function that can grow trees using arbitrary subclasses of `ExpressionTree`, we don't grow a new node if it already exists. The `maxd` parameter makes sure that growth will stop!\n",
"\n",
- "This is a good candidate for a *class method* and we will move it into the **ExpressionTree** class."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- " @classmethod\n",
- " def treegrow(cls,tree,maxd=5,varList=['x1','x2','x3']):\n",
- " d = tree.getDepth \n",
- " if d < maxd and tree.arity > 0:\n",
- " if (d==maxd-1):\n",
- " if not tree.left:\n",
- " k = np.random.choice([k for k in cls.__subclasses__() if k.arity==0])\n",
- " tree.left = k(varList=varList)\n",
- " else:\n",
- " if not tree.left:\n",
- " k = np.random.choice([k for k in cls.__subclasses__()])\n",
- " tree.left = k()\n",
- " cls.treegrow(tree.left,maxd)\n",
- " if d < maxd and tree.arity > 1:\n",
- " if (d==maxd-1):\n",
- " if not tree.right:\n",
- " k = np.random.choice([k for k in cls.__subclasses__() if k.arity==0])\n",
- " tree.right = k(varList=varList)\n",
- " else:\n",
- " if not tree.right:\n",
- " k = np.random.choice([k for k in cls.__subclasses__()])\n",
- " tree.right = k()\n",
- " cls.treegrow(tree.right,maxd)"
+ "This is a good candidate for a `class method` and we will move it into the `ExpressionTree` class.\n",
+ "\n",
+ "```python\n",
+ "@classmethod\n",
+ "def treegrow(cls,tree,maxd=5,varList=['x1','x2','x3']):\n",
+ " d = tree.getDepth \n",
+ " if d < maxd and tree.arity > 0:\n",
+ " if (d==maxd-1):\n",
+ " if not tree.left:\n",
+ " k = np.random.choice([k for k in cls.__subclasses__() if k.arity==0])\n",
+ " tree.left = k(varList=varList)\n",
+ " else:\n",
+ " if not tree.left:\n",
+ " k = np.random.choice([k for k in cls.__subclasses__()])\n",
+ " tree.left = k()\n",
+ " cls.treegrow(tree.left,maxd)\n",
+ " if d < maxd and tree.arity > 1:\n",
+ " if (d==maxd-1):\n",
+ " if not tree.right:\n",
+ " k = np.random.choice([k for k in cls.__subclasses__() if k.arity==0])\n",
+ " tree.right = k(varList=varList)\n",
+ " else:\n",
+ " if not tree.right:\n",
+ " k = np.random.choice([k for k in cls.__subclasses__()])\n",
+ " tree.right = k()\n",
+ " cls.treegrow(tree.right,maxd)\n",
+ "```"
]
},
{
@@ -641,7 +619,7 @@
{
"data": {
"text/plain": [
- "mylib.TreeLib.BinaryFuncMinus"
+ "__main__.BinaryFuncPow"
]
},
"execution_count": 20,
@@ -677,13 +655,13 @@
"\r\n",
"4\r\n",
"\r\n",
- "-\r\n",
+ "^\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 21,
@@ -728,7 +706,7 @@
"\r\n",
"4\r\n",
"\r\n",
- "-\r\n",
+ "^\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -746,7 +724,7 @@
"\r\n",
"12\r\n",
"\r\n",
- "x1\r\n",
+ "1.00\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -770,7 +748,7 @@
"\r\n",
"9\r\n",
"\r\n",
- "^\r\n",
+ "/\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -782,7 +760,7 @@
"\r\n",
"7\r\n",
"\r\n",
- "x1\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -794,7 +772,7 @@
"\r\n",
"8\r\n",
"\r\n",
- "1.00\r\n",
+ "x3\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -806,7 +784,7 @@
"\r\n",
"10\r\n",
"\r\n",
- "x1\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -818,7 +796,7 @@
"\r\n",
"11\r\n",
"\r\n",
- "1.00\r\n",
+ "x3\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -830,7 +808,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 23,
@@ -850,11 +828,11 @@
{
"data": {
"text/plain": [
- "0 0.000000e+00\n",
- "1 2.000000e+00\n",
- "2 2.400000e+01\n",
- "3 1.000000e+10\n",
- "Name: x1, dtype: float64"
+ "0 0.726308\n",
+ "1 0.745461\n",
+ "2 0.763143\n",
+ "3 1.000000\n",
+ "dtype: float64"
]
},
"execution_count": 24,
@@ -898,7 +876,7 @@
"\r\n",
"4\r\n",
"\r\n",
- "-\r\n",
+ "^\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -916,7 +894,7 @@
"\r\n",
"12\r\n",
"\r\n",
- "x1\r\n",
+ "1.00\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -940,7 +918,7 @@
"\r\n",
"9\r\n",
"\r\n",
- "^\r\n",
+ "/\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -952,7 +930,7 @@
"\r\n",
"7\r\n",
"\r\n",
- "x1\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -964,7 +942,7 @@
"\r\n",
"8\r\n",
"\r\n",
- "1.00\r\n",
+ "x3\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -976,7 +954,7 @@
"\r\n",
"10\r\n",
"\r\n",
- "x1\r\n",
+ "x2\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -988,7 +966,7 @@
"\r\n",
"11\r\n",
"\r\n",
- "1.00\r\n",
+ "x3\r\n",
"\r\n",
"\r\n",
"\r\n",
@@ -1000,7 +978,7 @@
"\r\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 26,
@@ -1013,11 +991,20 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
- "source": []
+ "source": [
+ "We have a very basic `ExpressionTree` class, and this completes the turorial.\n",
+ "\n",
+ "*Possible improvements:*\n",
+ "* a method to trim redundent branches i.e branches that evaluate to a constant replaced by constant leaf node. (reduce tree complexity)\n",
+ "* a method to prune branches to a constant value leaf node. (a kind of tree mutation).\n",
+ "* add subclasses that represent comparison functions, such as >=, !=, < etc.\n",
+ "* add subclasses that aggregate data e.g. `mean(X1)`\n",
+ "* Handle categorical variables.\n",
+ "\n",
+ "Further work could use this as part of a [*Genetic Programming*](https://en.wikipedia.org/wiki/Genetic_programming) class which could be used to find algebraic expressions that model experimental data, perhaps a topic for a future tutorial. "
+ ]
}
],
"metadata": {