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 [4]:
# print(  # Tab now should display the docstring

# Also woks:
print??

1.2.3 Write your own documentations


In [5]:
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 [6]:
isprime??

1.2.4 Inline plots & interactive widgets


In [7]:
%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))


2. Short tutorial on python

2.1 datatypes and operators

2.1.1 integer, float, strings


In [8]:
#name = object
a = 17
b = 3.14
c = "bla"

In [9]:
print(a)
print(b)
print(c)


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

In [10]:
print(type(a))
print(type(b))
print(type(c))


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

2.1.2 Assignments


In [11]:
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 [12]:
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 [217]:
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[217]:
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 [61]:
print(x==y)
print(x==5)


False
False

2.2.2 is operator


In [15]:
x = 3
y = 3
x is y


Out[15]:
True

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


In [16]:
x = [1]
y = [1]
x is y


Out[16]:
False

But:


In [64]:
x==y


Out[64]:
True

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


In [70]:
from math import sin, pi
sin(2*pi)==0


Out[70]:
False

2.2.3 If ... else ...


In [53]:
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 [55]:
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 [71]:
value = 17

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


17
18
19
20

In [72]:
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.3.1 Warning: Make sure that the condition gets True after a finite number of steps!

2.4 Sequences & for-loops

2.4.1 Sequences

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

In [74]:
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 [75]:
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 [77]:
print(len(a),len(b),len(c))


5 5 5

In [78]:
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 [80]:
print( a[0], b[1], c[2] )


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 [82]:
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 [83]:
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 [86]:
a = [1,2,3,4]      #create list
a[2] = 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 [93]:
s = [1,2]
t = s

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


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

2.4.3 Specialities of strings:


In [87]:
# Strings can be enlosed by both single quote and double quote.
s='My home'
t="My home"
s==t


Out[87]:
True

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


In [94]:
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 [88]:
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 [96]:
i = 1
while i < 50:
    if i%7 == 0:
        print(i)
    i += 1


7
14
21
28
35
42
49

In [97]:
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 [103]:
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 [108]:
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 [109]:
#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 [110]:
#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 [119]:
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 [123]:
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 [136]:
nested_list = [[1,2], [3], [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 [138]:
l = [1,2,3,4,5]
t = [1,2,3,4,5]
#or, simply:
t = tuple(l)

In [141]:
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 [143]:
l[2] = 99
print(l)
t[2] = 99
print(t)


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

TypeError: 'tuple' object does not support item assignment

What's the use of immutability? glad you asked

2.4.7 dictionaries:

stores key->value pairs:


In [144]:
d={'germany':'berlin', 'france':'paris', 'poland':'warsaw', 'denmark':'copenhagen'}

In [145]:
d['germany']


Out[145]:
'berlin'

You can add keys anytime:


In [146]:
d['belgium']='brussels'

useful things you can do with dictionaries:


In [147]:
d.keys()


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

In [148]:
d.values()


Out[148]:
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 [150]:
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 [151]:
def triple(x):
    return x*3

In [152]:
triple(4)


Out[152]:
12

Very important: writing documentation for your code


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

In [154]:
triple #Shift+tab shows documentation in ipython


Out[154]:
<function __main__.triple>

In [155]:
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 [156]:
def modify(l):
    l.append(23)
    return 0

In [158]:
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)