Ich hab ein eigentlich einfaches Problem und weiss nicht, wie ich es in Python lösen soll. Mein Level ist ziemlicher Anfänger.
Ein Messgerät spuckt Integer-Werte aus, die sind in einer Datei fortlaufend in jeweils einer eigenen Zeile gespeichert:
430
456
470
456
456
450
450
Mal sind es ein paar hundert Werte, es können aber auch ein paar tausend sein.
Ich möchte nun wissen,, wie oft jeder Wert vorkommt, die Werte sollen sortiert ausgegeben werden, die häufigsten zuerst:
456, 3
450, 2
430, 1
470, 1
Ich weiss nicht, wie ich eine 2-Spaltige Liste o.ä. von Werte schaffe und bearbeite, 1.Spalte mit den Werten, 2.Spalte mit der dazugehörigen Häufigkeit.
Für Hinweise wäre ich dankbar.
Werte sammeln und Häufigkeiten zählen
Grundsätzlich geht so etwas mit collections.Counter:
(
Fließkommazahlen kommen bei dir im Beispiel nicht vor, sind bei Messwerten jedoch ja Grundsätzlich schon ein Thema und können manchmal Probleme machen:
)
(
Fließkommazahlen kommen bei dir im Beispiel nicht vor, sind bei Messwerten jedoch ja Grundsätzlich schon ein Thema und können manchmal Probleme machen:
Code: Alles auswählen
In [1]: from collections import Counter
In [2]: Counter((1.2345678912345678, 1.2345678912345679))
Out[2]: Counter({1.234567891234568: 2})
-
- User
- Beiträge: 13
- Registriert: Samstag 21. Oktober 2017, 11:34
Alle Werte sind Integer, je Zeile 1 Wert
Ich habe das mal mit Counter probiert:
ergibt:
1 Counter({'4': 1, '3': 1, '0': 1, '\n': 1}) 430
2 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
3 Counter({'4': 1, '7': 1, '0': 1, '\n': 1}) 470
4 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
5 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
6 Counter({'4': 1, '5': 1, '0': 1, '\n': 1}) 450
7 Counter({'4': 1, '5': 1, '0': 1}) 450
Counter zählt nicht die Werte, sondern die Zeichen incl des Zeilenvoschubs
Ich habe das mal mit Counter probiert:
Code: Alles auswählen
#!/usr/bin/python3
import os
from collections import Counter
daten = open('nr','r')
i = 0
for wert in daten:
i = i + 1
c = Counter(wert)
print(i, c, wert)
daten.close()
1 Counter({'4': 1, '3': 1, '0': 1, '\n': 1}) 430
2 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
3 Counter({'4': 1, '7': 1, '0': 1, '\n': 1}) 470
4 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
5 Counter({'4': 1, '5': 1, '6': 1, '\n': 1}) 456
6 Counter({'4': 1, '5': 1, '0': 1, '\n': 1}) 450
7 Counter({'4': 1, '5': 1, '0': 1}) 450
Counter zählt nicht die Werte, sondern die Zeichen incl des Zeilenvoschubs
Zuletzt geändert von lagerschaden am Samstag 18. Februar 2023, 20:51, insgesamt 1-mal geändert.
Weil du die Zeichen (die man nun mal aus Dateien erhält) nicht in Zahlwerte wandelst. Kannst du selbst tun. Oder einfach sowas wie Pandas benutzen, das erledigt das meistens automatisch, und kennt garantiert auch eine Histogramm-Funktion.
- __blackjack__
- User
- Beiträge: 13998
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@lagerschaden: Der `Counter` zählt die Elemente von dem Objekt das übergeben wird. Wenn Du dem eine einzelne Zeile übergibst, dann zählt er die Elemente von der Zeile, also die Zeichen. Du musst *einen* `Counter` erstellen, mit den Elementen die Du zählen willst, also mit den Zahlen.
Weitere Anmerkungen: Eingerückt wird mit vier Leerzeichen pro Ebene.
`os` wird importiert, aber nirgends verwendet.
Dateien öffnet man wo möglich mit der ``with``-Anweisung, und bei Textdateien sollte man immer explizit die Kodierung angeben.
Das manuelle hochzählen von `i` würde man in Python mit der `enumerate()`-Funktion erledigen.
Wenn man Zahlen zählen will, sollte man die auch Texte/Zeilen aus der Datei auch besser tatsächlich in Zahlen umwandeln.
`c` ist kein guter Name, wie die meisten einbuchstabigen, nichtssagenden Namen. `x`, `y`, und `z` für allgemeine Gleitkommawerte und Koordinaten und `i`, `j`, `k` für ganze Zahlen die als (Lauf)Index verwendet werden sind fast die einzigen Ausnahmen wo ein einzelner Buchstabe okay ist, falls man keinen besseren findet. Hier beispielsweise `line_number` statt `i`.
Das Ursprungsprogramm überarbeitet, aber immer noch mit dem Fehler:
Denn wenn man den beseitigt, verschwindet auch die Zeilennummer (ungetestet):
Weitere Anmerkungen: Eingerückt wird mit vier Leerzeichen pro Ebene.
`os` wird importiert, aber nirgends verwendet.
Dateien öffnet man wo möglich mit der ``with``-Anweisung, und bei Textdateien sollte man immer explizit die Kodierung angeben.
Das manuelle hochzählen von `i` würde man in Python mit der `enumerate()`-Funktion erledigen.
Wenn man Zahlen zählen will, sollte man die auch Texte/Zeilen aus der Datei auch besser tatsächlich in Zahlen umwandeln.
`c` ist kein guter Name, wie die meisten einbuchstabigen, nichtssagenden Namen. `x`, `y`, und `z` für allgemeine Gleitkommawerte und Koordinaten und `i`, `j`, `k` für ganze Zahlen die als (Lauf)Index verwendet werden sind fast die einzigen Ausnahmen wo ein einzelner Buchstabe okay ist, falls man keinen besseren findet. Hier beispielsweise `line_number` statt `i`.
Das Ursprungsprogramm überarbeitet, aber immer noch mit dem Fehler:
Code: Alles auswählen
#!/usr/bin/env python3
from collections import Counter
def main():
with open("nr", encoding="ascii") as daten:
for line_number, wert in enumerate(map(int, daten), 1):
counter = Counter(wert)
print(line_number, counter, wert)
if __name__ == "__main__":
main()
Code: Alles auswählen
#!/usr/bin/env python3
from collections import Counter
def main():
with open("nr", encoding="ascii") as daten:
counter = Counter(map(int, daten))
print(counter)
if __name__ == "__main__":
main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
-
- User
- Beiträge: 13
- Registriert: Samstag 21. Oktober 2017, 11:34
Danke für die Hinweise, habe jetzt selbst eine Lösung mit Counter gefunden.
Code: Alles auswählen
#!/usr/bin/python3
from collections import Counter
daten = open('nr', 'r')
i = 0
a=[]
for wert in daten:
wert = wert[:wert.find(' ')]
a = a + [wert]
i = i + 1
daten.close()
print(Counter(a))
-
- User
- Beiträge: 13
- Registriert: Samstag 21. Oktober 2017, 11:34
und noch etwa kürzer
Code: Alles auswählen
#!/usr/bin/python3
from collections import Counter
daten = open('access.log','r')
a=[]
for wert in daten:
wert = wert[:wert.find(' ')] # schneidet alles nach den Werten ab, da können noch Zusätze stehen, die für die Zählung irrelevant sind
a.append(wert)
daten.close()
print(Counter(a), len(a))
- __blackjack__
- User
- Beiträge: 13998
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Das mit dem `find()` ist auch nicht so richtig robust. Falls da kein Leerzeichen ist, dann gibt die Methode -1 zurück, das heisst das letzte Zeichen wird abgeschnitten. Das ist recht ”magisch” und man hat ein Problem falls die letzte Zeile nicht durch ein Zeilenendezeichen abgeschlossen ist. Ich würde das expliziter, beispielsweise mit der `partition()`-Methode lösen.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Hier mal eine robustere Variante, die auch mit leeren Zeilen klarkommt:
Code: Alles auswählen
#!/usr/bin/env python3
from collections import Counter
FILENAME = "access.log"
def get_number_counter(stream):
return Counter(
int(line.partition(" ")[0])
for line in stream if line.strip()
)
def show_number_counts(counter):
for number, count in counter.items():
print(f"{count}x\t{number}")
def main():
with open(FILENAME, encoding="ascii") as stream:
counter = get_number_counter(stream)
show_number_counts(counter)
if __name__ == "__main__":
main()
Und wenn man das ganze noch ausbauen will:
Damit werden nur Zeilen gezählt, die mit einer Nummer anfangen. Somit kommt das Skript auch mit Kommentar-Zeilen klar. Außerdem ist die Ausgabe der Zahlen jetzt sortiert von klein nach groß.
Code: Alles auswählen
#!/usr/bin/env python3
from collections import Counter
FILENAME = "access.log"
def get_number_counter(stream):
return Counter(
int(parts[0]) for parts in (
line.split() for line in stream
) if parts and parts[0].isdigit()
)
def show_number_counts(counter):
for number, count in sorted(counter.items()):
print(f"{count}x\t{number}")
def main():
with open(FILENAME, encoding="ascii") as stream:
counter = get_number_counter(stream)
show_number_counts(counter)
if __name__ == "__main__":
main()
- DeaD_EyE
- User
- Beiträge: 1219
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Noch schwerer für Anfänger zu verstehen:
Anfängerfreundlicher:
Im zweiten Beispiel nutze ich das Prinzip: 'Ask for Forgiveness, Not Permission'
Im ersten Beispiel ist es nicht möglich, da ich eine List Comprehension nutze und dort keine Fehler abgefangen werden können.
Oftmals ist es einfacher einen Fehler abzufangen, als vorher alle möglichen Prüfungen durchzuführen.
Beim int ist es noch recht einfach. Bei einem float gibt es viele Möglichkeiten der Darstellung, was z.B. diese Prüfungen noch länger gestaltet. Aber schon beim int kann man erkennen, dass der Code etwas kürzer ist.
Anmerkung zu Bytes: In beiden Beispielen öffne ich die Dateien im RAW-Modus "rb". Dann bekommt man `bytes` anstatt `str`. Die Funktion `int()` kann auch `bytes` in eine Ganzzahl umwandeln.
PS: Ich habe jetzt nicht so viel gemacht wie die anderen. Du sollst ja programmieren lernen.
Code: Alles auswählen
from collections import Counter
from random import choices
# 100 Zufällige Messwerte zwischen 430 und 450 schreiben
with open("messwerte.txt", "w") as fd:
for value in choices(range(430, 451), k=100):
fd.write(f"{value}\n")
with open("messwerte.txt", "rb") as fd:
# innerhalb dieses Blocks ist die Datei geöffnet und
# kann gelesen werden
messwerte = [
int(digits) for line in fd if (digits := line.rstrip()) and digits.isdigit()
]
messwerte_c = Counter(messwerte)
print(messwerte_c)
Code: Alles auswählen
from collections import Counter
with open("messwerte.txt", "rb") as fd:
messwerte = []
for line in fd:
try:
messwerte.append(int(line.rstrip()))
except ValueError:
pass
messwerte_c = Counter(messwerte)
print(messwerte_c)
Im ersten Beispiel ist es nicht möglich, da ich eine List Comprehension nutze und dort keine Fehler abgefangen werden können.
Oftmals ist es einfacher einen Fehler abzufangen, als vorher alle möglichen Prüfungen durchzuführen.
Beim int ist es noch recht einfach. Bei einem float gibt es viele Möglichkeiten der Darstellung, was z.B. diese Prüfungen noch länger gestaltet. Aber schon beim int kann man erkennen, dass der Code etwas kürzer ist.
Anmerkung zu Bytes: In beiden Beispielen öffne ich die Dateien im RAW-Modus "rb". Dann bekommt man `bytes` anstatt `str`. Die Funktion `int()` kann auch `bytes` in eine Ganzzahl umwandeln.
PS: Ich habe jetzt nicht so viel gemacht wie die anderen. Du sollst ja programmieren lernen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- DeaD_EyE
- User
- Beiträge: 1219
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Ja, am Ende käme eine leere Liste bei raus und der OP weiß dann nicht warum.snafu hat geschrieben: Dienstag 21. Februar 2023, 06:31 @DeaD_EyE
Laut OP steht in den Zeilen nicht zwingend nur eine Zahl. Es können noch Zusatzinformationen dabei sein, die aber nicht verarbeitet werden sollen. Damit käme dein Code nicht klar...
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server