Dictionary "dynamisch" erweitern - 2-d Array

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
uwe76
User
Beiträge: 4
Registriert: Mittwoch 4. November 2009, 12:44

Hallo Leute,

ich hoffe ihr könnt mir bei meinem blöden Dictionary Problem weiterhelfen, irgendwie steh ich auf dem Schlauch...
Mit folgendem Beispielcode will ich eigentlich nichts weiter erreichen, als ein Dictionary zu erhalten, dessen Einträge ich über z.B. nodaldata_dict[2]['u2'] ansprechen kann. Das Dictionary soll dabei selbstständig für den zweiten Schlüssel anwachsen sprich, num_nodes ist bekannt und varnames ist unbekannt (falls es nicht anders geht, könnte ich zur Not auch varnames in einer Vorher-Schleife auslesen). Die Verschachtelung der beiden Schleifen ist leider nicht anders möglich und quasi vorgegeben...

Code: Alles auswählen

import time

varnames=['u1','u2','nt121', 'n11']
num_nodes=3
nodaldata_dict = {}.fromkeys([x for x in range(num_nodes)], {})

for name in varnames:
  for i in range(num_nodes):
    if not name == "nt121":
      print 'node:', i, name, time.time()
      nodaldata_dict[i][name] = time.time()
      time.sleep(0.1)

for key in nodaldata_dict.keys():
  print '->', key, nodaldata_dict[key]
Als Ergebnis zu oberen Zeilen erhalte ich für alle Erst-Schlüssel immer die Werte des letzten Schleifendurchlaufs:

Code: Alles auswählen

node: 0 u1 1257335371.31
node: 1 u1 1257335371.41
node: 2 u1 1257335371.51
node: 0 u2 1257335371.61
node: 1 u2 1257335371.71
node: 2 u2 1257335371.81
node: 0 nt11 1257335371.91
node: 1 nt11 1257335372.01
node: 2 nt11 1257335372.11
-> 0 {'nt11': 1257335372.107372, 'u1': 1257335371.5055981, 'u2': 1257335371.8064871}
-> 1 {'nt11': 1257335372.107372, 'u1': 1257335371.5055981, 'u2': 1257335371.8064871}
-> 2 {'nt11': 1257335372.107372, 'u1': 1257335371.5055981, 'u2': 1257335371.8064871}
Als Ergebnis hätte ich aber gern:

Code: Alles auswählen

-> 0 {'nt11': 1257335371.91, 'u1': 1257335371.31, 'u2': 1257335371.61}
-> 1 {'nt11': 1257335372.01, 'u1': 1257335371.41, 'u2': 1257335371.71}
-> 2 {'nt11': 1257335372.11, 'u1': 1257335371.51, 'u2': 1257335371.81}
Könnt Ihr mir vielleicht auf die Sprünge helfen, warum die vorherigen Einträge immer wieder überschrieben werden? Hab ich da irgendwo einen Denkfehler in den Schleifen eingebaut bzw. da es ja nicht funktionert, WO liegt mein Denkfehler?

Viele Grüße
Uwe
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

uwe76 hat geschrieben:

Code: Alles auswählen

nodaldata_dict = {}.fromkeys([x for x in range(num_nodes)], {})
Hier sagst du: "mappe alle x auf dieses eine dict {}.
Was du möchtest ist aber: "mappe alle x auf je ein eigenes dict.

Das geht zB. so:

Code: Alles auswählen

nodaldata_dict = dict((x,{}) for x in range(num_nodes))
Was du aber tatsächlich verwenden willst ist collections.defaultdict:

Code: Alles auswählen

import collections
...
nodaldata_dict = collections.defaultdict(dict)
Gruß,
Mick.
[edit]han grad gesehen, dass du gar kein dict brauchst, weil die Keys darin ja aufeinanderfolgende ints sind. Also lieber eine Liste verwenden:

Code: Alles auswählen

import time

varnames = ['u1','u2','nt121', 'n11']
node_nums = xrange(3)
nodaldata = [{} for x in node_nums]

for name in varnames:
  for i in node_nums:
    if not name == "nt121":
      print 'node:', i, name, time.time()
      nodaldata[i][name] = time.time()
      time.sleep(0.1)

for i in node_nums:
  print '->', i, nodaldata[i]
Außerdem: statt range() lieber xrange() verwenden, wenn es nur zum iterieren gebraucht wird. Wenn an mehreren Stellen im Code über den selben Bereich iteriert wird, dann sollte man das kenntlich machen, indem man diesem Bereich einen Namen gibt, zB. node_nums.

Und Typbezeichnungen in Variablennamen sind schlecht. Wenn ich nodaldata_dict nicht in nodaldata umbenannt hätte dann hätte ich jetzt eine Liste die ..._dict heißt, was für jeden Leser (mich selbst eingeschlossen) ziemlich verwirrend wäre.
[/edit]
Zuletzt geändert von pillmuncher am Mittwoch 4. November 2009, 15:05, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Wie wärs mit einer eleganteren Schleife?

Code: Alles auswählen

from itertools import product
...

for name, i in product(varnames, node_nums):
	...
Oder geht das wirklich nicht?
uwe76
User
Beiträge: 4
Registriert: Mittwoch 4. November 2009, 12:44

Mick! Vielen Dank!

Du hast natürlich recht, es funktioniert!

Code: Alles auswählen

import time
import collections

varnames=['u1','u2','nt121', 'n11']
num_nodes=3
nodaldata_dict = collections.defaultdict(dict)

for name in varnames:
  for i in range(num_nodes):
    if not name == "nt121":
      print 'node:', i, name, time.time()
      key = str(i)
      nodaldata_dict[key][name] = { name : time.time()}
      time.sleep(0.1)

for key in nodaldata_dict.keys():
  print '--->', key, nodaldata_dict[key]

Code: Alles auswählen

---> 1 {'n11': {'n11': 1257343822.0042391}, 'u1': {'u1': 1257343821.4026909}, 'u2': {'u2': 1257343821.7034869}}
---> 0 {'n11': {'n11': 1257343821.9039681}, 'u1': {'u1': 1257343821.3039999}, 'u2': {'u2': 1257343821.603224}}
---> 2 {'n11': {'n11': 1257343822.1045711}, 'u1': {'u1': 1257343821.502949}, 'u2': {'u2': 1257343821.8037801}}
"collections" kannte ich noch gar nicht...

@ice2k3: Hmmm... könnte sein, dass das für meine Schleifen auch funktioniert; werde ich mal testen - obiger Beispielcode ist nur ein kleiner Extrakt der eigentlichen Anwendung. Danke für den Tip!

[edit]
@Mick: Hast schon wieder recht, die xrange Variante reicht mir auch :)
@ise2k3: Hab gerade gesehen, dass product erst ab Python v2.6 funktioniert; hab leider noch Python 2.5.3 - dann muss es halt unelegant gehen ;)
[/edit]

Nochmals vielen Dank,
herzliche Grüße
Uwe
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

ice2k3 hat geschrieben:Wie wärs mit einer eleganteren Schleife?
Genau. Und dann noch ohne if not name == ... im Body der Schleife:

Code: Alles auswählen

def vns_without(*names):
    return (name for name in varnames if name not in names)

for name, i in itertools.product(vns_without('nt121'), node_nums):
   print 'node:', i, name, time.time()
  ...
Oder pointless^H^H^H^Hfree:

Code: Alles auswählen

import functools
...
def without(*xs):
    return functools.partial(itertools.ifilterfalse, xs.__contains__)

for name, i in itertools.product(without('nt121')(varnames), node_nums):
    ...
Naja, nicht ganz pointfree, aber dafür um so unübersichtlicher...
:wink:
Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Dann wenigstens als Generator-Expression. ;)

Code: Alles auswählen

for i, name in ((i,n) for i in node_nums for n in varnames)
Oder natürlich auch gleich so...

Code: Alles auswählen

for i, name in ((i,n) for i in node_nums for n in varnames if not n == "nt121"):
@pillmuncher: Das ist IMHO auch eleganter wie deine "without" Lösungen ;)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

ice2k3 hat geschrieben: @pillmuncher: Das ist IMHO auch eleganter wie deine "without" Lösungen ;)
Glücklicherweise war sie nicht ernst gemeint.
;)
Gruß,
Mick.

[edit]

Code: Alles auswählen

def all_failing(predicate):  # pointfree, at last:
    return functools.partial(itertools.ifilterfalse, predicate)

def without(*names):
    return all_failing(names.__contains__)

stripped = without('nt121', 'hose', 'hemd')

for name, i in itertools.product(stripped(varnames), node_nums):
    ...
Vielleicht hab ich einfach zuviel Zeit...

[/edit]
In specifications, Murphy's Law supersedes Ohm's.
uwe76
User
Beiträge: 4
Registriert: Mittwoch 4. November 2009, 12:44

Ok Leut, Euer Kung Fu ist besser als meins - wobei das if-Statement lediglich verdeutlichen sollte, dass variables eigentlich unbekannt ist und so natürlich nicht in meinem Code steht ;)
Aber wirklich tolle Ansätze, die Ihr da zum Besten gebt; kann man 'ne Menge von lernen.

Viele Grüße
Uwe
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@uwe76: Eigentlich haben wir nur rumgekaspert... Der ernstere Hintergrund auf den ich hinweisen wollte, war der, dass die if-Abfrage im inneren der Schleife (|varnames| * num_nodes) mal auf dieselbe Bedingung testest, deren Ergebnis sich aber nur in Abhängigkeit von varnames ändert, d.h., sie wird (|varnames| * (num_nodes - 1)) mal umsonst ausgeführt. Bei |varnames| = 4 und nun_nodes = 3 ist das zwar völlig wurst, aber bei erheblich größeren Zahlen könnte es recht langsam werden. Mit Generatoren/Iteratoren kann man das mehr oder weniger elegant on the fly erledigen, indem man einfach die Werte von varnames schon früh filtert, entweder mittels Funktionen aus itertools, wie in meinen Beispielen, oder mittels einer generator expression, wie in ice2k3s. Seine, da geb ich ihm natürlich recht, sind deutlich pythonischer.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
Antworten