Seite 1 von 1
Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 15:56
von Dennis89
Hallo zusammen,
ich rätsle schon einige Zeit an einer eleganten Lösung.
Ich habe zwei Wörterbücher, der Schlüssel ist ein Gegenstand und der Wert ist die Menge des Gegenstands. Jetzt hätte ich gern nur die Information in wie weit sich die Wörterbücher unterscheiden. Also wenn ein Gegenstand nur in einem Wörterbuch drin ist, dann soll der mit zugehöriger Menge ausgegeben werden und wenn die gleichen mit unterschiedlicher Menge enthalten sind, dann soll der Gegenstand mit der Differenzmenge ausgegeben werden.
Mein Beispielcode:
Code: Alles auswählen
MASTER = {'10': 6, '30': 2, '40': 4}
TO_COMPARE = {'10': 5, '30': 2}
def main():
parts_different_quantity = set(MASTER.items() ^ TO_COMPARE.items())
print(parts_different_quantity)
if __name__ == '__main__':
main()
Die Ausgabe ist:
Das Ergebnis habe ich an sich auch erwartet. Nur wie geht es weiter. Das ist so schön elegant, jetzt verschachtelte 'for'-Schleifen zu schreiben und ein neues Wörterbuch zu erstellen mit der "manuell" berechneten Differenz, würde die Schönheit ja schon irgendwie versauen.
Geht das irgendwie eleganter?
Vielen Dank und Grüße
Dennis
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 16:27
von kbr
Das geht, was es wirklich das ist, was du meinst:
Code: Alles auswählen
d1 = dict(zip("abcde", range(10, 15)))
d2 = dict(zip("cdefg", range(20, 25)))
# sammel alle key-value Paare, die beide dicts nicht gemeinsam haben:
combined = d1 | d2
diff = {key: combined[key] for key in d1.keys() ^ d2.keys()}
# plus der gemeinsamen keys mit der Differenz der Werte:
diff.update({key: abs(d1[key] - d2[key]) for key in d1.keys() & d2.keys()})
print(diff)
{'a': 10, 'g': 24, 'f': 23, 'b': 11, 'c': 8, 'e': 8, 'd': 8}
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 18:15
von narpfel
Ist das nicht eigentlich
`collections.Counter`?
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 19:30
von kbr
Wenn es um das Befüllen der Dictionaries geht, kann das eine Option sein. Warum aber für die Auswertung die Differenz der Werte der Schnittmenge beider Dictionaries in Verbindung mit den weiteren Inhalten sinnvoll ist, erschließt sich mir nicht. Aber vielleicht gibt es dafür einen guten Grund.
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 20:40
von Dennis89
Super, vielen Dank!
Das sieht schon schöner aus. Die Wörterbücher sind eigentlich eingelesene Listen. In den Listen steht schon die Anzahl der Gegenstände und weil das zusammengehört speichere ich die beim einlesen gleich als Wörterbuch. Alle Listen sollen das gleiche beinhalten wie 'MASTER' und wenn sie das nicht tun, dann will ich wissen, was fehlt und wie oft es fehlt. Deswegen die Auswertung der Differenz. Sollte das geschickter gehen, dann immer gern. Ich könnte mir vorstellen das Pandas da einiges bietet, aber ich wollte auch mit den Bitwisen Operatoren vertrauter werden, weil ich denke, dass das fundamentale Grundlagen sind(?) und das wäre mir persönlich erst mal wichtiger.
Grüße
Dennis
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 21:15
von narpfel
Code: Alles auswählen
In [6]: a = Counter({'10': 6, '30': 2, '40': 4})
In [7]: b = Counter({'10': 5, '30': 2})
In [8]: a - b
Out[8]: Counter({'40': 4, '10': 1})
In [9]: difference = a - b
In [10]: difference["30"]
Out[10]: 0
?
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Freitag 17. November 2023, 21:22
von Dennis89
Das darf ja nicht wahr sein. Die Differenz der Counter, ist alles was ich brauche!
Danke, da besteht der Code ja eigentlich nur noch aus Daten einlesen und Ergebnis speichern
Grüße
Dennis
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 10:04
von kbr
narpfel hat geschrieben: Freitag 17. November 2023, 21:15
?
Interessant, dass auf `collections.Counter` die Subtraktion implementiert ist. Das wusste ich noch nicht. Warum hast du das bei deinem ersten Hinweis nicht direkt gezeigt?
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 12:39
von narpfel
@kbr: Hab ich doch?
Sogar noch mit Beispiel in der Doku.
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 14:53
von kbr
Das meinte ich nicht. Du hättest beispielsweise direkt ein konkretes Beispiel zeigen können, statt andere raten zu lassen, was genau in der Dokumentation du gemeint hast. In etwa so:
Code: Alles auswählen
import collections
d1 = collections.Counter(dict(zip("abcde", range(10, 15))))
d2 = collections.Counter(dict(zip("cdefg", range(20, 25))))
for diff in d1 - d2, d2 - d1:
for key in "abcdefg":
print(key, diff[key])
Dann hätte sich auch schnell erkennen lassen, dass weder "d1 - d2" noch "d2 - d1" dem von @Dennis89 gewünschten Ergebnis entsprechen – wie ich auch erst an obigen Beispiel festgestellt habe.
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 15:24
von Dennis89
Hallo,
ich hatte eigentlich erst vor nächste Woche daran weiter zu arbeiten, aber jetzt habe ich mir die 'Counter'-Lösung auch noch genauer angeschaut. Ja das stimmt, die Gegenstände, die in der zweiten Liste gar nicht vorkommen, werden nicht mit ausgegeben. Die brauche ich natürlich auch. Die Version mit den Bitweisen Operatoren von @kbr berücksichtigen das.
Grüße
Dennis
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 15:29
von narpfel
Code: Alles auswählen
In [4]: (d1 - d2) | (d2 - d1)
Out[4]: Counter({'g': 24, 'f': 23, 'b': 11, 'a': 10, 'c': 8, 'd': 8, 'e': 8})
? Oder sollen die Werte aus `d2 - d1` negativ sein?
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 15:53
von Dennis89
Ahja, da hätte man eigentlich drauf kommen können. Dankeschön.
Das mit den negativen Werte wäre vielleicht sogar sehr gut, dann kann ich daran unterscheiden aus welcher Liste die Differenz kommt.
Ich dachte dass das irgendwie mit '~' gehen könnte, aber das darf ich so nicht auf ein Counter-Objekt anwendne.
Grüße
Dennis
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 16:00
von kbr
Ok, so geht es also auch:
Code: Alles auswählen
d1 = collections.Counter(dict(zip("abcde", range(10, 15))))
d2 = collections.Counter(dict(zip("cdefg", range(20, 25))))
diff = dict(d1 - d2 | d2 - d1)
Interessant ist, das man dem Coode, wie so oft, nicht direkt ansieht, wie schnell er ausgeführt wird:
Code: Alles auswählen
%timeit diff = dict(d1 - d2 | d2 - d1)
3.69 µs ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
Nutzt man ein dict statt dem Counter wird der Code deutlich schneller:
Code: Alles auswählen
d1 = dict(zip("abcde", range(1, 6)))
d2 = dict(zip("bcefg", range(10, 16)))
%%timeit
combined = d1 | d2
diff = {key: combined[key] for key in d1.keys() ^ d2.keys()}
diff.update({key: abs(d1[key] - d2[key]) for key in d1.keys() & d2.keys()})
1.16 µs ± 12.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Für die meisten Anwendungen dürfte dies aber unter Mikrooptimierung fallen und daher unwichtig sein.
Negative Differenzwerte ergeben sich, so wie ich das gegenwärtig sehe, nur mit der zweiten (unteren) Variante (ohne abs() dann).
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 16:24
von Sirius3
Am einfachsten wäre, das in einem einzigen Dictcomprehension-Ausdruck zu schreiben:
Code: Alles auswählen
{key: d1.get(key, 0) - d2.get(key, 0) for key in d1.keys() | d2.keys()}
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 16:48
von kbr
Wenn es um Einfachheit geht, so geht es sogar noch etwas einfacher:
Code: Alles auswählen
{key: d1.get(key, 0) - d2.get(key, 0) for key in d1 | d2}
Leider ist diese Variante
zu einfach: bei keys die sich in d2 aber nicht in d1 befinden, ergeben sich negative Werte, was nicht gewünscht ist. Das müsste dann später noch korrigiert werden.
Edit: aber so geht es:
Code: Alles auswählen
{key: d1.get(key, 0) - d2.get(key, 0) if key in d1 else d2.get(key, 0) for key in d1 | d2}
Edit 2: mit nur einem "get()" geht es auch, die beiden anderen sind nicht erforderlich:
Code: Alles auswählen
{key: d1[key] - d2.get(key, 0) if key in d1 else d2[key] for key in d1 | d2}
Re: Wörterbücher auf unterschiedlichen Inhalt vergleichen, das geht doch eleganter?
Verfasst: Samstag 18. November 2023, 17:01
von Dennis89
Ich weis nicht ob sonst noch jemand Code nach dem Aussehen beurteilt, aber die Dictcomprehension sieht auch elegant aus.
Vielen Dank für die weiteren Beispiele.
Die Werte der Keys, die in d2 aber nicht in d1 sind, dürfen gerne negativ sein. Dann kann ich daran erkennen, wo die Werte herkommen. Ich muss aber erst noch nachfragen, wie die Werte aufbereitet werden sollen.
Aber dank euch habe ich ja für alle Eventualitäten Lösungen, vielen Dank. (Damit will ich das aber nicht abbrechen, falls es noch mehr sinnvolle geben sollte)
Grüße
Dennis
P.S. das mit der Zeit ist interessant. Wenn ich das Abarbeiten meiner 'main'-Funktion messen will, nehme ich dann 'time.process_time' und berechne die Differenz zwischen Start und Ende oder gibts da was besseres? timeit sei ja nur für kurze Codestücke gedacht.