dictionary bei gleichen keys, value zusammenrechen

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
VoLLioMenTT
User
Beiträge: 21
Registriert: Montag 16. Oktober 2017, 16:00

Liebe Python-Forum Community,

Ich habe 2 lists, eine davon besteht aus Artikelnummern(list1) und eine aus stückanzahlen (list2):

Hier 2 lists als Beispiele:
list1: [Artikelnummer1, Artikelnummer1, Artikelnummer2, Artikelnummer2, Artikelnummer3, Artikelnummer4]
list2: [3, 4.5, 8, 12, 130, 5.3]


Nun möchte ich diese lists zu einem dictionary zusammenführen, allerdings kommt die selbe Artikelnummer so wie bei den Beispielen öfter vor. Falls sie sich wiederholt will ich, dass im dictionary für die jeweilige Artikelnummer aus der list1 jenachdem wie oft sie vorkommt, die stückanzahlen aus der list2 zusammengerechnet wird.

Also für das Beispiel oben sollte es so aussehen:

dic: {'Artikelnummer1': '7.5', 'Artikelnummer2': '20', 'Artikelnummer3': '130', 'Artikelnummer4': '5.3'}


Kann mir jemand helfen?

MfG
VoLLioMenTT
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,



was hast du bisher versucht, wie sieht dein jetziger Code dazu aus?
Beziehungsweise, wo kommst du gerade nicht weiter?
VoLLioMenTT
User
Beiträge: 21
Registriert: Montag 16. Oktober 2017, 16:00

@sebastian0202

Also mein gesamter Code sieht folgendermaßen aus:

Code: Alles auswählen

import openpyxl
from collections import OrderedDict
from tkinter.filedialog import askopenfilename
import os


def get_list_xlsx1(xlsx1, ver_xlsx1, hor_xlsx1):
    wb = openpyxl.load_workbook(xlsx1)
    ws = wb.get_active_sheet()
    
    cell_xlsx1_1 = "placeholder"
    cell_xlsx1_2 = "placeholder"
    cell_xlsx1_3 = "placeholder"
    
    list_xlsx1 = []
    
    hor_xlsx1_plus1 = hor_xlsx1 + 1
    
    while cell_xlsx1_1 != None:
        cell_xlsx1_1 = ws.cell(row=ver_xlsx1, column=hor_xlsx1).value
        cell_xlsx1_2 = ws.cell(row=ver_xlsx1, column=hor_xlsx1_plus1).value
        
        if(cell_xlsx1_2 == "MM"):
            cell_xlsx1_1 = cell_xlsx1_1 / 1000
            
        list_xlsx1.append(cell_xlsx1_1)

        ver_xlsx1 += 1
        
    list_xlsx1.pop()
    
    return list_xlsx1

def get_list_xlsx1_an(list_xlsx1, xlsx1, ver_xlsx1, hor_xlsx1):
    wb = openpyxl.load_workbook(xlsx1)
    ws = wb.get_active_sheet()

    cell_xlsx1_an = "placeholder"

    list_xlsx1_an = []

    hor_xlsx1_less2 = hor_xlsx1 - 2

    while cell_xlsx1_an != None:
        cell_xlsx1_an = ws.cell(row=ver_xlsx1, column=hor_xlsx1_less2).value

        list_xlsx1_an.append(cell_xlsx1_an)
        
        ver_xlsx1 += 1

    list_xlsx1_an.pop()

    return list_xlsx1_an

def get_dic_xlsx1(list_xlsx1, list_xlsx1_an):
    dic_xlsx1 =dict(zip(list_xlsx1_an, list_xlsx1))
        
    return dic_xlsx1

def output_dic_in_xlsx2(dic_xlsx1, xlsx2, ver_xlsx2, hor_xlsx2):
    wb =openpyxl.load_workbook(xlsx2)
    ws = wb.get_active_sheet()

    positions_per_item = OrderedDict()
    
    list_xlsx2 = []
    value = 0
    for item in dic_xlsx1.keys():
        position_list = []
        for row_number, cell in enumerate(ws['D{}:D{}'.format(ws.min_row, ws.max_row)], start=5):
            if cell[0].value == item:
                cell = hor_xlsx2 + str(row_number)
                ws[cell] = dic_xlsx1.values(#value)
                value += 1
        
    wb.save(xlsx2)
Bei dem genannten Problem handelt es sich um die Funktion "get_dic_xlsx1", da habe ich noch nicht wirklich rumprobiert, da ich leider wirklich nicht weiß wie ich auf das gewünschte Ergebniss kommen könnte. (Daweil habe ich wie man sieht die listen einfach so zusammengefügt um die nächste Funktion schreiben zu können.)

Ebenfalls würde die Möglichkeit bestehen das ich mir die zwei Funktionen darüber (get_list_xlsx1 und get_list_xlsx1_an) spare, sie zusammenfüge und mir direkt das gewünschte dictionary ausgeben lasse. Allerdings bin ich auch daran gescheitert.

MfG
VoLLioMenTT
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@VoLLioMenTT: wenn man irgendwelche Dummy-Werte einsetzen muß, dann ist das ein Zeichen dafür, dass man die Logik falsch aufgebaut hat. Hier also die Überprüfung des Abbruchkriteriums an der falschen Stelle:

Code: Alles auswählen

def get_list_of_values(filename, row, column):
    wb = openpyxl.load_workbook(filename)
    ws = wb.get_active_sheet()
    
    result = []
    while True:
        value = ws.cell(row=row, column=column).value
        if value is None:
            break
        unit = ws.cell(row=row, column=column + 1).value
        
        if unit == "MM":
            value /= 1000
        result.append(value)
        row += 1
    return result
Die ganzen xlsx1-Suffixe enthalten keine Information und können wegfallen. Die Funktion heißt jetzt »get_list_of_values« weil ich nicht weiß, was da wirklich ermittelt wird.

Wenn Du das Excel in mehreren Funktionen brauchst, solltest Du es nur einmal öffenen und nur das Sheet übergeben, da Du anscheinend aber auch noch eine weitere Spalte auslesen willst, warum machst Du das nicht gleich in einer Funktion?

Code: Alles auswählen

def get_list_of_an_and_values(filename, row, column):
    wb = openpyxl.load_workbook(filename)
    ws = wb.get_active_sheet()
    
    result = []
    while True:
        value = ws.cell(row=row, column=column).value
        if value is None:
            break
        an = ws.cell(row=row, column=column - 2).value
        unit = ws.cell(row=row, column=column + 1).value
        
        if unit == "MM":
            value /= 1000
        result.append((an, value))
        row += 1
    return result
Du weißt nicht, wie Du auf das gewünschte Ergebnis kommst? Dann frag Dich, wie Du es machen würdest, wenn Du eine Liste auf Papier mit Bleistift ausrechnen müßtest?
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,



ich finde die Namensfindung der Funktionen und Variablen sehr unpassend.
Das verwirrt mich mehr, als es mir hilft.
Aber hier mal Code für eine! mögliche Lösung.

Code: Alles auswählen


artikel = ['Artikelnummer1', 'Artikelnummer1', 'Artikelnummer2', 'Artikelnummer2', 'Artikelnummer3', 'Artikelnummer4']
menge = [3, 4.5, 8, 12, 130, 5.3]

def ware_zusammenfassen(artikel, menge):
    # was ist mit unterschiedlichen Laengen der Listen?
    # um was fuer Artikel handelt es sich, wenn sie nicht als Ganzes vorkommen?

    waren = {}
    
    # index und name für jedes Element in 'artikel'
    for index, name in enumerate(artikel):
        # falls der Name noch nicht in 'waren' definiert wurde
        if name not in waren:
            waren[name] = 0
        # Menge dem Artikel dazu addieren
        waren[name] += menge[index]
    return waren

waren = ware_zusammenfassen(artikel, menge)
print(waren)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@sebastian0202: Du programmierst die Funktionalität von `defaultdict` nach:

Code: Alles auswählen

from collections import defaultdict

def ware_zusammenfassen(artikel, menge):
    waren = defaultdict(int)
    for name, menge in zip(artikel, menge):
        waren[name] += menge[index]
    return waren
Benutzeravatar
pixewakb
User
Beiträge: 1411
Registriert: Sonntag 24. April 2011, 19:43

Woher bekommst Du in Deinem Beispiel den "index"-Wert?

Die Lösung hätte ich so auch gemacht: Über 2 gezippte Listen iterieren und dann im Wörterbuch updaten, auf defaultdict wäre ich allerdings nicht gekommen. In deinem Beispiel: Bewirkt das defaultdict(int), dass je neuer Wert immer schon 0 eingetragen ist??
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@pixewakb: ja, der `[index]` ist ein Copy-Paste-Fehler. Und ja, `int()` gibt jedem neuen Wert als Start eine 0.
VoLLioMenTT
User
Beiträge: 21
Registriert: Montag 16. Oktober 2017, 16:00

Guten morgen,

erstmal vielen dank für eure Antworten.

@Sirius3
Ich habe dein Script übernommen, allerdings bekomme ich dabei weiterhin auch gleiche Artiklenummern mehrmals mit ihrer dazugehörigen Stückanzahl durch. Deswegen habe ich das Script leicht angepasst, so dass es mir 2 lists (result_an und result_value) ausgibt. Mit diesen lists bin ich dann in eine andere Funktion gegangen und habe versucht sie richtig zusammenzurechnern sogar mit einem kleinen erfolg.

Hier mal der gesamte Code:

Code: Alles auswählen

import openpyxl
from collections import OrderedDict
from tkinter.filedialog import askopenfilename
import os


def get_list_xlsx1(filename, row, column):
    wb = openpyxl.load_workbook(filename)
    ws = wb.get_active_sheet()

    result_an = []
    result_value = []
    while True:
        value = ws.cell(row=row, column=column).value
        if value is None:
            break
        an = ws.cell(row=row, column=column - 2).value
        unit = ws.cell(row=row, column=column + 1).value

        if unit == "MM":
            value /= 1000
            
        result_an.append(an)
        result_value.append(value)
        row += 1
    return result_an, result_value

def get_dic_xlsx1(xlsx1_an, xlsx1_value):
    count = 0
    xlsx1_value_2 = []
    xlsx1_value_2.append(xlsx1_value[count])
    for item in xlsx1_an:
        count += 1
        xlsx1_value_2.append(xlsx1_value[count])
        if item == xlsx1_an[count]:
            xlsx1_an.pop()

            sum_value_count = xlsx1_value_2[count-1] + xlsx1_value_2[count]
            
            xlsx1_value_2.pop()
            xlsx1_value_2.append(sum_value_count)

    dic_xlsx1 =dict(zip(xlsx1_an, xlsx1_value_2))
    return dic_xlsx1

def get_position_list_xlsx2(dic_xlsx1, xlsx2, hor_xlsx2):
    wb = openpyxl.load_workbook(xlsx2)
    ws = wb.get_active_sheet()

    positions_per_item = OrderedDict()
    position_list_xlsx2 = []

    for item in dic_xlsx1.keys():
        positions = []
        for row_number, cell in enumerate(ws['D{}:D{}'.format(ws.min_row, ws.max_row)], start=1):
           if cell[0].value == item:
               position_list_xlsx2.append(row_number)
        
    return position_list_xlsx2

def output_dic_xlsx1_in_xlsx1(dic_xlsx1, xlsx2, position_list_xlsx2, hor_xlsx2):
    wb = openpyxl.load_workbook(xlsx2)
    ws = wb.get_active_sheet()

    count_var = 0
    
    for item in dic_xlsx1.values():
        cell_var = hor_xlsx2 + str(position_list_xlsx2[count_var])
        print(cell_var)
        count_var += 1
        ws[cell_var] = item
    wb.save(xlsx2)
        
xlsx1 = "Handlagermaterial.xlsx"
xlsx2 = "Kalkulationsstückliste.xlsx"

ver_xlsx1 = 2
hor_xlsx1 = 3

hor_xlsx2 = "E"

xlsx1_an, xlsx1_value = get_list_xlsx1(xlsx1, ver_xlsx1, hor_xlsx1)
print("Artikelnummern:")
print(len(xlsx1_an))
print(xlsx1_an)
print("---------------------------------------------------------------------")
print("Values:")
print(len(xlsx1_value))
print(xlsx1_value)
print("---------------------------------------------------------------------")
print("Dictionary xlsx1:")
dic_xlsx1 = get_dic_xlsx1(xlsx1_an, xlsx1_value)
print(len(dic_xlsx1))
print(dic_xlsx1)
print("---------------------------------------------------------------------")
print("positions list xlsx2:")      
position_list_xlsx2 = get_position_list_xlsx2(dic_xlsx1, xlsx2, hor_xlsx2)
print(len(position_list_xlsx2))
print(position_list_xlsx2)
print("---------------------------------------------------------------------")   
output_dic_xlsx1_in_xlsx1(dic_xlsx1, xlsx2, position_list_xlsx2, hor_xlsx2)
bei dem oben beschriebenen Funktionen handelt es sich um die ersten beiden (get_list_xlsx1 und get_dic_xlsx1).

Output bekomme ich nun folgenden:

Artikelnummern:
86
['EKK0000030', 'EKK0000030', 'EKK0000031', 'EKK0000031', 'EKK0000062', 'ESK0000017', 'ESK0000017', 'ESK0000017', 'ESK0000099', 'ESK0000099', 'ES00000009', 'ES00000028', 'ES00000038', 'ES00000054', 'ES00000054', 'ES00000054', 'ES00000054', 'ES00000148', 'ES00000809', 'NP00000110', 'NP00000110', 'NP00000112', 'NP00000113', 'NP00000144', '100CH595', '100GX705', '100HK990', '100JK590', '100MH042', '100MH042', '100MH060', '100MH062', '100MH062', '100MH062', '100MH062', '100MH062', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100PM032', '100PM032', '100QU894', '100QU894', '100WQ459', '100WR481', '22052090', '22052090', '22052090', '22052090', '30027407', '30043160', '30043160', '30058627', '30060500', '30060502', '30060503', '30060504', '30060506']
---------------------------------------------------------------------
Values:
86
[0.25, 0.3, 1, 1, 0.2, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 1, 1, 3, 2, 1, 5, 1, 10.0, 8, 10.0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1.5, 2.0, 1, 1, 2, 2, 5, 2, 4, 1, 1, 10, 2, 1, 12, 20, 6]
---------------------------------------------------------------------
Dictionary xlsx1:
23
{'EKK0000030': 0.55, 'EKK0000031': 2, 'EKK0000062': 0.2, 'ESK0000017': 3, 'ESK0000099': 2, 'ES00000009': 1, 'ES00000028': 1, 'ES00000038': 1, 'ES00000054': 16, 'ES00000148': 1, 'ES00000809': 1, 'NP00000110': 5, 'NP00000112': 1, 'NP00000113': 5, 'NP00000144': 1, '100CH595': 10.0, '100GX705': 8, '100HK990': 10.0, '100JK590': 1, '100MH042': 6, '100MH060': 1, '100MH062': 5, '100MH064': 18}
---------------------------------------------------------------------
positions list xlsx2:
22
[11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25, 26, 27, 30, 36, 39, 40, 47, 48, 49, 50]
---------------------------------------------------------------------
E11
E12
E14
E15
E16
E17
E18
E19
E20
E23
E24
E25
E26
E27
E30
E36
E39
E40
E47
E48
E49
E50

Wie man sehen kann nimmt er zwar anfangs mal alle Artikelnummern und Stückanzahlen, aber bei der zweiten Funktion hört er bei der Artikelnummer "100MH064" auf und ich weiß leider nicht wieso und wenn ich das richtig sehe nimmt er mir bei der Position list nochmals um einen zu wenig.

Über dem Output steht jedes mal noch die länge der list oder des dictionarys zur Anschauung.

Ich hoffe es ist alles verständlich.

MfG
VoLLioMenTT
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,



der Code von Sirius3 funktioniert problemlos. Hier der Code mal mit deinen Werten:

Code: Alles auswählen

from collections import defaultdict

artikel = ['EKK0000030', 'EKK0000030', 'EKK0000031', 'EKK0000031', 'EKK0000062', 'ESK0000017', 'ESK0000017', 'ESK0000017', 'ESK0000099', 'ESK0000099', 'ES00000009', 'ES00000028', 'ES00000038', 'ES00000054', 'ES00000054', 'ES00000054', 'ES00000054', 'ES00000148', 'ES00000809', 'NP00000110', 'NP00000110', 'NP00000112', 'NP00000113', 'NP00000144', '100CH595', '100GX705', '100HK990', '100JK590', '100MH042', '100MH042', '100MH060', '100MH062', '100MH062', '100MH062', '100MH062', '100MH062', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100MH064', '100PM032', '100PM032', '100QU894', '100QU894', '100WQ459', '100WR481', '22052090', '22052090', '22052090', '22052090', '30027407', '30043160', '30043160', '30058627', '30060500', '30060502', '30060503', '30060504', '30060506']
menge = [0.25, 0.3, 1, 1, 0.2, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 1, 1, 3, 2, 1, 5, 1, 10.0, 8, 10.0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1.5, 2.0, 1, 1, 2, 2, 5, 2, 4, 1, 1, 10, 2, 1, 12, 20, 6]

def ware_zusammenfassen(artikel, menge):
    waren = defaultdict(int)
    for name, value in zip(artikel, menge):
        waren[name] += value
    return waren

waren = ware_zusammenfassen(artikel, menge)
print("Anzahl Unterschiedliche Artikel: %s" % (len(set(artikel))))
print("Anzahl Unterschiedliche Artikel im defaultdict: %s" % (len(waren.keys())))
print(sorted(artikel, key=str.lower))

Kannst du uns sagen was deine Funktion get_dic_xlsx1 Zeile für Zeile macht?
Schreibe dir einfach mal für jede Zeile ein Kommentar mit dem was es macht, was du denkst.

Noch eine kleine Inf: Du hast 36 unterschiedliche Artikel und mit deiner Funktion kommen am Ende 23 raus.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@VoLLioMenTT: Versuch mal zu erklären, was die Zeilen

Code: Alles auswählen

            xlsx1_an.pop()
und

Code: Alles auswählen

            xlsx1_value_2.pop()
machen.

Außerdem ist das alles viel zu kompliziert. Versuch mal das hier:

Code: Alles auswählen

from collections import Counter
from itertools import count
import openpyxl


def read_quantities_from_range(xls_filename, row, column):
    total_quantity = Counter()
    sheet = openpyxl.load_workbook(xls_filename).get_active_sheet()
    for row in count(start=row):
        quantity = sheet.cell(row=row, column=column).value
        if quantity is None:
            break
        article = sheet.cell(row=row, column=column - 2).value
        unit = sheet.cell(row=row, column=column + 1).value
        if unit == "MM":
            quantity /= 1000
        total_quantity[article] += quantity
    return dict(total_quantity)
Ach ja: Abkürzungen sind Scheiße. Verwende aussagekräftige Namen, dann verstehst du auch selber deinen Code besser.
In specifications, Murphy's Law supersedes Ohm's.
VoLLioMenTT
User
Beiträge: 21
Registriert: Montag 16. Oktober 2017, 16:00

Erstmal vielen dank für die schnellen Antworten,

@pillmuncher
.pop löscht doch den letzen Datensatz aus eine list oder?
als Beispiel:
list = [markus, thomas, maria]
list.pop()
print(list)
Output:
[markus, thomas]

Damit wollte ich die letze Artikelnummer löschen falls sie 2 mal vorkommt, sowie die letze stückanzahl und davor noch die richtige zusammenrechnen und dann mit append dranhängen.

Und ja an der Namenssetzung meiner Variablen muss ich definitv noch arbeiten, tut mir leid falls euch das, dass lesen meines Codes erschwert hat.

Ich habe übrigens deinen Code genommen, damit funktioniert es jetzt Problemlos vielen dank.

Jetzt habe ich noch ein letzes Problem undzwar bekomme ich aus den 36 Datensätzen im Dictionary nur 26 positions zurück undzwar findet er sie bis "100WR481" und alle weiteren die im Gegensatz zu denen davor nur aus Zahlen bestehen findet er nicht, könnte es damit zutun haben?

MfG
VoLLioMenTT
Antworten