Listenelemente umgruppieren

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
Benutzeravatar
Mawilo
User
Beiträge: 446
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Freitag 31. Oktober 2008, 08:01

Hallo,

ich habe ein etwas kniffliges Problem. Ich habe folgendes Dictionary:

Code: Alles auswählen

input = {
        1:[['aaa', 124], ['bbb', 1],['ccc', 60], ['ddd', 2]],
        2:[['eee', 62], ['fff', 124]],
        3:[['ggg', 2], ['hhh', 68], ['iii', 124]],
        4:[['kkk', 1], ['lll', 2], ['mmm', 124]],
        }

Die Buchstaben stehen für verschiedene Werte. Interessant ist die Zahl. In dieser Zahl sind
Gültigkeitstage codiert. Dabei gibt es für jede mögliche Kombination der Wochentage einen Code.
Alle Codes können hier angesehen werden. In dieser csv-Tabelle ist die erste Spalte der Code und
die zweite Spalte die Gültigkeitsbeschreibung. Die dritte Spalte wird hierfür nicht benötigt.

Später werden die einzelnen Elemente grafisch ausgegeben. Alle Elemente mit dem gleichen
Gültigkeitscode werden dabei in eine Zeile geschrieben. Also müssen die einzelnen Elemente so
gruppiert werden, dass jeder Wochentag nur einmal ausgegeben wird. Die Reihenfolge sollte dabei
natürlich mit Montag beginnen und mit Sonntag enden.

Im Beispiel input hat vom Key 1 das erste Element eine Gültigkeit von Montag bis Freitag, das zweite
Element ist am Sonntag, das dritte Element Dienstag bis Freitag und das vierte Element
ist am Samstag gültig. Benötigt wird also eine Gültigkeitsangabe für jeweils Montag, Dienstag bis Freitag,
Samstag und eine für Sonntag. Es müssen immer alle Tage angegeben werden. Fehlt im Input ein Tag,
so wird ein leeres Element mit der entsprechenden Gültigkeit erstellt.


Für das Beispiel müsste der Output so aussehen:

Code: Alles auswählen

output = {
        1:[['aaa', 64], ['aaa', 60], ['ccc', 60], ['ddd', 2], ['bbb', 1]],
        2:[['fff', 64], ['fff', 60], ['eee', 60], ['eee', 2], ['', 1]],
        3:[['hhh', 64], ['iii', 64], ['iii', 56], ['hhh', 4], ['iii', 4], ['ggg', 2], ['', 1]],
        4:[['mmm', 124],['lll', 2],['kkk', 1]],
        }
Was mir fehlt ist eine Möglichkeit, die Elemente in der beschriebenen Weise zu gruppieren.
Ich hoffe, ich konnte mein Problem einigermaßen nachvollziebar erklären und mir kann jemand einen
Lösungsansatz geben.


Grüße
Mawilo
Zuletzt geändert von Mawilo am Freitag 31. Oktober 2008, 10:45, insgesamt 1-mal geändert.
BlackJack

Freitag 31. Oktober 2008, 09:27

Die dritte Spalte ist IMHO das *Wichtigste*, denn dort ist die Beschreibung, im Gegensatz zur ersten Spalte, so kodiert, dass man im Programm damit etwas anfangen kann. Da sind die Tage nämlich als Bits kodiert. Sonntag = 64, Montag = 32, Dienstag = 16, …, Samstag = 1.

PS: Argh! Muss wohl noch 'nen Kaffee kippen. In der ersten Spalte sind die Tage ja auch in den Bits kodiert, nur in "europäischer" Reihenfolge, also Montag = 64, Dienstag = 32, …, Sonntag = 1.
Zuletzt geändert von BlackJack am Freitag 31. Oktober 2008, 09:34, insgesamt 1-mal geändert.
Benutzeravatar
Mawilo
User
Beiträge: 446
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Freitag 31. Oktober 2008, 09:33

Nicht ganz, in der ersten Spalte sind die Bits kodiert (So=1, Sa=2, Fr=4, ...)
Die dritte Spalte stellt nur den jeweiligen Folgetag in der Gültigkeit dar.
BlackJack

Freitag 31. Oktober 2008, 10:21

Die Sortierung von Deinem `output` verstehe ich nicht!? Ich dachte es sollte so sortiert sein, dass Montag als erstes in der Liste steht. Im letzten Beispiel ist das also genau umgekehrt!?

Ansonsten:

Code: Alles auswählen

from itertools import groupby
from operator import itemgetter, or_


def f(xs):
    tmp_a = [[] for dummy in xrange(7)]
    for item, coded_days in xs:
        for i in xrange(7):
            day = coded_days & 2**(6 - i)
            if day:
                tmp_a[i].append(item)
    result = list()
    tmp_b = ((2**(6 - i), x) for i, x in enumerate(tmp_a))
    for ys, group in groupby(tmp_b, itemgetter(1)):
        if ys == []:
            ys = ['']
        code = reduce(or_, map(itemgetter(0), group))
        result.extend((y, code) for y in ys)
    return result


def main():
    data = [[['aaa', 124], ['bbb', 1], ['ccc', 60], ['ddd', 2]],
            [['eee', 62], ['fff', 124]],
            [['ggg', 2], ['hhh', 68], ['iii', 124]],
            [['kkk', 1], ['lll', 2], ['mmm', 124]]]
    for xs in data:
        print f(xs)
Ausgabe:

Code: Alles auswählen

[('aaa', 64), ('aaa', 60), ('ccc', 60), ('ddd', 2), ('bbb', 1)]
[('fff', 64), ('eee', 60), ('fff', 60), ('eee', 2), ('', 1)]
[('hhh', 64), ('iii', 64), ('iii', 56), ('hhh', 4), ('iii', 4), ('ggg', 2), ('', 1)]
[('mmm', 124), ('lll', 2), ('kkk', 1)]
Wenn man die magischen Bits und die Elemente der Listen in Objekte mit passenden Namen und entsprechenden Methoden versieht, bekommt man *vielleicht* auch Quelltext hin, den man nach einem halben Jahr auch noch versteht. ;-)
Benutzeravatar
Mawilo
User
Beiträge: 446
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Freitag 31. Oktober 2008, 10:46

Hast natürlich Recht - ich habe den gewünschten Output im ersten Beitrag berichtigt.
Wenn man die magischen Bits und die Elemente der Listen in Objekte mit passenden Namen und entsprechenden Methoden versieht, bekommt man *vielleicht* auch Quelltext hin, den man nach einem halben Jahr auch noch versteht
Da habe ich jetzt schon Probleme :shock:
Benutzeravatar
Mawilo
User
Beiträge: 446
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Dienstag 4. November 2008, 19:06

Ich habe den Input etwas verändert und versucht, die Funktion anzupassen, damit es in mein Programm passt. Leider funktioniert es nun nicht mehr richtig. :oops:

Code: Alles auswählen

class SortTrafficDays:
    def __init__(self, rot_dic):
        self.rot_dic = rot_dic
        new_rot_dic = {}
        for day in self.rot_dic.keys():
            new_rot_dic[day] = self.sortDays(self.rot_dic[day])
        self.rot_dic = new_rot_dic
        
    def sortDays(self, day_list):
        tmp_a = [[] for dummy in xrange(7)]
        for element in day_list:
            for i in xrange(7):
                day = int(element[5]) & 2**(6 - i)
                if day:
                    tmp_a[i].append(element)
        result = list()
        tmp_b = ((2**(6 - i), x) for i, x in enumerate(tmp_a))
        for ys, group in groupby(tmp_b, itemgetter(1)):
            if ys == []:
                ys = [['', '', '0:00:00', '', '0:24:00', '', '', '']]
            code = reduce(or_, map(itemgetter(0), group))    
            for y in ys:
                y[5] = code
                result.append(y) 
        return result

input = {
        1:[ 
            ['aaa', 'aab', 'aac','aad', 'aae', 124, 'aaf', 'aag'], 
            ['bbb', 'bbc', 'bbc','bbc', 'bbd', 1, 'bbe', 'bbf'],
            ['ccc', 'ccd', 'ccd','ccd', 'hdt', 60, 'ccd','ccd'], 
            ['ddd', 'sjf', 'hel', 'abf', 'wzt', 2, 'mdn', 'khd']
            ],
        2:[
            ['eee', 'usg', 'uto', 'anf', 'prt', 62, 'mcn', 'asd'], 
            ['fff', 'nsd', 'mkj', 'asx', 'hzu', 124, 'ert', 'rtz']
            ],
        3:[
            ['ggg', 'zui', 'jkl', 'dfg', 'ghj', 2, 'uio', 'vbn'], 
            ['hhh', 'asd', 'sdf', 'dfg', 'fgh', 68, 'wer', 'qwe'], 
            ['iii', 'jkl', 'hjk', 'mki', 'nju', 124, 'vfr', 'bgt']],
        4:[
            ['kkk', 'cde', 'vfr', 'bgt', 'cvb', 1, 'tgb', 'zhn'], 
            ['lll', 'fgh', 'ikm', 'zhn', 'way', 2, 'ghu', 'dfr'], 
            ['mmm', 'drt', 'tzg', 'uhj', 'ikj', 124, 'dfc', 'bgf']
            ],
        }
        
if __name__ == '__main__':
    SortTrafficDays(input)
    for d in input.keys():
        print d
        for e in input[d]:
            print e


Wäre schön, wenn mir jemand einen Tip geben könnte, an welcher Stelle ich alles versemmelt habe.

Mawilo
BlackJack

Dienstag 4. November 2008, 20:54

Du gibst ja auch gar nicht die neu berechneten Daten aus, sondern `input`.

Die Klasse ist merkwürdig. Warum kannst Du nicht normale Funktionen schreiben!?

Und wie schon gesagt: Die Daten gehören nicht in Listen sondern in Klassen. Bei `element[5]` weiss man doch so auf Anhieb gar nicht was gemeint ist, wogegen `element.days` schon etwas selbsterklärender ist. Vor allem kann man dann auch die Datenstruktur ändern ohne überall im Programm die nichtssagenden Indizes suchen und ggf. ändern zu müssen.

Etwas neues an Index 5 zuzuweisen ist eventuell auch ungünstig, weil das wie man an Deiner Ausgabe von `input` sieht, überall aussen sichtbar ist.

`dict.keys()` erzeugt explizit eine Liste mit den Schlüsseln, das ist zum iterieren aber eigentlich nicht notwendig. Da reicht ein einfaches ``for key in a_dict:`` aus. Ausserdem gibt's `dict.items()` bzw. `dict.iteritems()` wenn Schlüssel *und* Werte braucht.
Benutzeravatar
Mawilo
User
Beiträge: 446
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Mittwoch 5. November 2008, 08:32

Hallo BlackJack,
BlackJack hat geschrieben:Du gibst ja auch gar nicht die neu berechneten Daten aus, sondern `input`.
:oops: Da schaut man die ganze Zeit die Funktion an und übersieht, dass die Ausgabe falsch ist :shock:
BlackJack hat geschrieben:Die Klasse ist merkwürdig. Warum kannst Du nicht normale Funktionen schreiben!?
Das ganze soll eigentlich in ein Programm integriert werden. Allerdings ist das aktuelle Problem noch in der Experimentierphase und damit per Definition noch sehr unschön :). Da ich auch nie programmieren gelernt habe und mir Python nur mit Büchern beigebracht habe, habe ich aber bestimmt auch eine andere Ansicht was die "Schönheit von Programmen" angeht. In erster Linie muss es für mich funktionieren.

Ich werde Deinen Hinweis gerne aufgreifen und versuchen, das Problem mit mehreren Funktionen zu lösen. Falls Du noch mehr Ideen hast, was alles in Funktionen und Klassen gehört, würde ich mich freuen.

Danke und Grüße
Mawilo
Antworten