Tools für Listen und Dicts

Aus einer Liste möchte man oft Elemente auswählen, die ein bestimmtes Kriterium erfüllen. Oder es soll eine Liste in eine andere Liste transformiert werden. Man könnte auch eine Kombination von Filtern und Transformieren anwenden. Hierzu stehen einerseits die Funktionen filter und map zu Verfügung. Andererseits kann man sog. list comprehensions verwenden.

Zunächst filter und map:

>>> liste1 = [ 1, 2, 3, 4, 5 ]
>>> import math
>>> map(math.sqrt, liste1) # apply math.sqrt to each element
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 2.23606797749979]
>>> map(lambda x: x**0.5, liste1) # same with lambda function
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 2.23606797749979]
>>> filter(lambda x: x % 2 == 0, liste1)
[2, 4]
>>> map(lambda x: x*10, filter(lambda x: x % 2 == 0, liste1))
[20, 40]


Aber man kann auch sog. list comprehensions verwenden:
>>> [element**0.5 for element in liste1]
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 2.23606797749979]
>>> [element for element in liste1 if element % 2 == 0]
[2, 4]
>>> [element*10 for element in liste1 if element % 2 == 0]
[20, 40]

List comprehensions haben folgenden allgemeine Form:

[ expr(element) for element in iterable if pred(element) ]
mit

Man kann auch mehrere Listen kombinieren:

[(x,y) for x in range(5) for y in range(5) ]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
und jeweils auch noch if Bedingung einbauen:
[(x,y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
damit erhält man eine Kombination aller geraden Zahlen von 0 bis 4 und aller ungeraden Zahlen von 0 bis 4.

Es entspricht einer doppelten for Schleife:

result = []
for x in range(5):
   if x % 2 == 0:
      for y in range(5):
         if y % 2 == 1:
            result.append((x,y))
Mit expliziten for Schleifen übersichtlicher und leichter verständlich, aber deutlich aufwendiger beim Schreiben und wesentlich langsamer bei der Ausführung.

Analog zu list comprehensions gibt es die dict comprehensions

sqdict = { i : i**2 for i in range(10) }
print ( sqdict )
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}


Listen zusammenführen mit zip
a=[ 1,2,3]
b=['a','b','c']
zip(a,b)
[(1, 'a'), (2, 'b'), (3, 'c')]
Ergibt kombinierte Liste von tuples

Praktische Anwendung – Skalarprodukt:

a = [ 0.3, 1.8, -2.2 ] 
b = [ -2.5, 3.8, 0.4]
sp = sum([ x*y for x,y in zip(a,b)])

Kann leicht erweitert werden um 2 Listen in ein dict zu kombinieren:

d = { x[1] : x[0] for x in zip(a,b) }
print(d)
{'a': 1, 'c': 3, 'b': 2}

# Oder direkter ...
d = dict(zip(b,a))


defaultdict

Im collections module gibt es nützliche Hilfs-Klassen zur Arbeit mit Listen und Dicts. Hier Beispiel zum Bestimmen der Häufigkeit von Wörtern in Text-Datei:


#
# python3 version
import urllib.request
# open Kant's Text
f=urllib.request.urlopen("https://goo.gl/rGqW4k")
# split into words
words=[]
for line in f: # iteriere ueber alle Zeilen
    line=line.decode("utf-8") # Decoding the binary data to text.
    words += line.split() # packe Words in list

print ("Zahl der Woerter:",len(words))    
# or more direct w/ double list-comprehension:
# words=[ word for line in f for word in line.split() ]
#
# count words v1
word_counts = {}
for word in words:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1

print ("V1:", word_counts["Vernunft"])
        
# Umstaendlich ...
#
# count words v2
word_counts = {}
for word in words:
    try:
        word_counts[word] += 1
    except:
        word_counts[word] = 1

print ("V2:", word_counts["Vernunft"])
# Auch umstaendlich ...
#
# count words v3
from collections import defaultdict
word_counts = defaultdict(int)
for word in words:
    word_counts[word] += 1

print ("V3:", word_counts["Vernunft"])
    
# defaultdict(int) initialisiert Eintraege beim Ansprechen automatisch auf int() = 0
#
# oder noch einfacher ...
from collections import Counter
word_counts=Counter(words)
#
# Counter liefert eine Art dict zurueck mit das als Wert die Haeufigkeit enthaelt: 
print ("V4:", word_counts["Vernunft"])

# und weitere Methoden ...
print (word_counts.most_common(10)) # die 10 haeufigsten ...



enumerate

Ein weiteres gängiges Problem ist, dass man beim Iterieren über eine Liste sowohl das jeweilige Element als auch den Index haben will.


#
# find largest element in list,
# both index and value of this element
#
nums = [ 1,5,8,3,7,6,15,11 ] # test list with some numbers

maxv=nums[0]
imax = 0

# classical method-1
for i in range(len(nums)): # index loop
    if nums[i]>maxv:
        maxv = nums[i]
        imax = i

print ("1:", maxv, imax )        
#
# classical method-2
maxv=nums[0]
imax = 0
i = 0
for x in nums: # keep separate counter/index
    if x>maxv:
        maxv = x
        imax = i
    i += 1
print ("2:", maxv, imax )

#
# python way: better use enumerate
#
maxv=nums[0]
imax = 0
for i,x in enumerate(nums): # provides index,value 
    if x>maxv:
        maxv = x
        imax = i
    ic += 1
print ("3:", maxv, imax )        


enumerate liefert Index und Element zusammen.