#!/usr/bin/env python
# coding: utf-8
# # Table of contents
#
# 1. [Introduction](#Introduction )
# 2. [Variables](#Variables)
# 3. [Values](#Values)
# 4. [Operators](#Operators)
# 5. [Functions](#Functions)
# 6. [Encryption and Decryption](#Encryption_Decryption)
#
# # Introduction
# In this tutorial, the topics **variables**, **values**, **operators**, **functions** and **encryption and decryption** will be treated. Mainly, the basics of each topic will be introduced eventhough there would be a lot more to learn in each topic. However, if you are new to coding in Python, this tutorial is suited for you as it should help you understand the basics, which is key, before going into more advanced coding. Bear in mind that one could go into more detail for these concepts: If you are interested in learning more to a topic either refer to our links which lead you to external, more in-depth sources or use Google as you can find mostly anything related to Python online. However, before you do this: Make sure you understand the basics presented in this tutorial and that you can apply them.
#
# To the content of this tutorial: First, **variables** are looked at. Variables are key in any programming language and often serve as placeholders so that algorithms or functions can be coded and later on used for different values.
# An algorithm is a step-by-step approach on how to solve a problem. A function relates input to output. Without variables, the code would need to be rewritten for any new value which is rather cumbersome. You can assign different values to a variable as for example a number or a string which is a list of numbers and characters.
#
# Second, **values** are introduced. Values are whatever is assigned to a variable. There are several types of variables, such as: strings, numbers or booleans. A boolean is the result of a logical statement, hence a boolean either takes the value *true* or *false*.
#
# Third, **operators** are treated. Operators are used to perform mathematical operations on one or more variables or values. They are nothing else than functions which are already built in in Python. Like values, operators are classified into different types: For example, arithmetic operators such as **+** and **-**.
#
# Fourth, **functions** are introduced. Functions generally make coding more efficient as they can be used for solving similar problems for different inputs. There are different kinds of functions which can be used in Python: Either those which you code and later on call (=use) yourself or the ones that are already built-in in Python: as for example the function `print()`, which returns whatever is written in parantheses. Additionally, there also exist functions within so-called libraries or packages which first need to be imported into Python.
#
# Fifth, **encryption and decryption** are treated. Encryption and decryption are cryptographic algorithms which can be used for sending secret messages that can only be read by the recipient provided he or she is knowledgeable about the applied algorithm. Encryption is the algorithm which transforms a plain, legible text into an encrypted, unintelligible text. The algorithm which makes encrypted text again intelligible is referred to as decryption.
#
# In the end, a conclusion is provided which sums up the key takeaways of this tutorial such that you can check whether you understood the tutorial's content or whether you have to go over certain parts again.
#
# # Variables
#
# A **variable** can be thought of as reserved memory space in the kernel which stores a value. Remember from the previous tutorial; the kernel is where the code gets executed and where variables are stored. In other words, when a variable is defined, some space in the memory of the kernel is reserved for that particular variable. When this variable is called, whatever is saved within the related memory space is returned. For a start, how to define a variable and then how to call/use a variable is explained.
#
# *(Note: This chapter contains verbatim quotes from: https://www.programiz.com/python-programming/variables-constants-literals)*
#
# ## Define a variable
#
# Use the equality sign = to define variables: The value to the left of the equality sign is used as the name of the variable and the value on the right of the operator is used as the value stored in the variable (or more exact in the memory space which is referred to as the variable). Example:
#
# a = 1
#
# The variable would be named a and the value assigned to it would be 1.
# In[1]:
# You can run the code in this cell as an example.
# Change the values for the variables a, b and c and check whether the output of the code changes
a = 1
b = 2
c = -3
print(a) # As you can see: print() simply returns whatever is given within the parentheses
print(b)
print(a, c)
# ### Name a variable
# When naming a variable or in other words, when chosing whatever is written on the left-hand side of the equation, you are relatively free. However, the following rules should be adhered to:
# * Use lowercase characters,
# * avoid non-letter characters (like !,&, %,,...),
# * underscore and numbers can be used, but not at the beginning of the name,
# * keywords (e.g. if, while, return,...) cannot be used as a name,
# * be as explicit as possible.
# ### Multiple assignment
#
# It is possible to assign a single value to several variables simultaneously. Example:
#
# x = y = z = 5
#
# This assigns the value 5 to the variables x, y, and z.
# In[2]:
x = y = z = 5
print(x)
print(y)
print(z)
print(x+y+z)
# Pay attention: If, for example, the value 5 gets assigned to x, and x gets assigned to f. Then if a different value gets assigned to x, f still refers to the memory space that x previously referred to and not to the new memory space. This is because Python runs code line by line.
# In[3]:
x = 5 # 5 is assigned to the variable x
f = x # x is assigned to the variable f
print(x, f) # both variables refer to the same value (5), as f refers to the memory space of x
x = 10 # 10 is newly assigned to x
print(x, f) # x equals newly 10, however, f still refers to the old memory space of x, hence to 5
# This can be avoided by rewriting f = x after newly defining x:
X = 10
f = x
print(x, f)
# It is also possible to define several variables at once, in one line of code. To do so, separate the individual variables and the values assigned to them by commas.
# In[4]:
x, y, z = 5, 6, 7
print(x)
print(y)
print(z)
print(x + y + z)
# ## Call a variable
#
# The value that is assigned to a variable can be called by using `print()` and in parantheses the name of the variable.
# The advantages of using variables instead values are the following:
# * Makes code more readable,
# * Avoids repetition of values,
# * Functions can be re-used for different values,
# * Complex formula can be read easier.
#
# **Example:**
# capital = 10 000,
# interest = 0.05,
# periods = 12
#
# $future value = capital \cdot (1 + interest)^{periods}$
#
# In this example, the variables are chosen in a way that they help to understand a function or formula better. The values assigned to the variables can be changed quickly such that the formula can be re-used.
#
# Here an example on how you can use `print()` to call your variable:
# In[5]:
a = 2
print(a + a ** a + (a / a))
#
# # Values
#
# Every program written in Python (or in any programming language) operates on data. A single piece of information or data can be referred to as a **value**.
# In Python, values are grouped into different data types or so-called classes. The function `type()` returns you the class a value belongs to. Not all possible types of values will be discussed here, however, the most important ones are looked at:
# * Strings
# * Numbers
# * Boolean
# * Lists
# * Tuples
# * Dictionaries
#
# *(Note: This chapter contains verbatim quotes from: https://realpython.com/python-data-types/ and https://developer.rhino3d.com/guides/rhinopython/python-datatypes/ and http://dataanalyticsedge.com/2018/05/08/basics-of-python/)*
#
# ## Types of values
#
# ### Strings
#
# The value type string refers to lists of characters, spaces and numbers that can form words and sentences. However, they do not need to make linguistic sense to be strings. They are written within quotation marks: Pairs of single or double quotes are usually used. Examples of strings are: "Hello World!", "I am 20 years old." and "alskdfj2983".
#
# Here are a few basic operations for strings (in the code below, it can be seen how they are applied):
#
# The plus sign + is used to add up different elements of the string and the asterisk * is the repetition operator: To indicate how often a string should be repeated. Multiple strings can either be combined with the + sign, commas or with the function `.format()`.
#
# To break up a string over more than one line, include a backslash \ before each new line. Inversely, to receive a string that goes over multiple lines, one can make a line break in Python by adding a backslash with a small n \n at the respective point.
#
# A convenient way to create a string with both single and double quotes in it, is to create a triple-quoted string: In this way, new lines can be created.
# In[6]:
# We use the print() function to tell Python to output strings
print("Hello World")
print("Numbers such as 1234 can also be printed as part of a string.")
print("We can also separate text by using commas", " as shown in this line of text.")
print("We can also use plus signs" + " to add text fragments to each other.")
print("Pay attention to ", " spaces" + "when adding text", "so your out " + " put does not contain any", "typos.")
# Format function
a = 5
b = 4
c = 3
print("Number a is {}. Number b is {}. Number c is {}.".format(a,b,c))
# Splitting up a string over multiple lines
print("abc")
# Output multiple lines with a one-line string
print("a\nb\nc")
# Triple Quoted String
print("""This
is a "really"
convenient 'way' of
adding multiple lines.""")
# Addition and multiplication of strings
text = "Hello"
print(text + text) # This will output the sentence twice
print(text * 3) # This will output the sentence three times
# How to subset a string
text = "Switzerland is so small!"
print(text[0]) # creates the subset S which is the character position 0
print(text[0:11]) # shows the subset Switzerland, the character position 0 to 10
# #### Functions for string operations
#
#
# In Python there are several built-in functions (what a built-in functions is, will be treated in a upcoming section called *functions*) which are useful for operations with strings. However, as these so-called methods are type-specific, these functions can only be used for strings.
#
# The most important methods are depicted in the following list:
#
#
# | Function | Description |
# |---|---|
# | strip() | removes any whitespace from the beginning or end |
# | len() | returns the length of a string |
# | lower() | returns string in lower case |
# | upper() | returns string in upper case |
# | replace() | replaces string with another string |
# | split() | splits tring into substring if it finds instrances of the seperator |
#
# Here are examples showing how to apply these functions:
# In[7]:
a = " Hello, World!"
print(a.strip( )) # takes away / "strips away" any superfluous spaces
b = "Hello, World!"
print(len(b)) # counts elements within string (elements = characters, spaces, signs, numbers)
print(b.lower())
print(b.upper())
print(b.replace("W","Z"))
print(b.split(","))
# ### Numbers
# Another value type or class of Python are numbers. Python 3 supports three different numerical types:
# * int (integers)
# * float (floating point real values)
# * complex (complex numbers)
#
# Normally, Python will convert a number automatically from one type to another if needed and this is fine most of the time. But under certain circumstances if a specific numeric type is needed, the format can be forced by using additional syntax from the table below:
#
# | Type | Format | Descrption |
# |---|---|---|
# | int() | a = 10 | Integer |
# | float() | a = 45.67 | (.) Floating point real values |
# | complex() | a = 4-3j | (j) Complex (imaginary) number |
#
# #### Integers
# Integers are whole numbers, positive or negative, without decimals, of unlimited length such as: 2, 6, 2368, -78, -9746286.
#
#
# #### Floats
# Floats, or "floating point number" are numbers, positive or negative, containing one or more decimals such as: 1.1, 0.00025, 134.56, -78.64, -12000.7, 5.0
#
# Floats can also be scientific numbers with an "e" to indicate the power of 10.
#
# #### Complex Numbers
# A complex number consists of an ordered pair of real floating-point numbers denoted by x + yj, where x and y are the real numbers and j is the imaginary unit. Examples: 2+45j, 9.322e-36j
#
# #### Built-in mathematical functions and math package
# Python supports many built-in funtions of which the most useful ones for this topic are presented in the following overview table:
#
# | Function | Description |
# |---|---|
# | abs() | Returns absolute value of the value; (positive) distance between the value and zero |
# | divmod() | Returns quotient and remainder of integer division |
# | max() | Returns the largest of the given arguments or items in an iterable |
# | min() | Returns the smallest of the given arguments or items in an iterable |
# | pow() | Raises a number to a power |
# | round() | Rounds a floating-point value |
#
#
# Other functions performing useful calculations in this context may also be appropriated by making use of the math library in Python. However, this must be imported into the code beforehand with `import math`. We will cover library in more detail in the part *functions* later on.
#
# Consequently, an overview of important functions of the math-package is provided:
#
# | Function | Description |
# |---|---|
# | math.ceil() | Returns the ceiling of the value; smallest integer not less than the value |
# | math.exp() | Returns the exponential of the value; the euler number to the power of the value |
# | math.floor() | Returns the floor of the value; largest integer not greater than than the value |
# | math.log() | Returns the natural logarithm of the value for value > 0 |
# | math.log10() | Returns the base-10 logarithm of value of value > 0 |
# | math.sqrt() | Returns the square root of the value for value > 0 |
# | math.cos() | Returns the cosine of the value radians |
# | math.sin() | Returns the sine of value radians |
# | math.tan() | Returns the tangent of value radians |
# In[8]:
# Integer numbers
print(10)
print(type(10))
print(0x50)
print(type(0x50))
# Float numbers
print(4.2)
print(type(4.2))
print(4.2e-4)
print(type(4.2e-4))
# Complex numbers
print(4+8j)
print(type(4+8j))
# Built-in Mathematical Functions of Python
print(abs(-5))
print(divmod(17,6))
print(max(4,3,7,4,9,2,3))
print(min(4,3,7,4,9,2,3))
print(pow(4,2))
print(round(14.7))
# Math package functions
import math
print(math.ceil(5.6))
print(math.exp(4))
print(math.floor(4.9))
print(math.log(150))
print(math.log10(125))
print(math.sqrt(2))
print(math.sin(30))
print(math.cos(85))
print(math.tan(22.5))
# For further information on the value type numbers, click __[here](https://www.tutorialspoint.com/python/python_numbers.htm)__ or __[here](https://www.w3schools.com/python/python_numbers.asp)__.
# ### Boolean
# Boolean values are either `True` or `False`. They are usually the output of a function that includes a logical operator. Expressions in Python are often evaluated in a Boolean context, meaning, they are interpreted to represent truth or falsehood. We will explain later in the notebook what exactly a logical operator is, but simply put, it tests if a statement is true or not.
# In[9]:
a = True
b = False
print(a)
print(type(b))
# ### Lists
# Lists are the most versatile of Python's compound data types. A list contains items separated by commas and enclosed within square brackets `[]`. In Python, any data type can be added to a list. Once a list is created using only integers as its elements, it is possible to add other value types, such as strings or booleans.
#
# The values stored in a list can be accessed using the slice operator `[]` and `[:]` with indexes starting at 0 in the beginning of the list and working their way to end -1. The plus sign + is the list concatenation operator, and the asterisk * is the repetition operator.
#
# To add elements to a list, we use the very useful method `.append()` that will attach a certain value to the end of the respective list.
#
# Lists are not limited to a single dimension; a so-called list of lists is possible. Multiple dimensions can be declared by seperating them with commas. For instance, in a two-dimensional list, the first number is always the number of rows where the second number is the number of column.
#
# For further information, click __[here](https://www.w3schools.com/python/python_lists.asp)__.
# In[10]:
# You can run the code in this cell to create lists, print them and to subset them.
number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
string_list = ["Hello", ",", "how", "are", "you", "?"]
mixed_list = [1, "I", 5.6, "goodbye"]
print(number_list)
print(string_list)
print(mixed_list)
print(mixed_list[3])
print(number_list[5:])
print(string_list[:3])
print(mixed_list[0:2])
# Method .append()
mixed_list.append("XYZ")
mixed_list.append(4-5j)
print(mixed_list)
# Example of a two-dimensional list
my_list = [[5,3],[2,3]]
print(my_list)
print(my_list[0],my_list[1])
# ### Tuples
# A tuple is another sequence data type that is similar to the list. A tuple consists of a number of values separated by commas. Unlike lists, however, tuples are enclosed within parentheses.
#
# The main differences between lists and tuples are: Lists are enclosed in square brackets `[]` and their elements and size can be changed, while tuples are enclosed in regular parentheses `()` and cannot be updated. Tuples can be thought of as read-only lists.
# In[11]:
# You can run the code in this cell to create some tuples, print them and subset them
# In this example, the tuples behave exactly as lists would
number_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
string_tuple = ("Hello", ",", "how", "are", "you", "?")
mixed_tuple = (1, "I", 5.6, "goodbye")
print(number_tuple)
print(string_tuple)
print(mixed_tuple)
print(mixed_tuple[1])
print(number_tuple[5:])
print(string_tuple[0:5])
# In[12]:
# The difference between lists and tuples becomes clearer, when we try to update the data contained therein
new_list = [1, 2, 2, 4, 5]
new_tuple = (1, 2, 2, 4, 5)
new_list[2] = 3
print(new_list)
new_tuple[2] = 3
print(new_tuple)
# It is possible to update the list, but not the tuple. Tuples are read-only.
# When running this cell, Python will display an error message, highlighting row 10
# Add a hashtag # symbol to row 10 and re-run the cell
# In[1]:
# To update the tuple from the previous exercise, we need to recreate the whole tuple with the new values
new_tuple = (1, 2, 2, 4, 5)
new_tuple = (1, 2, 3, 4, 5)
print(new_tuple)
# ### Dictionaries
# Dictionaries are lists of Key:Value pairs. They are very helpful when related information is held that is associated through keys. The main operation here is to extract a value based on the key name.
#
# Unlike lists, where index numbers are used, dictionaries allow for the use of a key to access its members.
#
# A dictionary key can be almost any value type but is usually a number or string. Values, on the other hand, can be any arbitrary Python object. The Keys must, however, be unique in the respective dictionary.
#
# Dictionaries are enclosed by curly brackets `{}` with different pairs being seperated by a comma `,` and associated with a colon `:`. The values can be assigned and accessed using square braces `[]`. Inversely however, the keys cannot be accessed that when using the respective corresponding values.
#
# Dictionaries have no concept of order among elements. It is incorrect to say that the elements are "out of order"; they are simply unordered.
#
# Useful methods in the context of dictionaries are for example `.keys()` that prints only the keys of a certain dictionary or `.values()` that prints only the values of a certain dictionary.
# In[2]:
# You can run the code in this cell to create a simple dictionary
d1 = {"Schweiz" : "Zürich", "UK" : "London", "Frankreich" : "Paris"}
print(d1)
print(d1["UK"])
print(d1.keys()) # Print only the keys of the dictionary
print(d1.values()) # Print only the values of the dictionary
# print(d1["Paris"])
# In[3]:
# Here is another example of a dictionary
GerEngDictionary = {"Hallo" : "Hello", "Auf Wiedersehen" : "Goodbye", "Gut" : "Good", "Schlecht" : "Bad"}
print(GerEngDictionary)
# ## Data Type Conversion / Variable Type Specification
# Sometimes, conversions between the different data types or - more generally - value type specifications are necessary. To convert between types or specifcy variable types, you simply use the type name as a function.
#
# There are several built-in functions to perform this task which will return a new object representing the final (converted) value.
#
# | Function | Description |
# |----------|-------------|
# | int() | Converts to an integer |
# | float() | Converts to a floating-point number |
# | complex() | Converts to a complex number |
# | str() | Converts to a string representation |
# | tuple(l) | Converts l to a tuple |
# | list (l) | Converts l to a list |
# | dict(t) | Creates a dictionary, t must be a sequence of (key, value) tuples |
#
# Keep in mind, that not all conversions are possible, since they do not make sense. Converting a string into a float will not work through these in-built functions.
# In[4]:
# Run the code in this cell to convert the data type of the variables below
# The function type() checks the data type of a variable. We use type() to show that the data type conversion worked
a = 4.0
print(type(a))
print(type(int(a)))
b = 5.0
print(type(b))
print(type(str(b)))
# In[5]:
# Below is an example of a data type conversion that does not work
# The value assigned to the variable a is the string "four"
# Python does not understand the meaning behind the word four, so cannot complete the conversion
a = "four"
print(type(a))
print(type(int(a)))
#
# # Operators
#
# Operators are used when you want to perform mathematical operations on one or more variables or values. Operators are nothing else than functions which are already built-in in Python. Operators can be categorized into different types: Arithmetic, boolean, assignment, logical, membership and identity operators that are each discussed in more detail below. Another important aspect of operators, is how they are ranked, meaning if several operators are used in one line of code, which operators are executed first.
#
# *(Note: This chapter contains verbatim quotes from: https://www.tutorialspoint.com/python/python_basic_operators.htm)*
#
# ## Types of Operators
#
# ### Arithmetic Operators
# Arithmetic operators are basic mathematical functions. They work with number values, but some also work with strings and other values.
#
# | Symbol | Name | Description | Example |
# |--------|------|-------------|--------------|
# | + | Addition | Adds values on either side of the operator | 1 + 1 = 2 |
# | - | Subtraction | Subtracts values on right side of the operator from left side of operator | 2 - 1 = 1 |
# | * | Multiplication | Multiplies the value on the right side of the operator with the value on the left side | 2 * 3 = 6 |
# | / | Division | Divides the value on the left side of the operator by the value on the right side | 6 / 3 = 2 |
# | % | Modulus | Divides the value on the left side of the operator by the value on the right side and returns the remainder | 7 % 3 = 1 |
# | ** | Exponent | Performs exponential (power) operation on the value on the left of the operator | 2 ** 3 = 8 |
# | // | Floor Division | The division of operands where the result is the quotient in which the digits after the decimal point are removed. But if one of the operands is negative, the result is floored, i.e., rounded away from zero (towards negative infinity) | 9 // 2 = 4 |
# In[6]:
# You can run the code in this cell to perform some basic math in Python
print(1 + 1)
print(2 - 1)
print(2 * 3)
print(6 / 3)
print(7 % 3)
print(2 ** 3)
print(9 // 2)
# In[7]:
# It is also possible to combine these operators
# We will cover below the precedence the operators take. Keep in mind that expressions within parentheses are executed first.
print(2 + 3 * 2)
print((2 + 3) * 2)
print(5 ** 2 + 1)
print(5 ** (2 + 1))
# ### Boolean Operators
# Boolean operators compare the values on either sides and decide the relation among them. They are also called Comparison operators or Relational operators. The output of an operation with a Boolean operator will be a Boolean value `True` or `False`.
#
# | Symbol | Description | Example |
# |--------|-------------|---------|
# | == | Checks if values on either side are equal | 2 == 2 returns True |
# | != | Checks if values on either side are not equal | 2 != 3 returns True |
# | > | Value on the left greater than value on the right | 5 > 2 returns True |
# | < | Value on the left smaller than value on the right | 5 < 2 returns False |
# | >= | Value on the left greater or equal to value on the right | 3 >= 2 returns True |
# | <= | Value on the left smaller or equal to value on the right | 2 <= 2 returns True |
#
# Boolean operators cannot be used between all values. While it makes sense to compare an integer to a float, comparing a string to a float will lead to an error.
# In[8]:
# You can run the code in this cell to use Boolean operators in practise
print(2 == 2)
print(2 != 3)
print(5 > 2)
print(5 < 2)
print(3 >= 2)
print(2 <= 2)
# In[9]:
# In this code, Python can perform the comparison between a float and an integer
# However, the relation between a string and an integer will lead to an error
print(2.0 > 5)
print("three" > 2)
# ### Assignment Operators
# Assignment operators are used to define variables or to update the value of variables. We have already used the most basic one, the equality sign =, to define functions.
#
# | Symbol | Description | Example |
# |--------|-------------|---------|
# | = | Assigns value on the right side of the operator to the variable on the left side | a = b assigns value of b to a |
# | += | Adds value on right side of operator to value on the left and assigns the result to the value on the left | a += b returns the same as a = a + b |
# | -= | Subtracts value on the right side of the operator from the value on the left and assigns the result to the value on the left | a -= b returns the same result as a = a - b |
# | *= | Multiplies value on the right with the value on the left side of the operator and assigns the result to the value on the left | a *= b returns the same result as a = a * b |
# | /= | Divides the value on the left side of the operator with the value on the right and assigns the result to the value on the left | a /= b returns the same result as a = a / b |
# | %= | Divides the value on left side of the operator by the value on the right side and assigns the remainer to the value on the left side | a %= b returns the same result as a = a % b |
# | \**= | Performs exponential (power) operation on value on the left side and assigns the result to the value on the left side | a \**= 2 returns the same result as a = a \** 2 |
# | //= | It performs floor division on operators and assigns value to the left operand | c //= a returns the same result as c = c // a |
# In[10]:
# Run the code in this cell to use the Addition/Assignment operator
a = 1
b = 2
a += b
print(a)
# In[11]:
# Run the code in this cell to use the Division/Assignment operator
a = 15
b = 5
a /= b
print(a)
# ### Logical Operators
# Logical Operators are used to reverse the logical state of its operand. They can also add conditions that need to be met for a statement to be true.
#
# | Symbol | Description | Example |
# |--------|-------------|---------|
# | and | Adds a condition that needs to be met | a < 4 and a > 2 only returns True if integer value of a is 3 |
# | or | Adds alternative condition | a < 4 or a < 2 will return True, if either condition is fulfilled |
# | not | Reverses logical state | Not (a < 4 and a > 2) will return False if integer value of a is 3 |
# In[12]:
# You can run the code in this cell to use the logical operators
a = 3
print(a < 4 and a > 2)
print(a < 4 or a < 2) # This statement returns True, since either condition can be fulfilled to return True
# ### Membership Operators
# Membership operators test if a certain value is part of a string, list or tuple.
#
# | Symbol | Description | Example |
# |--------|-------------|---------|
# | in | Checks if a certain value is part of a certain sequence. Returns True if it is, otherwise it returns False | a in b returns True if a is part of sequence b |
# | not in | Checks if a certain value is not part of a certain sequence. Returns True if it is not, otherwise (i.e. if the value is part of the sequence) it returns False | a not in b returns False, if a is part of sequence b |
# In[13]:
# You can run the code in this cell to create a list and then check if certain values are part of the list
list = [1, 3, 5, 7, 9]
print(1 in list)
print(2 in list)
print(3 in list)
print(4 in list)
print(5 in list)
print(6 not in list)
print(7 not in list)
print(8 not in list)
print(9 not in list)
# ### Identity Operators
#
# Identity Operators compare the memory location of two objects.
#
# | Symbol | Description | Example |
# |--------|-------------|---------|
# | is | Checks if the values on either side point to the same location in memory. If they do it returns True, otherwise it returns False | a is b will return True if id(a) equals id(b) |
# | is not | Checks if the values on either side point to a different location in memory. If they do it returns True, otherwise (i.e. if they point to the same location in memory) it returns False | a is not b will return True if id(a) does not equal id(b) |
# In[14]:
# You can run the code in this cell to create variables and check them against each other
a = 2
b = 3
c = 4
d = 2
e = 3
print(a is d)
print(a is b)
print(c is 2 * a)
print(b ** a is e ** d)
# ## Ranking of Operators
# Operators are executed in a certain order. Being aware of the priority that operators take when Python executes code is crucial. The table below shows which operations are executed first in descending order.
#
# | Precedence | Operator | Description |
# |------------|----------|-------------|
# | 1 | ** | Exponentiation (Raise to the power) |
# | 2 | * / % // | Multiplication, Division, Modulo and Floor Division |
# | 3 | + - | Addition and Subtraction |
# | 4 | > < >= <= | Comparison operators |
# | 5 | == != <> | Equality operators |
# | 6 | = += -= *= /= %= \**= //= | Assignment operators |
# | 7 | 'is' 'is not' | Identity operators |
# | 8 | 'in' 'not in' | Membership operators |
# | 9 | 'not' 'or' 'and' | Logical operators |
#
# # Functions
#
# ## What are functions?
#
# A **function** is a block of code which relates input to output and can be used for different inputs (variables, values).
#
# One can either use **self-created functions** or already **built-in functions** in Python. Self-created functions refer to functions that are defined and coded by a person, while built-in functions are already pre-coded and can simply be applied.
# Some examples for built-in functions are:
# * `print()` - which returns whatever is written within the parantheses,
# * `type()` - which was used before to find the class of a value,
# * and all the operators (such as +,-,OR,etc.).
#
# These examples for built-in functions can simply be called/used. However, some functions are not already built-in in the Python interpreter, for those a library with pre-coded algorithms first needs to be imported. An algorithm describes a step-by-step approach on how to solve a problem. However, in this tutorial, the terms functions and algorithms can be used interchangeably.
#
# ## Self-created functions
# How self-created functions can be defined and later on called will be looked at and as well why functions make coding more efficient.
#
# ### Define a function
# A function is structured as follows:
#
# The abbreviation def stands for define. What comes after def is the name of the function(function_name). In parentheses (input) a parameter/placeholder is added for whatever value or variable will later on be plugged into the function. Then below this, the actual definition of the function follows which is referred to as "documentation string" or "doc string" for short. The doc string is used to give a brief explanation of what the function does. This allows other users (or the same user, if using the function later one) to get a quick summary of a function's purpose. The definition of the function is ended with return which gives back the output of the function.
# In[16]:
def function_name(input):
"documentation string"
return output
# In[17]:
# To look up the doc string of a function, enter the function name followed by ?
# The information will be displayed when you run the code in this cell
get_ipython().run_line_magic('pinfo', 'function_name')
# In[18]:
# Another option is to use the built-in function help followed by the object in parentheses
help(function_name)
# In[19]:
# More complex functions require more documentation
help(print)
# Define a function which calculates the square of its input and call it `square_function()`:
# In[20]:
def square_function(x):
"function that calculates the second power"
result = x**2
return result
# ### Name a function
# For naming a function the same rules like for naming a variable apply:
# * Use lowercase characters,
# * avoid non-letter characters (like !,&, %,,...),
# * underscore and numbers can be used, but not at the beginning of the name,
# * keywords (e.g. if, while, return,...) cannot be used as a name,
# * be as explicit as possible.
#
# ### Call a function
# To call or in other words to use a function, the name of the function together with the desired input in parentheses should be run:
# In[21]:
square_function(5)
# ### Why functions are efficient
# To illustrate why functions make programming more efficient, the `square_function()` is used to calculate the square of all numbers form 0 to 100:
# In[ ]:
mylist = range(100)
result_list = []
i=0
for i in mylist:
result_list.append(square_function(i))
print(result_list)
# It can be observed that the square_function not only saves a lot of time, as it would have been cumbersome to calculate the square of every number individually, it also helped to avoid errors like missing out a number.
#
# Here is a short guide helping to understand the code above as not all that is used has been treated yet:
# * `range(x)` creates a list of consecutive integeres from 0 to x,
# * `for i in mylist` is a loop which goes through every element in mylist (loops will be treated in a later tutorial),
# * `result_list = []` means that the list of all results at the beginning is empty,
# * `result_list.append(square_function(i))` means that every output of the `square_function()` is appended to the `result_list`,
# * `print(result_list)` shows the list of all results.
#
# ### Difference between defining variables inside and outside of functions
# There is one important feature of defining a variable on the top of your script. Such a variable is called global and can be accessed from anywhere in the script after the variable was defined. Here is an example of defining a global variable.
# In[22]:
a = 2
def function():
print(a)
function()
print(a)
# When instead of only defining a variable at the beginning of a function, we define a variable inside the function, the relation and with it the priorities change. This can be seen in the following example.
# In[23]:
a = 2
def function():
a = 3
print(a)
function()
print(a)
# We have a built-in keyword in python that gives us more versatility in chosing when to use a globally defined variable or not. To use it, simply type global before the assigned variable name. Here is an example to illustrate the usage of the global keyword. The variable that we are referring to inside the function is now the global c, defined at the beginning of the script.
# In[24]:
c = 20
def change_value(new_c):
global c
c = new_c
print(c)
change_value(40)
print(c)
# There is another keyword that can be used when working with nested functions. We can use the keyword nonlocal when we want to avoid saving the variable locally, as in inside a function, but we want it saved a level higher. So instead of saving the variable inside the function, we save it at the beginning of the function, but also not globally, because there could be another function in between them.
# Let us first set up an example where we can illustrate the usage of nonlocal.
# In[30]:
d = 20
def outer():
d = 30
def inner():
d = 40
print('from inner:', d)
inner()
print('from outer:', d)
outer()
print('gloablly:', d)
# If we now use the nonlocal keyword in the inner function, we will see that the value of the variable 'e' we will be using in this particular function will be 40, as defined in the inner function. So that when calling the function outer, we will have 'e' defined as 40 and not 30.
# In[29]:
e = 20
def outer():
e = 30
def inner():
nonlocal e
e = 40
print('from inner:', e)
inner()
print('from outer:', e)
outer()
print('globally:', e)
# Now, if for example we want to refer to the global value of the variable in the inner function, without affecting the outer function, we could just set our variable f to global before defining it in the inner function. This way, the global variable will be changed to 40, but the definition of our variable in the outer function will not be affected.
# In[28]:
f = 20
def outer():
f = 30
def inner():
global f
f = 40
print('from inner:', f)
inner()
print('from outer:', f)
outer()
print('gloablly:', f)
# ## Built-in functions
# Although self-created functions can be defined in Python, whenever possible, built-in functions should be used as they are better designed than self-created functions. There exist two types of built-in functions in Python: Those within Python which can be simply called and those within libraries which first need to be imported.
#
# ## Built-in functions within Python
# For a start, built-in functions which form part of Python are looked at. These built-in functions can simply be called by using the name of the function and adding an input into its parentheses. Refer to the list below for common built-in functions (some might look familiar since we have encountered them in previous sections). This list is not exclusive, a more comprehensive list on built-in functions can be found __[here](https://data-flair.training/blogs/python-built-in-functions/)__.
#
# | Function | Description |
# |----------|------------------------------------------------------|
# |abs() | Returns the absolute value |
# |append() | Adds single elements to a list |
# |count() | Returns number of occurences of an element in a list |
# |len() | Returns length of a list |
# |lower() | Returns lowercase string |
# |upper() | Returns uppercase string |
# |max() | Returns largest element of a list |
# |min() | Returns smallest element of a list |
# |print() | Returns what is given within parantheses |
# |return() | Like print, used for defining functions |
# |sum() | Adds up items of a list |
# |round() | Returns rounded value |
# |help() | Shows information about function |
#
# ## Built-in functions within libraries
# Secondly, we consider built-in functions for which first a library needs to be imported. A library contains one or more algorithms which are already pre-coded, in order that they can be called as soon as the library is imported. Note that the terms modules, packages and libraries are used interchangeably for the purposes of this tutorial.
#
# ### How to import libraries
# The following three sets of code import libraries into Python:
# In[ ]:
import random #imports the entire library
from numba import jit #imports parts of a library
import numpy as np #imports a library and gives it a shorter name
#
#
# ### How to install libraries
# When running into an error whilst importing a library, it could be because the library has not been installed yet into your Python. When coding in Jupyter Notebook, some libraries are already installed and can simply be imported if needed, as for example numpy, scipy or random. Others need to be installed first. This is done by using **pip**. The pip is an installer program which is included in any later version than Python 3.4.
#
# Remember from [Tutorial 2](https://nbviewer.jupyter.org/github/drarnau/Programming-for-Quantitative-Analysis/blob/master/01_Preliminaries.ipynb) that any pip command can only be run outside Jupyter Notebook, meaning in the command line as follows (let us assume we want to install the library "theano"):
#
#
# (base) C:\users\username pip install theano
#
# Further information on how to install libraries can be found __[here](https://packaging.python.org/tutorials/installing-packages/#requirements-for-installing-packages)__.
#
# ### Main libraries
# Here is an overview on the most common libraries and packages in Python. Further libraries can be found __[here](https://wiki.python.org/moin/UsefulModules)__.
#
# | Library | Description |
# |:-- |--------------------------------------------------------------------------------------------|
# |**Math** | |
# | math | Arithmetic functions |
# |**Data Science** | |
# | numpy | How to deal with data |
# | random | Generates random numbers |
# | scipy | Manipulating and visualizing data |
# | numba | Translates Python code to optimized and faster machine code |
# |**Machine-learning** | |
# | scikit-learn | Data mining and machine learning tasks |
# | theano | Uses GPU for intensive computation |
# | TensorFlow | Goolge framework for machine learning based projects |
# |**Web scraping and natural numbers processing** | |
# | scrapy | Web scraping tasks |
# | NLTK | Natural number processing (analyzing texts and finding correlations) |
# | pattern | Includes all web scraping and natural numbers processing libraries in one |
# |**Plotting and visualization** | |
# | matplotlib | Data visualization |
# | seaborn | Based on matplotlib library, to generate graphs in an easier way than matplotlib |
# | bokeh | Interactive, zoomable graphs. Based on modern web browser, uses javascript to render graph |
# | ggplot | Data visualization using grammar of graphics method (derivation of R's ggplot2) |
#
# ### Matplotlib
# Discussing all the above libraries individually would go beyond the scope of this tutorial, however, the library **matplotlib** is introduced in more detailed as it is very useful for visualizing data.
#
# Matplotlib is a Python 2D plotting library which can be used to visualize data as graphs, barcharts, histograms, timelines as well as further sorts of illustrations. This library is increasingly popular as it is considered a favorable alternative to the popular software **MATLAB**.
#
# MATLAB (*matrix laboratory*) is a programming language developed by MathWorks for solving mathematical problems and providing graphical representation of the results. Matlab is mainly used in the fields of engineering, science and economics.
# The Python library Matplotlib together with the Python libraries numpy and scipy (see: [Main libraries](#link_table)) is a good alternative to MATLAB as Python is free, whilst a license for MATLAB is rather expensive.
#
# The goal is to plot the function: $y = 2x$. How this is achieved is looked at step-by-step:
# 1. Generate the frame for the graph:
# *Notice that after every step `plt.show()` is used so that you can see how the graph looks like so far*
# In[ ]:
# Import matplotlib:
import matplotlib.pyplot as plt
# Generate the frame:
fig = plt.figure() # plots figure (can be thought of as area in which all needed for plot incl. axes, graph, labels is contained)
ax = plt.axes() # without axes the figure is not bounded and would not be displayed
#Show the graph:
plt.show()
# 2. Define the length of the axes and label them:
# In[ ]:
# Import matplotlib:
import matplotlib.pyplot as plt
# Generate the frame:
fig = plt.figure()
ax = plt.axes()
# Define length of axes and label them:
plt.axis([0,10, 0,20]) # the first two values define from where to where x-axis goes, the second two values for the y-axis
plt.grid() # adds a grid s.t. points on graph can be read off easier
plt.ylabel('Y-axis')
plt.xlabel('X-axis')
#Show the graph:
plt.show()
# 3. Plot the function: *Notice that for this step we also need to make use of the library numpy.*
# In[ ]:
# Import matplotlib and numpy:
import matplotlib.pyplot as plt
import numpy as np
# Generate the frame:
fig = plt.figure()
ax = plt.axes() # without axes the figure cannot be displayed
# Define length of axes and label them:
plt.axis([0,10, 0,20]) # the first two values define from where to where x-axis goes, the second two values for the y-axis
plt.grid() # adds a grid, so that points on graph can be read off easier
plt.ylabel('Y-axis')
plt.xlabel('X-axis')
# Define x and y such that they adhere to the function and then plot them:
x = np.arange(0,10) # np.arange defines x as a range of evenly arranged inputs from 0 to 10
y = 2*x
ax.plot(x, y)
#Show the graph:
plt.show()
# 4. Change linestyle and color of graph:
# In[ ]:
# Import matplotlib and numpy:
import matplotlib.pyplot as plt
import numpy as np
# Generate the frame:
fig = plt.figure()
ax = plt.axes()
# Define length of axes and label them:
plt.axis([0,10, 0,20]) # the first two values define from where to where x-axis goes, the second two values for the y-axis
plt.grid() # adds a grid, so that points on graph can be read off easier
plt.ylabel('Y-axis')
plt.xlabel('X-axis')
# Define x and y such that they adhere to the function and then plot them:
x = np.arange(0,10) # np.arange defines x as a range of evenly arranged inputs from 0 to 10
y = 2*x
ax.plot(x, y, 'r-') # solid red
ax.plot(x, y+1, 'k:') # dotted black
ax.plot(x, y+2, 'g-.') # dash-dot green
ax.plot(x, y+3, 'c:') # dotted blue
#Show the graph:
plt.show()
# This is one way of how to plot a function with matplotlib. For further insights into matplotlib go to __[Matplotlib.org](https://matplotlib.org/)__ or __[Python online handbook](https://jakevdp.github.io/PythonDataScienceHandbook/04.00-introduction-to-matplotlib.html)__.
#
# # Encryption and Decryption
# Encryption is an algorithm for transforming information in order to make it unintelligible. Decryption is an algorithm which transforms encrypted information so that it is intelligible again.
# Both encryption and decryption are socalled cryptographic algorithms. In most cases, for encryption and decryption two related algorithms are applied.
#
# With most modern cryptography, the ability to keep encrypted information secret is not necessarily dependent on the cyptographic algorithm as this is widely known, but on a number called a key that must be used with the algorithm to produce an encrypted result or to decrypt previously encrypted information. Decryption with the correct key is simple. Decryption without the correct key is very difficult and in some cases impossible.
#
# ![Encryption Decryption](imgs/03_EncryptionDecryption.png)
# Source image: https://stackoverflow.com/questions/4948322/fundamental-difference-between-hashing-and-encryption-algorithms
#
# ## Encryption
# When encrypting a plain text, output and input of the encryption algorithm are defined as follows:
#
# * **Input** = A string of characters and spaces.
# * **Output** = A list of numbers and spaces.
#
# To make an example on how to relate the above input to its output, the following **encryption algorithm** is defined:
# * Go through the elements in the string:
# * If the element is a **space**: Append it to the output list.
# * If the element is **not a space**:
# 1. Find position in English alphabet,
# 2. Square position,
# 3. Append it to the output list.
#
# The code below shows how to apply the encryption algorithm to encrypt a plain text.
#
#
#
#
# In[32]:
import string
def encrypt(mystring): # as before with functions, we define the encryption algorithm and give it the name encrypt
mystring = mystring.lower() # lower to transform all characters into lowercase
mylist = [] # mylist is the output list, which is empty in the beginning
for character in mystring: # for character in mystring is a loop (treated in later tutorial) which goes through all character within mystring
if character.isspace(): # checks whether character under scrutiny is a space
mylist.append(character) # If the character is a space: add it to mylist (output list)
else:
mynumber = string.ascii_lowercase.index(character) # If character is not a space, find position in English alphabet
# call this position mynumber
mynumber = (mynumber)**2 # Square the position mynumber
mylist.append(mynumber) # add it to the output list
return mylist # return the output list
# As an example, the plain text: "python is fun" is encrypted.
# In[33]:
encrypt("python is fun")
# Notice that if you use any non-letter characters (like ?, . , !, ...), you will run into an error as these do not form part of the English alphabet.
#
# ## 5.2. Decryption
# When decrypting an encrypted text, output and input are defined as follows:
# * **Input** = A list of numbers and spaces.
# * **Output** = A string of characters and spaces.
#
# Notice that this is the reverse of the encryption, as with decryption we try to get back from the encrypted text to the plain text. Therefore, the decryption algorithm needs to be the reverse of what was used to encrypt the text:
#
# * Go through all elements in the list:
# * If the element is a space: Append it to the output string.
# * If the element is not a space:
# 1. Take square root,
# 2. Find what letter it is assigned to in the English alphabet,
# 3. Append it to the output string.
#
# The code below shows how to apply the decryption algorithm to decrypt an encrypted code:
# In[34]:
import string
def decrypt(mylist):
mystring = "" # as it is a string we use "" and not [] to show that there is nothing assigned yet to mystring
for element in mylist: # as before: loop which goes through each element within mylist
if element == " ":
mystring = mystring + element # if the element is a space, it gets added to the output list
else: # if the element is a number,
element = int(element**0.5) # the square root is taken of that element (to the power of 0.5)
# then int() converts any floats to integers by rounding, as only for integers position in English alphabet can be found
myletter = string.ascii_lowercase[element] # find associated letter in British alphabet, call it myletter
mystring = mystring + myletter #add my letter to mystring
return mystring # return the output string (mystring)
# To check whether the decryption algorithm is correct, the encrypted text **mylist** is decrypted and it should return the plain text "python is fun".
# In[35]:
mylist = [225, 576, 361, 49, 196, 169, ' ', 64, 324, ' ', 25, 400, 169]
decrypt(mylist)
# To double-check: If we encrypt a plain text and then decrypt it again, we should get the plain text:
# In[36]:
decrypt(encrypt("python is a programming language"))
# Now, the two functions `decrypt()` and `encrypt()` can be used to communicate secret messages. Make sure that the recipient is knowledgable about the algorithms that were applied in order to encrypt the secret message.