- Python Data Science Handbook
*by Jake VanderPlas (Code released under the MIT License)* - ➡️ A Whirlwind Tour of Python
*by Jake VanderPlas released under the "No Rights Reserved" CC0 license (O’Reilly). Copyright 2016 O’Reilly Media, Inc., 978-1-491-96465-1***(What the present tutorial is mostly based on)** - Python for Data Analysis
*by Wes McKinney (Code released under the MIT License)* - Python 3.8 Documentation

**Obligatory Wikipedia excerpt:**

Python is an interpreted, high-level and general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability with its notable use of significant whitespace. Its language constructs and object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects.

Python is dynamically typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly, procedural), object-oriented, and functional programming. Python is often described as a "batteries included" language due to its comprehensive standard library.

An important goal of Python's developers is keeping it fun to use. This is reflected in the language's name—a tribute to the British comedy group Monty Python—and in occasionally playful approaches to tutorials and reference materials, such as examples that refer to spam and eggs (from a famous Monty Python sketch) instead of the standard foo and bar.

A common neologism in the Python community is pythonic, which can have a wide range of meanings related to program style. To say that code is pythonic is to say that it uses Python idioms well, that it is natural or shows fluency in the language, that it conforms with Python's minimalist philosophy and emphasis on readability. In contrast, code that is difficult to understand or reads like a rough transcription from another programming language is called unpythonic.

The language's core philosophy is summarized in the documentThe Zen of Python(PEP 20) [...]

In [ ]:

```
#MAKE PAGE WIDER
from IPython.display import HTML
display(HTML("<style>.container { width:80% !important; }</style>"))
```

In [ ]:

```
import this
```

In [ ]:

```
x = 1 # x is an integer
x = 1. # x is now a float
x = 'Hello, world!' # x is now a string
x = [1, 2, 3, 4] # x is now a list
```

The **C struct** behind the curtain:

```
struct _longobject {
long ob_refcnt;
PyTypeObject *ob_type;
size_t ob_size;
long ob_digit[1];
};
```

In [ ]:

```
x=1
type(x)
```

In [ ]:

```
x=1.
type(x)
```

In [ ]:

```
x = 'Hello, World!'
type(x)
```

In [ ]:

```
x = [1,2,3,4]
type(x)
```

In [ ]:

```
x = [1, 2, 3]
y = x
```

In [ ]:

```
x.append(4) # append 4 to x
print(y) # y's list is modified as well!
```

In [ ]:

```
x = 'something else'
print(y) # y is unchanged
```

Python implements seven basic binary arithmetic operators:

Operator | Name | Description |
---|---|---|

`a + b` |
Addition | Sum of `a` and `b` |

`a - b` |
Subtraction | Difference of `a` and `b` |

`a * b` |
Multiplication | Product of `a` and `b` |

`a / b` |
True division | Quotient of `a` and `b` |

`a // b` |
Floor division | Quotient of `a` and `b` , removing fractional parts |

`a % b` |
Modulus | Integer remainder after division of `a` by `b` |

`a ** b` |
Exponentiation | `a` raised to the power of `b` |

When working with Boolean values, Python provides operators to combine the values using the standard concepts of "and", "or", and "not". Predictably, these operators are expressed using the words `and`

, `or`

, and `not`

:

In [ ]:

```
a=5
b=2
c=3
```

In [ ]:

```
a==5 and b==2
```

In [ ]:

```
a==5 and not c==5
```

In [ ]:

```
(a==5 or not b==6) and not c==3
```

In [ ]:

```
a==5 or not b==6 and not c==3
```

In [ ]:

```
(a==5 or not b==6) and not c==3
```

Python also contains prose-like operators to check for identity and membership. They are the following:

Operator | Description |
---|---|

`a is b` |
True if `a` and `b` are identical objects |

`a is not b` |
True if `a` and `b` are not identical objects |

`a in b` |
True if `a` is a member of `b` |

`a not in b` |
True if `a` is not a member of `b` |

In [ ]:

```
a = [1,2,3]
b = a
b is a
```

In [ ]:

```
a = [1,2,3]
b = [1,2,3]
b is a
```

In [ ]:

```
1 in [1,2,3]
```

In [ ]:

```
'foo' in [1,2,3]
```

In [ ]:

```
5 not in [1,2,3]
```

Type | Example | Description |
---|---|---|

`int` |
`x = 1` |
integers (i.e., whole numbers) |

`float` |
`x = 1.0` |
floating-point numbers (i.e., real numbers) |

`complex` |
`x = 1 + 2j` |
Complex numbers (i.e., numbers with real and imaginary part) |

`bool` |
`x = True` |
Boolean: True/False values |

`str` |
`x = 'abc'` |
String: characters or text |

`NoneType` |
`x = None` |
Special object indicating nulls |

In [ ]:

```
```

Type Name | Example | Description |
---|---|---|

`list` |
`[1, 2, 3]` |
Ordered collection |

`tuple` |
`(1, 2, 3)` |
Immutable ordered collection |

`dict` |
`{'a':1, 'b':2, 'c':3}` |
Unordered (key,value) mapping |

`set` |
`{1, 2, 3}` |
Unordered collection of unique values |

In [ ]:

```
L = [2, 3, 5, 7]
```

In [ ]:

```
# Length of a list
len(L)
```

In [ ]:

```
# Append a value to the end
L.append(11)
L
```

In [ ]:

```
# Addition concatenates lists
L + [13, 17, 19]
```

In [ ]:

```
# sort() method sorts in-place
L = [2, 5, 1, 6, 3, 4]
L.sort()
L
```

In [ ]:

```
L = [1, 'two', 3.14, [0, 3, 5]]
```

In [ ]:

```
L = [2, 3, 5, 7, 11]
```

In [ ]:

```
L[0]
```

In [ ]:

```
L[1]
```

In [ ]:

```
L[-1]
```

In [ ]:

```
L[-2]
```

In [ ]:

```
L[0:3]
```

In [ ]:

```
L[:3]
```

In [ ]:

```
L[3:]
```

In [ ]:

```
L[:3]+L[3:]
```

In [ ]:

```
L.reverse() #This happen
L
```

In [ ]:

```
l = [7, 1, 2]
r = [9, 6, 8]
l + r*2
```

In [ ]:

```
print(l)
l.sort()
print(l)
```

In [ ]:

```
print(r)
sorted(r)
```

In [ ]:

```
print(r)
```

In [ ]:

```
sorted?
```

In [ ]:

```
dir(l)
```

In [ ]:

```
numbers = {'one':1, 'two':2, 'three':3}
```

In [ ]:

```
numbers['two']
```

In [ ]:

```
numbers['ninety'] = 90
```

In [ ]:

```
k = numbers.keys()
k
```

In [ ]:

```
v = numbers.values()
v
```

In [ ]:

```
dir(numbers)
```

In [ ]:

```
for thing in numbers.keys():
print(thing)
```

In [ ]:

```
primes = {2, 3, 5, 7}
odds = {1, 3, 5, 7, 9}
```

In [ ]:

```
# union: items appearing in either
primes | odds # with an operator
primes.union(odds) # equivalently with a method
```

In [ ]:

```
# intersection: items appearing in both
primes & odds # with an operator
primes.intersection(odds) # equivalently with a method
```

In [ ]:

```
# difference: items in primes but not in odds
primes - odds # with an operator
primes.difference(odds) # equivalently with a method
```

In [ ]:

```
# symmetric difference: items appearing in only one set
primes ^ odds # with an operator
primes.symmetric_difference(odds) # equivalently with a method
```

In [ ]:

```
numbers = {'one', 'four', 'twenty'}
```

In [ ]:

```
k & numbers
```

In [ ]:

```
k | numbers
```

In [ ]:

```
k - numbers
```

In [ ]:

```
k ^ numbers
```

In [ ]:

```
x = -15
if x == 0:
print(x, "is zero")
elif x > 0:
print(x, "is positive")
elif x < 0:
print(x, "is negative")
else:
print(x, "is unlike anything I've ever seen...")
```

Introduced in PEP 308, and often referred to as a ternary operator:

```
x = x_if_true if condition else x_if_false
```

which is the succint version of:

```
if condition:
x = x_if_true
else:
x = x_if_false
```

In [ ]:

```
sun_shining = False
x = 35 if sun_shining else -4
print(x)
```

In [ ]:

```
def sun_shining(sun_shining=True):
return 35 if sun_shining else -4
```

In [ ]:

```
sun_shining(True)
```

In [ ]:

```
sun_shining(False)
```

In [ ]:

```
for N in [2, 3, 5, 7]:
print(N, end=' ') # print all on same line
```

In [ ]:

```
for i in range(10):
print(i, end=' ')
```

In [ ]:

```
for n in range(20):
# if the remainder of n / 2 is 0, skip the rest of the loop
if n % 2 == 0:
continue
print(n, end=' ')
```

In [ ]:

```
i = 0
while i < 10:
print(i, end=' ')
i += 1
```

In [ ]:

```
a, b = 0, 1
amax = 100
L = []
while True:
(a, b) = (b, a + b)
if a > amax:
break
L.append(a)
print(L)
```

In [ ]:

```
def fibonacci(N, a=0, b=1):
L = []
while len(L) < N:
a, b = b, a + b
L.append(a)
return L
```

In [ ]:

```
fibonacci(10)
```

In [ ]:

```
fibonacci(10, b=3, a=1)
```

In [ ]:

```
def catch_all(*args, **kwargs):
print("args =", args)
print("kwargs = ", kwargs)
```

In [ ]:

```
catch_all(1, 2, 3, a=4, b=5)
```

In [ ]:

```
catch_all('a', keyword=2)
```

In [ ]:

```
inputs = (1, 2, 3)
keywords = {'pi': 3.14}
catch_all(*inputs, **keywords)
```

In [ ]:

```
add = lambda x, y: x + y
add(1, 2)
```

In [ ]:

```
things = ["cat", "apple", "boat"]
sorted(things) # alphabetically, upper case first
```

In [ ]:

```
sorted(things, key=lambda x: len(x))
```

In [ ]:

```
import math
math.log2(1024)
```

In [ ]:

```
math.log(math.e)
```

In [ ]:

```
math.cos(math.pi)
```

In [ ]:

```
import random as rnd ## you can re-name imported modules
rnd.randint(1, 6) ## Here, the end points are both included
```

In [ ]:

```
things = ['cat', 'apple', 'boat']
rnd.choice(things)
```

In [ ]:

```
## Modules can have sub-modules
import urllib.request as rq
response = rq.urlopen("http://en.wikipedia.org/wiki/Python")
print(response.read(151).decode('utf8'))
```

In [ ]:

```
import itertools
perms = itertools.permutations([1, 2, 3], r=2)
# r-length tuples, all possible orderings, no repeated elements
# default r: length of the iterable
for p in perms:
print(p)
```

In [ ]:

```
combs = itertools.combinations([1, 2, 3], r=2)
# r-length tuples, in sorted order, no repeated elements
print(list(combs))
```

In [ ]:

```
[n for n in range(11)]
```

In [ ]:

```
perms = itertools.permutations([1, 2, 3], r=2)
[p for p in perms]
```

In [ ]:

```
perms = itertools.permutations([1, 2, 3], r=2)
[thing for thing in perms]
```

In [ ]:

```
[n**2 for n in range(11)]
```

In [ ]:

```
[n for n in range(11) if n%3] # n%3 is shorthand for n%3!=0
```

In [ ]:

```
[n if n%2 else -n for n in range(11) if n%3]
```

In [ ]:

```
[(n,n) if n%2 else (-n,9) for n in range(11) if n%3]
```

In [ ]:

```
list_of_tuples = [(n,n) if n%2 else (-n,9) for n in range(11) if n%3]
{a:b for a,b in list_of_tuples}
```

In [ ]:

```
numbers
```

In [ ]:

```
{v:k for k,v in numbers.items()}
```

In [ ]:

```
{a%4 for a in range(1000)}
```

Lambdas can be used to create anonymous functions.

In [ ]:

```
sum_lambda = lambda x, y: x+y
sum_lambda(3,2)
```

Supports `map`

, `filter`

, and `reduce`

functions.
All three can be replaced with List Comprehensions or loops, but often provide a more elegant solution.
Keep in mind that they return a generator by default, so we have to cast them back into a list to actually apply the transformation.

In [ ]:

```
list(map(lambda x: x+1, [1,2,3,4,5]))
```

In [ ]:

```
list(filter(lambda x: x % 2 == 0, [1,2,3,4,5]))
```

In [ ]:

```
# in Python3, reduce() isn't a built-in function anymore
# and has to be imported from the functools module
from functools import reduce
reduce(lambda x, y: x+y, [1,2,3,4,5])
```