Größter Wert aus Spalte ausgeben

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.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammmen,

poste hier mal das komplette Konstrukt, wie ich es jetzt einsetze.

Code: Alles auswählen

table_list = []
erg_list = []
verg_dict = {}
def get_num(filename):
    with open(filename, 'r') as infile:
        reader = csv.reader(infile, delimiter="\t")
        for line in reader:
            if line[0] != '---' and line[2] != '---':
                table_list.append(line)

    y = 1
    while True:
        table = tuple(table_list)
        try:
            biggest = max(filter(lambda x: int(x[0]) == y, table))
            erg_list.append(biggest)
            y = y + 1
        except ValueError:
            break

    for items in erg_list:
        if items[0] != '---':
            verg_dict[items[0]] = items[2]

get_num(subgroupindex_path)


gruppenzaehler_dict = {}
daten_list = []
try:
    with codecs.open(subgroupindex_path, "r") as ziel_file:
        reader = csv.reader(ziel_file, delimiter="\t")
        for line in reader:
            try:
                gruppenzaehler_dict[line[0], line[1]] = line[2]
                a = (line[0], line[1], line[2])
                daten_list.append(a)
            except IndexError:
                pass
except IOError:
    pass

daten_list = sorted(set(daten_list))


zeile_list = []
x = 0
p = 1
for line in daten_list:
    if verg_dict != {}:
        w = verg_dict.get(line[0], line[2])
    else:
        w = ''
    if w == '' and line[0] != '---' and line[2] == '---':
        if line[0] in str(x):
            p = p + 1
            zeile = (line[0], line[1], p)
            zeile_list.append(zeile)
        else:
            p = 1
            zeile = (line[0], line[1], p)
            zeile_list.append(zeile)
            x = x + 1
    else:
        zeile = (line[0], line[1], line[2])
        zeile_list.append(zeile)

zeile_list = sorted(set(zeile_list))

with open(subgroupindex_path, "w") as ziel_file:
    writer = csv.writer(ziel_file, delimiter="\t")
    writer.writerows(zeile_list)
    daten_list = zeile_list

del zeile_list


zeile_list = []
if verg_dict != {}:
   for line in daten_list:
        if line[0] != '---' and line[2] == '---':
            p = verg_dict.get(line[0], line[2])
            p = int(p) + 1
            zeile = (line[0], line[1], p)
            zeile_list.append(zeile)
            get_num(subgroupindex_path)
        else:
            zeile = (line[0], line[1], line[2])
            zeile_list.append(zeile)

zeile_list = sorted(set(zeile_list))


try:
    if zeile_list[0] != '':
        with open(subgroupindex_path, "w") as ziel_file:
            writer = csv.writer(ziel_file, delimiter="\t")
            writer.writerows(zeile_list)
except IndexError:
    pass
Bestimmt würdet Ihr das hier wesentlich kürzer fassen, aber ich kann nur meinem Kenntnisstand etwas entsprechend umsetzen. Alles andere ist ein Lernprozess, der nach und nach kommen wird.

Hyperion, dachte mir das schon ... :wink:
Das Konstrukt, habe ich in eine Funktion verpackt, bistimmt geht es auch kürzer, aber für mich übersichtlicher und noch besser erkennbar.
Das mit den Namenszusätzen, wie name_list oder name_dict, ist für mich noch wichtig, um dies besser erkennen zu können.
Oder kann es da Probleme geben?

bords0, werde dies mal testen!

Das ganze Konstrukt, hat folgende Abläufe sicher zu stellen:

Code: Alles auswählen

- existiert die Datei subgroupindex_path nicht
   - anlegen und befüllen der Datei
      - Gruppierung nach Spalte 1 (Hauptgruppen, 1 - 20)
         - fortlaufender Zähler in Spalte 3 (Untergruppenindex) anlegen
         - Ist der Hauptgruppe kein Wert zugeordnet, erhält dieser auch kein Untergruppenindex

- existiert die Datei subgroupindex_path
   - überprüfen neuer Daten
      - Gruppierung nach Spalte 1 (Hauptgruppen, 1 - 20)
         - fortlaufender Zähler in Spalte 3 (Untergruppenindex) anlegen
         - Ist der Hauptgruppe kein Wert zugeordnet, erhält dieser auch kein Untergruppenindex
           - werden nachträglich der Hauptgruppe einen Wert zugewiesen
             - Zuweisung  Zähler fürUntergruppenindex

   - nicht mehr existierende Daten
     - werden aus Datei gelöscht
        - gelöschter Index wird nicht mehr in der betreffenden Hauptgruppe vergeben
BlackJack

@Nobuddy: Das mag syntaktisch eine Funktion sein, aber Funktionen veränder normalerweise keine Datenstrukturen, die nicht als Argumente übergeben wurden. Weil damit das Programm unübersichtlich wird. Man sieht nicht mehr was, wo verändert oder benutzt wird.

Wenn Namen nur innerhalb einer relativ kurzen Funktion verwendet werden, dann brauchst Du auch die Namenszusätze für die Typen nicht. Also mehr Quelltext in Funktionen stecken. Auf Modulebene sollten nur definitionen von Konstanten stehen, also Namen aus importen, Funktions- und Klassendefinitionen, und Namen die an Konstante Werte gebunden sind. Die Typ-Anhängsel bei den Namen werden dann zum Problem, wenn man den Typ ändert. Dann muss man nämlich auch überall denn Namen ändern.

Statt Typen an die Namen anzuhängen, solltest Du lieber Abkürzungen vermeiden. Namen sollen das Verständnis des Quelltextes erleichtern. `verg_dict` tut das für mich nicht. Und für Dich in einem halben Jahr wahrscheinlich auch nicht mehr.

Nochmal zurück zur Funktion: Da werden Daten an eine `table_list` angehängt, und die Funktion wird mehrfach aufgerufen und das anscheinen auch immer mit dem selben Argument. Ausserhalb der Funktion wird der Name `table_list` nicht verwendet. Soll das so sein? Das ist alles sehr verwirrend, und sieht für mich erst einmal falsch aus. Werte sollten Funktionen als Argumente betreten und als Rückgabewerte verlassen. Eine Funktion sollte ausser auf Konstanten auf nichts „einfach so” zugreifen.

Warum wird in `get_num()` ein Tupel aus `table_list` erstellt? Der Schritt ist unnötig.

Statt `filter()` könnte man hier `itertools.ifilter()` verwenden. Beziehungsweise ist das wahrscheinlich in der Form sowieso kaputt, denn das `max()` bezieht sich auf Zeichenketten und nicht auf Zahlen:

Code: Alles auswählen

In [4]: max('7', '42')
Out[4]: '7'
Die Abbruchbedingung für die Verarbeitung ist auch ziemlich undurchsichtig. Das scheint sich darauf zu verlassen, dass am Anfang der `table_list` die Elemente mit Werten sind, und dann noch welche folgen, bei denen zumindest beim ersten Element in der ersten Spalte keine Ziffernfolge steht. Das ist für meinen Geschmack etwas zu implizit und sollte schon beim Einlesen gelöst werden. Die Liste sollte nur Daten enthalten, mit denen man auch etwas anfangen kann.

Jetzt wird mir auch klar warum es nichts ausmacht, dass die Funktion mehrfach aufgerufen wird und `table_list` immer weiter wächst. Es werden trotzdem immer die selben Datensätze am Anfang der Liste verarbeitet und der ganze, immer grösser werdende Rest ignoriert. WTF…

Weiter lese ich in dem Quelltext jetzt mal nicht…
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,

Danke für Deine Kritik!

Ich kann mir vorstellen, daß für Jemand der in der Materie drin ist, Kopfschütteln und vielleicht auch Bauchschmerzen, bei meiner jetzigen Programmierweise bekommt.
Da liegt noch ein langer Weg vor mir, hoffe aber mit Eurer Unterstützung, dies zu schaffen.

Ich werde versuchen, die Kritikpunkte umzusetzen. :wink:

Grüße Nobuddy
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Wäre das eine Funktion, so wie Du sie forderst?

Code: Alles auswählen

def get_write(filename, datenpool):
    with open(filename, "w") as zielfile:
        writer = csv.writer(zielfile, delimiter="\t")
        writer.writerows(datenpool)

get_write(subgroupindex_path, neuedaten)
BlackJack

@Nobuddy: Das wäre eine Funktion die keine Datenstrukturen verwendet, die nicht als Argument herein gekommen sind, ja. Allerdings ist der Name irreführend, denn bei `get_*` erwartet man einen Rückgabewert.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

BlackJack hat geschrieben:@Nobuddy: Das wäre eine Funktion die keine Datenstrukturen verwendet, die nicht als Argument herein gekommen sind, ja. Allerdings ist der Name irreführend, denn bei `get_*` erwartet man einen Rückgabewert.
Ok `get_*` ist hier nicht angebracht, evtl. dann 'now_write'.
Oder wie würdest Du diese Funktion benennen?
BlackJack

@Nobuddy: `write_csv()` vielleicht.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

BlackJack hat geschrieben:@Nobuddy: `write_csv()` vielleicht.
Danke, das werde ich so übernehmen!
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

bords0 hat geschrieben:Ungetesteter Versuch mit groupby, unter der Annahme, dass alle Untergruppen einer Gruppe direkt hintereinander kommen:

Code: Alles auswählen

from itertools import groupby, itemgetter

with open(uigruppen_path, 'r') as f:
    reader = csv.reader(f, delimiter="\t")
    table = ((int(l[0]), int(l[2])) for l in reader if l[0] != '---')
    groups = (g for k, g in groupby(table, key=itemgetter(0)))
    result = dict(max(g, key=itemgetter(1)) for g in groups)

print(result)
#oder:
print(sorted(result.items()))
(Gibt deine Lösung auch bei mehrstelligen Zahlen das richtige Ergebnis? Stringsortierung und numerische Sortierung sind ja verschieden.)
So, versuche Deinen Vorschlag umzusetzen.
Möchte folgendes noch dazu erwähnen.
Bei Spalte_1 (hier l[0]), steht bei Werten kleiner 10 eine Null davor.
Beispiel: 1 = 01

Jetzt weiß ich nicht, ob Dein Konstrukt so noch funktioniert?

Ich habe eine Fehlermeldung beim Testen erhalten:
ImportError: cannot import name itemgetter
Ist itemgetter in itertools nicht(mehr) enthalten, bzw. was ich kann ich da machen?

Grüße Nobuddy
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Für die import-Fehlermeldung, habe ich beim Goggeln eine Lösung gefunden

Code: Alles auswählen

from operator import itemgetter
from itertools import groupby
Hoffe, das ist richtig, zumindest kommt diesbezüglich keine Fehlermeldung mehr.

Allerdings funktioniert es noch nicht so, wie ich mir das vorstelle.
Könnte mir vorstellen, daß es mit dem zusammenhängt:
Bei Spalte_1 (hier l[0]), steht bei Werten kleiner 10 eine Null davor.
Beispiel: 1 = 01
Gibt es da Vorschläge von Euch dazu?
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Nobuddy hat geschrieben: Allerdings funktioniert es noch nicht so, wie ich mir das vorstelle.
Bitte detailiertere Fehlermeldung: Welchen Code benutzt, welchen Input, welchen Output erhalten, welchen erwartet, sonstige Schwierigkeiten? :K
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Konnte es selbst lösen! :D


Zuvor sieht der Inhalt der Datei subgroupindex_path so aus:
01 FARBBÄNDER ---
01 FARBBÄNDER-FÜR-KASSEN ---
01 FARBBÄNDER-FÜR-MATRIXDRUCKER ---
02 ADRESS-ETIKETTEN ---
02 BEWERBUNGSMAPPEN ---
02 BRIEFUMSCHLÄGE ---
02 BUCH-ETIKETTEN ---
02 CD/DVD-BESCHRIFTUNGS-SETS ---
18 ATEMSCHUTZ ---
18 BEWEGUNGSMELDER ---
18 BINDEVLIESE ---
18 BLUTDRUCKMESSGERÄTE ---
18 DOKUMENTEN-KASSETTEN ---
18 FAHRRADSCHLÖSSER ---
18 FEUERLÖSCHER&LÖSCHDECKEN ---
20 ADVENTSKRÄNZE&ZUBEHÖR ---
20 ADVENTSTELLER ---
20 BASTELBÜCHER ---
20 BASTELKARTON ---
20 BASTELN-MIT-HOLZ ---
20 DEKORATIONEN ---
20 GESCHENKBÄNDER&SCHLEIFEN ---
20 GESCHENKPAPIER ---
20 GESCHENKPAPIER-ROLLENWARE ---
20 GESCHENKTÜTEN ---
20 GUTSCHEINKARTEN ---
20 KARTEN&UMSCHLÄGE ---
20 LICHTERKETTEN ---
20 MODELLIERMASSE&ZUBEHÖR ---
20 MOTIVLOCHER ---
20 SERVIETTEN&TISCHDECKEN ---
20 STICKER ---
20 TRANSPARENTPAPIERE ---
40 DISPLAYS ---
40 POS-MATERIAL ---
40 REGALWÄNDE&BESTÜCKUNG ---
Nun möchte ich die erste Spalte Gruppieren und der dritten Spalte mit den '---' eine fortlaufende Nummer verpassen.

Der Inhalt der Datei subgroupindex_path sieht so aus, wenn die Daten bearbeitet wurden (Auszug):
01 FARBBÄNDER 1
01 FARBBÄNDER-FÜR-KASSEN 2
01 FARBBÄNDER-FÜR-MATRIXDRUCKER 3
02 ADRESS-ETIKETTEN 1
02 BEWERBUNGSMAPPEN 2
02 BRIEFUMSCHLÄGE 3
02 BUCH-ETIKETTEN 4
02 CD/DVD-BESCHRIFTUNGS-SETS 5
18 ATEMSCHUTZ 1
18 BEWEGUNGSMELDER 2
18 BINDEVLIESE 3
18 BLUTDRUCKMESSGERÄTE 4
18 DOKUMENTEN-KASSETTEN 5
18 FAHRRADSCHLÖSSER 6
18 FEUERLÖSCHER&LÖSCHDECKEN 7
20 ADVENTSKRÄNZE&ZUBEHÖR 1
20 ADVENTSTELLER 2
20 BASTELBÜCHER 3
20 BASTELKARTON 4
20 BASTELN-MIT-HOLZ 5
20 DEKORATIONEN 6
20 GESCHENKBÄNDER&SCHLEIFEN 7
20 GESCHENKPAPIER 8
20 GESCHENKPAPIER-ROLLENWARE 9
20 GESCHENKTÜTEN 10
20 GUTSCHEINKARTEN 11
20 KARTEN&UMSCHLÄGE 12
20 LICHTERKETTEN 13
20 MODELLIERMASSE&ZUBEHÖR 14
20 MOTIVLOCHER 15
20 SERVIETTEN&TISCHDECKEN 16
20 STICKER 17
20 TRANSPARENTPAPIERE 18
40 DISPLAYS 1
40 POS-MATERIAL 2
40 REGALWÄNDE&BESTÜCKUNG 3
Nun kann es passieren, daß Zeilen aus der Datei entfallen und Andere dafür hinzukommen (Beispiel):
20 ADVENTSKRÄNZE&ZUBEHÖR 1
20 ADVENTSTELLER 2
20 BASTELBÜCHER 3
20 BASTELKARTON 4
20 BASTELN-MIT-HOLZ 5
20 DEKORATIONEN 6
20 GESCHENKBÄNDER&SCHLEIFEN 7
20 GESCHENKPAPIER 8
20 GESCHENKPAPIER-ROLLENWARE 9
20 GESCHENKTÜTEN 10
20 GUTSCHEINKARTEN 11
20 NEUEZEILE ---
20 LICHTERKETTEN 13
20 NEUEZEILE ---
20 MOTIVLOCHER 15
20 SERVIETTEN&TISCHDECKEN 16
20 STICKER 17
20 TRANSPARENTPAPIERE 18
40 DISPLAYS 1
40 NEUEZEILE ---
40 REGALWÄNDE&BESTÜCKUNG 3
Diese neuen Zeilen, sollen wenn möglich nicht die alten frei geworden Nummer aus der Gruppe bekommen.
Dies ist mir momentan nicht ganz gelungen, da z.B. bei der letzten Zahl einer Gruppe, der Wert wieder der neuen Zeile vergeben wird.

Mein Konstrukt dazu:

Code: Alles auswählen

###-2-## Basis-Untergruppen-Index mit Untergruppenzähler erstellen
# untergruppenindex.txt

table = {}
vergleich = {}
ergebnis = []
def get_num(filename):
    with codecs.open(filename, 'r') as infile:
        reader = csv.reader(infile, delimiter="\t", quotechar="^")
        for item in reader:
            if item[0] != '---' and item[2] == '---':
                table[item[0]] = item[0]

    basisdaten = []
    with codecs.open(filename, 'r') as infile:
        reader = csv.reader(infile, delimiter="\t", quotechar="^")
        for item in reader:
            if item[2] != '---' and table.get(item[0]):
                zeile = (item[0], item[2])
                basisdaten.append(zeile)

                
    basisdaten = sorted(set(basisdaten))

    x = 1
    p = 0
    for item in basisdaten:
        try:
            if num == '':
                num = item[0]
        except UnboundLocalError:
            num = item[0]
            pass

        if item[0] == num:
            p = item[1]
            wertneu = (num, p)
        else:
            ergebnis.append(wertneu)
            while item[0] != num:
                num = item[0]
                x = int(num)
                p = 0
    try:
        ergebnis.append(wertneu)
    except UnboundLocalError:
        pass

    for items in ergebnis:
        vergleich[items[0]] = items[1]

get_num(subgroupindex_path)


gruppenzaehler = {}
daten = []
try:
    with codecs.open(subgroupindex_path, "r") as infile:
        reader = csv.reader(infile, delimiter="\t", quotechar="^")
        for item in reader:
            try:
                gruppenzaehler[item[0], item[1]] = item[2]
                a = (item[0], item[1], item[2])
                daten.append(a)
            except IndexError:
                pass
except IOError:
    pass

daten = sorted(set(daten))



neuedaten = []
x = 1
p = 1
for item in daten:
    if vergleich != {}:
        w = vergleich.get(item[0], item[2])
    else:
        w = ''
    if w == '' and item[0] != '---' and item[2] == '---':
        # Führende Nullen entfernen
        a = item[0]
        if a != '':
            if a[0] == '0':
                num = '0{}'.format(str(x))
            else:
                num = '{}'.format(str(x))

        if item[0] == num:
            zeile = (item[0], item[1], p)
            neuedaten.append(zeile)
            p = p + 1
        else:
            while item[0] != num:
                num = item[0]
                x = int(num)
                p = 1
                zeile = (item[0], item[1], p)
                neuedaten.append(zeile)
            p = p + 1
    else:
        zeile = (item[0], item[1], item[2])
        neuedaten.append(zeile)

neuedaten = sorted(set(neuedaten))

write_csv(subgroupindex_path, neuedaten)

daten = neuedaten

del neuedaten


###-3-## Neue Datenzeilen  mit Untergruppenzähler aktualisieren
# untergruppenindex.txt
daten = []
neuedaten = []
with codecs.open(subgroupindex_path, "r") as infile:
    reader = csv.reader(infile, delimiter="\t", quotechar="^")

    for item in reader:
        if vergleich.get(item[0]) and item[2] == '---':
            zeile = (item[0], item[1], item[2])
            daten.append(zeile)
        else:
            zeile = (item[0], item[1], item[2])
            neuedaten.append(zeile)

daten = sorted(set(daten))

if vergleich != {}:
    for item in daten:
        if vergleich.get(item[0]):
            get_num(subgroupindex_path)
            p = vergleich.get(item[0], item[2])
            p = int(p) + 1
            zeile = (item[0], item[1], p)
            neuedaten.append(zeile)

        neuedaten = sorted(set(neuedaten))

        try:
            if neuedaten[0] != '':
                write_csv(subgroupindex_path, neuedaten)
        except IndexError:
            pass
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Nobuddy hat geschrieben:Diese neuen Zeilen, sollen wenn möglich nicht die alten frei geworden Nummer aus der Gruppe bekommen.
Dies ist mir momentan nicht ganz gelungen, da z.B. bei der letzten Zahl einer Gruppe, der Wert wieder der neuen Zeile vergeben wird.
Deine Versuche, dich irgendwie um die Verwendung einer echten relationalen Datenbank herumzuwinden in allen Ehren, aber auf Dauer machst du dir damit deutlich mehr Arbeit als wenn du einmalig den Umgang mit Datenbanken erlernst.

Wenn die Nummer auch nach dem Entfernen der Daten noch wichtig ist, dann ist es ein Fehler den Datensatz überhaupt physisch zu löschen. In dem Fall würde man eher ein Flag verwenden, das den Eintrag als aktiv oder inaktiv kennzeichnet.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich denke auch, dass die Zeit langsam mal reif dafür ist. Davon abgesehen sind die Grenzen von CSV irgend wann einfach mal erreicht. Da böten sich wenn schon andere, einfacher strukturierbare Serialisierungsformate an.

Alleine schon, dass auf "magische" Art neue Zeilen hinzukommen können - da werkeln doch im Hintergrund noch die alten Bash-Scripte rum möchte ich wetten. Anstatt auf einer abstrakten Datenbasis zu arbeiten wird da alles noch auf Dateiebene abgehandelt und das vermutlich von zig Scripts. Das führt auf Dauer nur zu Chaos ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Gebt mir noch ein wenig Zeit, das mit der relationalen Datenbank kommt dann auch bestimmt! :wink:
Nobuddy hat geschrieben:Wenn die Nummer auch nach dem Entfernen der Daten noch wichtig ist, dann ist es ein Fehler den Datensatz überhaupt physisch zu löschen. In dem Fall würde man eher ein Flag verwenden, das den Eintrag als aktiv oder inaktiv kennzeichnet.
Das würde mich brennend interessieren, wie funktioniert das?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nobuddy hat geschrieben: Das würde mich brennend interessieren, wie funktioniert das?
Indem Du einfach eine Datenspalte dazu packst, in der eben zwei Werte enthalgten sein können; der eine steht für aktiv, der andere für inaktiv. Wie Du das abbildest sei Dir überlassen ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten