# 1. Introduction to Interactive Network Analysis and Visualization with Python

a GGNB short method course offered by Debsankha Manik and Jana Lassar at the MPI for Dynamics and Self-Organization in September 2014

## 1.1 What is Python?

• a general-purpose, high-level programming language.
• free and open source
• easy to learn and easy to read
• portable to any OS
• saves your time rather than CPU time
• used by many scientists -> large set of modules for data processing/analysis/visualization/...

### 1.1.1 How to use Python

• interactive interpreter (python, ipython)
• scripts (with any text editor, there is no standard IDE)
• jupyter (aka ipython notebook)

## 1.2 A short intro to jupyter

### 1.2.1 Markdown is cool

Markdown is a lightweight markup language.

#### 1.2.1.1 This is a heading

This is a list:

• apples
• oranges
• pears

A numbered list:

1. apples
2. oranges
3. pears

LaTeX equations

\begin{align} f(x \; | \; \mu, \sigma) = \frac{1}{\sigma\sqrt{2\pi} } \; e^{ -\frac{(x-\mu)^2}{2\sigma^2} } \end{align}

And this is a link where you find more about markdown syntax.

### 1.2.2 Use the TAB



In :

# print(  # Tab now should display the docstring

# Also woks:
print??



### 1.2.3 Write your own documentations



In :

def isprime(n):
"""Determine if a given number is a prime number"""
for x in range(2, n-1):
if n % x == 0:       #if n modulo x equals 0
return False     #then n is not a prime number
return True




In :

isprime??



### 1.2.4 Inline plots & interactive widgets



In :

%matplotlib inline
import pylab as pl
import numpy as np
from IPython.html.widgets import interact

def plot(frequency):
x = np.arange(1000)
pl.plot(x, np.sin(2*np.pi*frequency*x/1000))
interact(plot, frequency=(0,20,1))




*{stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:100000;}



# 2. Short tutorial on python

## 2.1 datatypes and operators

### 2.1.1 integer, float, strings



In :

#name = object
a = 17
b = 3.14
c = "bla"




In :

print(a)
print(b)
print(c)




17
3.14
bla
[2, 4.1, 'bla']




In :

print(type(a))
print(type(b))
print(type(c))




<class 'int'>
<class 'float'>
<class 'str'>
<class 'list'>



### 2.1.2 Assignments



In :

a = 17
a = "Used names can always be reassigned to other objects regardles of their data type!"
print(a)




Used names can always be reassigned to other objects regardles of their data type!



### 2.1.3 Conversion of datatypes:



In :

print(int(5.5))
print(float('5.23'))
print(str(12))
print(bool('True'))




5
5.23
12
True



### 2.1.4 Arithmetic and operators

• +
• -
• *
• /
• // (integer division)
• % (modulo operator)
• ** (power)


In :

print(x+y)
print("bla" + "bla")
print(x/y)
print(x*2)
print('bla'*3)
print(x/2)
print(x**3)                #the exponentiation operator
print(x%2)                 #the remainder operator




Out:

8.5



## 2.2 Conditions and control statements (if, while)

### 2.2.1 Comparison operators:

Operator True, if
a == b a equals b
a > b a is larger than b
a < b a is smaller than b
a >= b a is larger than b or equals b
a <= b a is smaller than b or equals b
a != b a and b are unequal
a is b a is the same object as b
a is not b a is not the same object as b


In :

print(x==y)
print(x==5)




False
False



### 2.2.2 is operator



In :

x = 3
y = 3
x is y




Out:

True



'==' compares values while 'is' compares identities



In :

x = 
y = 
x is y




Out:

False



But:



In :

x==y




Out:

True



Warning: do not check equality of two floats (finite precision!!)



In :

from math import sin, pi
sin(2*pi)==0




Out:

False



### 2.2.3 If ... else ...



In :

number_of_people = 6

if number_of_people < 5:
print('Not enough people to play this game.')
elif number_of_people < 10:
print('More would be better, but its sufficient.')
elif number_of_people < 20:
print('Perfect! Enjoy!')
elif number_of_people < 30:
print('Less would be better, but it will work somehow.')
else:
print('Sorry, but more than 30 is too much.')




More would be better, but its sufficient.



Conditional expressions:



In :

x = 12

#the long version:
if x%2==0:
message = "Even."
else:
message = "Odd."
print(message)

#the short version:
print( "Even." if x%2==0 else "Odd." )




Even.
Even.



## 2.3 While loops:



In :

value = 17

while value < 21:
print(value)
value = value + 1




17
18
19
20




In :

value = 17
max_value = 30

while True:
value = value + 1
if value > max_value:
break              #stop here and escape the while loop
elif value%2==0:
continue           #stop here and continue the while loop
print(value)




19
21
23
25
27
29



## 2.4 Sequences & for-loops

### 2.4.1 Sequences

Sequence mutable? data type
list yes arbitrary
tuple no arbitrary
string no Unicode symbols


In :

a = [1,2,3,4,5]   #a list
b = (1,2,3,4,5)   #a tuple
c = '12345'       #a string



Since lists and tuples can contain arbitrary data types, they can be 'nested':



In :

nested_list = [[1,2,3],[4,5,6],[7,8,9]]



All three sequence types (tuples, strings and lists) share much of their syntax and functionality.



In :

print(len(a),len(b),len(c))




5 5 5




In :

print( a + a )
print( b + b )
print( c + c )




[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
1234512345



single items are accessible by their index (starting from 0):



In :

print( a, b, c )




1 2 3



Negative indices are counted from the end (starting with -1)



In [ ]:

print ( a[-1], b[-3] )



A subset of items can be accessed by "slices".

Syntax: [I:J:K] means start from index I, stop at index J and take every K'th item. If I is omitted, start from the first item, if J is omitted, stop at the last item, and if K is omitted, take every item.



In :

print( a[1:4] ) #get items from 1 to 4
print( a[3:5] ) #get items from 3 to 5
print( a[:4] ) #get items from 0 to 4
print( a[3:] ) #get items from 3 to the end
print( a[::2] ) #get every second item




[2, 3, 4]
[4, 5]
[1, 2, 3, 4]
[4, 5]
[1, 3, 5]



The in-operator checks whether an item is in the sequence:



In :

print((2,3) in (1,2,3,4,5))
print('cde' in 'abcdefgh')




False
True



### 2.4.2 Specialities of lists

#### 2.4.2.1 In contrast to tuples and strings, lists are mutable. Items can be replaced, removed or added.



In :

a = [1,2,3,4]      #create list
a = 12          #replace item 2 by value 12
a.append(34)       #add value 34 to the end
a.extend([0,0,0])  #add several values to the end
a.pop()            #remove last item
a.insert(3, 'blub')#insert object before index 3
a.reverse()        #reverse list
print(a)




[0, 0, 34, 4, 'blub', 12, 2, 1]



#### 2.4.2.2 Warning: assignment to lists do not create a copy



In :

s = [1,2]
t = s

t.append(99)
print(s, t)




[1, 2, 99] [1, 2, 99]



### 2.4.3 Specialities of strings:



In :

# Strings can be enlosed by both single quote and double quote.
s='My home'
t="My home"
s==t




Out:

True



strings can contain quotation marks themselves. single/double quotes become important then:



In :

newstring="This is Mary's home"
anotherstring='And he said: "Let there be light"'

# And if you **really** need it, both single and double quotes:
huge_string="""This 'string' contains "both" types of quote"""



#### 2.4.3.1 Strings have some nice methods



In :

print(huge_string.upper())
print(huge_string.startswith('a'))
print(huge_string.find('contain'))
print(huge_string.split(' '))
print(huge_string.count('s'))




THIS 'STRING' CONTAINS "BOTH" TYPES OF QUOTE
False
14
['This', "'string'", 'contains', '"both"', 'types', 'of', 'quote']
4



### 2.4.4 For-loops

Remember how while loops were prone to infinite loop bugs? Python gives you a chance to avoid them in most cases:



In :

i = 1
while i < 50:
if i%7 == 0:
print(i)
i += 1




7
14
21
28
35
42
49




In :

for i in range(1, 50, 1):
if i%7 == 0:
print(i)




7
14
21
28
35
42
49



#### 2.4.4.1 Works on strings, tuples:



In :

count = 0

st = "home, sweet home"
for char in st:
if char == 'h':
count += 1
print("%c appears %d times in \"%s\""%('h', count, st))




h appears 2 times in "home, sweet home"



#### 2.4.4.2 Did I tell you about string formatting?



In :

print("%s is %d years old"%('John', 34))
print("{name} is {age} years old, {name} has 3 children".format(name='John', age=34))




John is 34 years old
John is 34 years old, John has 3 children



For-loops can not only iterate through sequences, but also through 'iterable' objects, like range().



In :

#Example: We want to sum up all numbers betwen 0 and 100.
#Instead of manually typing a list of all numbers, we can use range:
s = 0
for i in range(101):
s = s + i
print(s)




5050



### 2.4.5 List comprehensions: A short way to create a sequence.



In :

#long version: "for-loop"
li = []
for i in range(100):
li.append(i*2)

#short version:
li = [2*i for i in range(101)]

print(li)




[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200]



List comprehensions can be used as a filter:



In :

li = [1/i for i in range(101) if i != 0]
print(li)




[1.0, 0.5, 0.3333333333333333, 0.25, 0.2, 0.16666666666666666, 0.14285714285714285, 0.125, 0.1111111111111111, 0.1, 0.09090909090909091, 0.08333333333333333, 0.07692307692307693, 0.07142857142857142, 0.06666666666666667, 0.0625, 0.058823529411764705, 0.05555555555555555, 0.05263157894736842, 0.05, 0.047619047619047616, 0.045454545454545456, 0.043478260869565216, 0.041666666666666664, 0.04, 0.038461538461538464, 0.037037037037037035, 0.03571428571428571, 0.034482758620689655, 0.03333333333333333, 0.03225806451612903, 0.03125, 0.030303030303030304, 0.029411764705882353, 0.02857142857142857, 0.027777777777777776, 0.02702702702702703, 0.02631578947368421, 0.02564102564102564, 0.025, 0.024390243902439025, 0.023809523809523808, 0.023255813953488372, 0.022727272727272728, 0.022222222222222223, 0.021739130434782608, 0.02127659574468085, 0.020833333333333332, 0.02040816326530612, 0.02, 0.0196078431372549, 0.019230769230769232, 0.018867924528301886, 0.018518518518518517, 0.01818181818181818, 0.017857142857142856, 0.017543859649122806, 0.017241379310344827, 0.01694915254237288, 0.016666666666666666, 0.01639344262295082, 0.016129032258064516, 0.015873015873015872, 0.015625, 0.015384615384615385, 0.015151515151515152, 0.014925373134328358, 0.014705882352941176, 0.014492753623188406, 0.014285714285714285, 0.014084507042253521, 0.013888888888888888, 0.0136986301369863, 0.013513513513513514, 0.013333333333333334, 0.013157894736842105, 0.012987012987012988, 0.01282051282051282, 0.012658227848101266, 0.0125, 0.012345679012345678, 0.012195121951219513, 0.012048192771084338, 0.011904761904761904, 0.011764705882352941, 0.011627906976744186, 0.011494252873563218, 0.011363636363636364, 0.011235955056179775, 0.011111111111111112, 0.01098901098901099, 0.010869565217391304, 0.010752688172043012, 0.010638297872340425, 0.010526315789473684, 0.010416666666666666, 0.010309278350515464, 0.01020408163265306, 0.010101010101010102, 0.01]



Also to place a default value



In :

li = [1/i if i!=0 else None for i in range(101) ]
print(li)




[None, 1.0, 0.5, 0.3333333333333333, 0.25, 0.2, 0.16666666666666666, 0.14285714285714285, 0.125, 0.1111111111111111, 0.1, 0.09090909090909091, 0.08333333333333333, 0.07692307692307693, 0.07142857142857142, 0.06666666666666667, 0.0625, 0.058823529411764705, 0.05555555555555555, 0.05263157894736842, 0.05, 0.047619047619047616, 0.045454545454545456, 0.043478260869565216, 0.041666666666666664, 0.04, 0.038461538461538464, 0.037037037037037035, 0.03571428571428571, 0.034482758620689655, 0.03333333333333333, 0.03225806451612903, 0.03125, 0.030303030303030304, 0.029411764705882353, 0.02857142857142857, 0.027777777777777776, 0.02702702702702703, 0.02631578947368421, 0.02564102564102564, 0.025, 0.024390243902439025, 0.023809523809523808, 0.023255813953488372, 0.022727272727272728, 0.022222222222222223, 0.021739130434782608, 0.02127659574468085, 0.020833333333333332, 0.02040816326530612, 0.02, 0.0196078431372549, 0.019230769230769232, 0.018867924528301886, 0.018518518518518517, 0.01818181818181818, 0.017857142857142856, 0.017543859649122806, 0.017241379310344827, 0.01694915254237288, 0.016666666666666666, 0.01639344262295082, 0.016129032258064516, 0.015873015873015872, 0.015625, 0.015384615384615385, 0.015151515151515152, 0.014925373134328358, 0.014705882352941176, 0.014492753623188406, 0.014285714285714285, 0.014084507042253521, 0.013888888888888888, 0.0136986301369863, 0.013513513513513514, 0.013333333333333334, 0.013157894736842105, 0.012987012987012988, 0.01282051282051282, 0.012658227848101266, 0.0125, 0.012345679012345678, 0.012195121951219513, 0.012048192771084338, 0.011904761904761904, 0.011764705882352941, 0.011627906976744186, 0.011494252873563218, 0.011363636363636364, 0.011235955056179775, 0.011111111111111112, 0.01098901098901099, 0.010869565217391304, 0.010752688172043012, 0.010638297872340425, 0.010526315789473684, 0.010416666666666666, 0.010309278350515464, 0.01020408163265306, 0.010101010101010102, 0.01]



And to write insanely obtuse code and mess with your collaborators :D



In :

nested_list = [[1,2], , [4,5,6]]
flattened_list = [item for sublist in nested_list for item in sublist]
print(flattened_list)




[1, 2, 3, 4, 5, 6]



### 2.4.6 What are tuples for, you ask?

They are almost like lists, but immutable:



In :

l = [1,2,3,4,5]
t = [1,2,3,4,5]
#or, simply:
t = tuple(l)




In :

print(l[2:5])
print(t[2:5])

print(2 in l)
print(2 in t)




[3, 4, 5]
(3, 4, 5)
True
True




In :

l = 99
print(l)
t = 99
print(t)




[1, 2, 99, 4, 5]

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-143-203bd43c12cc> in <module>()
1 l = 99
2 print(l)
----> 3 t = 99
4 print(t)

TypeError: 'tuple' object does not support item assignment



### 2.4.7 dictionaries:

stores key->value pairs:



In :

d={'germany':'berlin', 'france':'paris', 'poland':'warsaw', 'denmark':'copenhagen'}




In :

d['germany']




Out:

'berlin'





In :

d['belgium']='brussels'



useful things you can do with dictionaries:



In :

d.keys()




Out:

dict_keys(['poland', 'germany', 'france', 'belgium', 'denmark'])




In :

d.values()




Out:

dict_values(['warsaw', 'berlin', 'paris', 'brussels', 'copenhagen'])



Any datatype can be used as a key in dictionary, so long as it is hashable

Thumbrule: integer, float, tuple, string are OK; list, other dictionaries are NOT OK



In :

mycrazydict = {(1,3,7):'odd!', [2,4,6]: 'even!'}




---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-150-da3a951b3156> in <module>()
----> 1 mycrazydict = {(1,3,7):'odd!', [2,4,6]: 'even!'}

TypeError: unhashable type: 'list'



## 2.5 functions:



In :

def triple(x):
return x*3




In :

triple(4)




Out:

12



Very important: writing documentation for your code



In :

def triple(x):
"""
This function accepts a number, returns 3 times that number.
"""
return x*3




In :

triple #Shift+tab shows documentation in ipython




Out:

<function __main__.triple>




In :

help(triple) #this also works




Help on function triple in module __main__:

triple(x)
This function accepts a number, returns 3 times that number.



when you pass any object to a function, any change done to the object will affect it globally:



In :

def modify(l):
l.append(23)
return 0




In :

l=[1,2,3]
modify(l)
print(l)




[1, 2, 3, 23]



but reassignments inside the function is not reflected globally



In [ ]:

def change(l):
l=[1,2,34]
return 0

mylist=[3,6]
change(mylist)

print mylist



## 2.6 Using modules

One of the most useful things about python is that there are tons of modules available, that do many of the tasks you might need to do:



In [ ]:

import math
math.sin(0.1)




In [ ]:

help(math)