MapReduce

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
phykka
User
Beiträge: 9
Registriert: Donnerstag 22. April 2021, 18:25

Hallo zusammen,

ich möchte eine python-Datei für einen MR-Jobs erstellen und habe dazu folgende csv Datei:
Timestamp,Produktname,Produkt_ID,Station1,Transport12,Station2,Transport23,Station3,Transport34,Station4,Transport45,Station5
2022-01-31 05:43:48.259893,Baseball,140499318896400,2.027,1.315,6.909,1.298,-4.039,0.9,1.344,0.972,10.031
2022-01-31 05:53:50.119893,Baseball,140499318897424,0.959,1.676,7.695,1.029,9.518,0.942,1.488,0.977,5.518
2022-01-31 05:59:21.199893,Baseball,140499318897200,3.528,0.788,10.93,1.855,8.214,0.702,-0.141,1.426,5.294
2022-01-31 06:04:38.839893,Baseball,140499318896400,3.201,1.755,5.46,0.422,6.667,0.567,1.108,0.628,2.827
2022-01-31 06:07:28.459893,Baseball,140499318897424,4.001,0.917,7.239,1.363,4.586,0.385,0.417,1.182,5.008
2022-01-31 06:12:28.939893,Baseball,140499318897200,1.418,0.611,6.102,0.625,2.436,1.334,0.865,1.031,3.235
2022-01-31 06:15:43.039893,Baseball,140499318896400,3.918,0.641,6.925,1.271,9.658,0.481,0.951,0.678,3.666
Ziel ist die Zeiten von Station1 bis Station5 zu addieren und zusammen mit dem Produktnamen (Produktname ist immer gleich) auszugeben.

Mein bisheriger Code:

Code: Alles auswählen

from mrjob.job import MRJob
from mrjob.step import MRStep

class Bearbeitungszeit(MRJob):

    def steps(self):
        step_1 = MRStep(mapper=self.zaehle_bearbeitungszeit)
        steps = [step_1]
        return steps

    def zaehle_bearbeitungszeit(self, _,value):
        (Timestamp,Produktname,Produkt_ID,Station1,Transport12,Station2,Transport23,Station3,Transport34,Station4,Transport45,Station5) = value.split(",")
        Nettobearbeitungszeit= Station1, Station2, Station3, Station4, Station5
        yield Produktname,Nettobearbeitungszeit

    pass

if __name__ == "__main__":
    Bearbeitungszeit.run()
Das Ergebnis:

Code: Alles auswählen

"Baseball"      ["2.027", "6.909", "-4.039", "1.344", "10.031"]
"Baseball"      ["0.959", "7.695", "9.518", "1.488", "5.518"]
"Baseball"      ["3.528", "10.93", "8.214", "-0.141", "5.294"]
"Baseball"      ["3.201", "5.46", "6.667", "1.108", "2.827"]
"Baseball"      ["4.001", "7.239", "4.586", "0.417", "5.008"]
"Baseball"      ["1.418", "6.102", "2.436", "0.865", "3.235"]
"Baseball"      ["3.918", "6.925", "9.658", "0.951", "3.666"]
Leider lassen sich die einzelnen Zahlen nicht addieren, da sie alle in einem Tupel sind.

Weiß jemand, wie sich die Zahlen / Tupel umwandeln lassen, damit sie addiert werden können und am Ende nur noch ein Wert existiert?

Vielen Dank vorab!
loggik
User
Beiträge: 3
Registriert: Mittwoch 1. Januar 2020, 21:06

Wenn ich das richtig sehe sind das alles Strings in dem Tuple. Du musst dir also erstmal die Frage stellen wie kann ich aus den ganzen Strings float machen.
Dafür kannst du mit for über das Tuple iterieren:

Code: Alles auswählen

result = 0
for item in my_tuple:
    result += float(item)
Wären die Werte in deinem Tuple schon von Anfang an vom Typ float, dann brauchst du nur sum()

Code: Alles auswählen

sum(my_tuple)
phykka
User
Beiträge: 9
Registriert: Donnerstag 22. April 2021, 18:25

Code: Alles auswählen

from mrjob.job import MRJob
from mrjob.step import MRStep

class Bearbeitungszeit(MRJob):

    def steps(self):
        step_1 = MRStep(mapper=self.zaehle_bearbeitungszeit)
        steps = [step_1]
        return steps

    def zaehle_bearbeitungszeit(self, _,value):
        (Timestamp,Produktname,Produkt_ID,Station1,Transport12,Station2,Transport23,Station3,Transport34,Station4,Transport45,Station5) = value.split(",")
        Nettobearbeitungszeit= (Station1, Station2, Station3, Station4, Station5)
        result = 0
        for item in Nettobearbeitungszeit:
            result += float(item)
        yield Produktname,Nettobearbeitungszeit

    pass

if __name__ == "__main__":
    Bearbeitungszeit.run()
vielen Dank! Ich habe es mit eingebaut aber leider erzeugt das erneut eine Fehlermeldung:

Code: Alles auswählen

    result += float(item)
ValueError: could not convert string to float: 'Station1'
loggik
User
Beiträge: 3
Registriert: Mittwoch 1. Januar 2020, 21:06

Kann es sein, dass die erste Zeile deiner csv-Datei mit eingelesen wird? (Das kann ich anhand deines codes nicht erkennen)

Ansonsten sagen die Fehlermeldungen bei Python sehr genau was schief gelaufen ist: In deinem Fall hat Python versucht den String "Station1" in eine float zu casten, was nicht funktioniert.

In der jetzigen Form wird zwar result berechnet aber nicht zurückgegeben, das wirst du auch noch überarbeiten müssen.
phykka
User
Beiträge: 9
Registriert: Donnerstag 22. April 2021, 18:25

Danke! :) jetzt funktioniert es
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Hallo
Das geht bestimmt noch schöner, aber ich hab mich auch nicht lange damit aufgehalten:

Code: Alles auswählen

from pandas import read_csv

frame = read_csv("jobs.csv")
stations = frame.T[3::2].T
print(stations)
print(stations.sum())
print(sum(stations.sum()))
gibt folgendes aus:

Code: Alles auswählen


  Station1 Station2 Station3 Station4 Station5
0    2.027    6.909   -4.039    1.344   10.031
1    0.959    7.695    9.518    1.488    5.518
2    3.528    10.93    8.214   -0.141    5.294
3    3.201     5.46    6.667    1.108    2.827
4    4.001    7.239    4.586    0.417    5.008
5    1.418    6.102    2.436    0.865    3.235
6    3.918    6.925    9.658    0.951    3.666
Station1    19.052
Station2     51.26
Station3     37.04
Station4     6.032
Station5    35.579
dtype: object
148.96299999999997
Benutzeravatar
DeaD_EyE
User
Beiträge: 1224
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Was ich immer so schade finde, ist die exzessive Nutzung von numpy, pandas oder anderen Bibliotheken, und das fehlende Verständnis, wie man etwas ausschließlich mit der Standardlib von Python macht. Hier mal ein Beispiel ohne pandas.

Code: Alles auswählen

import io
import csv
from collections import namedtuple
from datetime import datetime


data = """Timestamp,Produktname,Produkt_ID,Station1,Transport12,Station2,Transport23,Station3,Transport34,Station4,Transport45,Station5
2022-01-31 05:43:48.259893,Baseball,140499318896400,2.027,1.315,6.909,1.298,-4.039,0.9,1.344,0.972,10.031
2022-01-31 05:53:50.119893,Baseball,140499318897424,0.959,1.676,7.695,1.029,9.518,0.942,1.488,0.977,5.518
2022-01-31 05:59:21.199893,Baseball,140499318897200,3.528,0.788,10.93,1.855,8.214,0.702,-0.141,1.426,5.294
2022-01-31 06:04:38.839893,Baseball,140499318896400,3.201,1.755,5.46,0.422,6.667,0.567,1.108,0.628,2.827
2022-01-31 06:07:28.459893,Baseball,140499318897424,4.001,0.917,7.239,1.363,4.586,0.385,0.417,1.182,5.008
2022-01-31 06:12:28.939893,Baseball,140499318897200,1.418,0.611,6.102,0.625,2.436,1.334,0.865,1.031,3.235
2022-01-31 06:15:43.039893,Baseball,140499318896400,3.918,0.641,6.925,1.271,9.658,0.481,0.951,0.678,3.666"""


reader = csv.reader(io.StringIO(data))
header = next(reader)
header.append("Summe")

Row = namedtuple("Row", header)
results = []

for ts, name, pid, *stations in reader:
    value = round(sum(map(float, stations)), 3)
    ts = datetime.strptime(ts, "%Y-%m-%d %H:%M:%S.%f")
    print(ts, name, pid, *stations, value)
    results.append(Row(ts, name, pid, *stations, value))
Das ist jetzt keine besondere Anstrengung für mich, da ich die stdlib fast auswendig kenne. Hier und dort vergesse ich mal bestimmte Features, aber bei ständiger Nutzung bleibt das Wichtigste im Kopf.
Bei dem Beispiel kommt z.B. PEP 3132 zur Anwendung: https://www.python.org/dev/peps/pep-3132/

Lernt man so was, wenn man pandas schwarze Magie nutzt? Nein.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@DeaD_EyE
Stellt sich halt nur die Frage, was daran falsch sein soll, Werkzeuge zu benutzen, wenn diese zur Verfügung stehen. Im Rahmen eines Python-Kurses, der noch nicht pandas behandelt hat, finde ich deine Lösung sinniger, weil der Lerneffekt größer ist. Im täglichen Gebrauch würde ich aber auf jeden Fall zur pandas-Lösung tendieren, da sie deutlich kürzer und für die meisten wohl auch schneller zu implementieren ist.
Benutzeravatar
__blackjack__
User
Beiträge: 14001
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei da jetzt soweit ich das sehe in `stations` auch die "Transport*"-Spalten enthalten sind und mit aufsummiert werden‽ Ich vermute das war nicht gewünscht/ist falsch.

Kann man in der Pandas-Lösung aber auch leicht übersehen. Da hätte ich statt magischem Slicing mit Schrittweite wahrscheinlich die Spalten nach danach selektiert ob die mit "Station" anfangen. Ist für den menschlichen Leser besser verständlich und wahrscheinlich auch sicherer.
“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
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Ich kenne die Stdlib nicht "fast auswendig" … auch die "schwarzre Magie" von pandas ist erstmal gewöhnungsbedürftig, aber ich habe halt nicht übersehen das der OP offenbar nur an den Station.* -spalten interressiert ist.
Hier eine Schönere Version:

Code: Alles auswählen

import pandas as pd 

frame = pd.read_csv("jobs.csv")
is_station = frame.columns.str.startswith('Station')
stations = frame.loc[:, is_station]
print(stations,"\n")
print(stations.sum(),"\n")
print(sum(stations.sum()))
Benutzeravatar
DeaD_EyE
User
Beiträge: 1224
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

__blackjack__ hat geschrieben: Samstag 11. Dezember 2021, 11:11 Wobei da jetzt soweit ich das sehe in `stations` auch die "Transport*"-Spalten enthalten sind und mit aufsummiert werden‽ Ich vermute das war nicht gewünscht/ist falsch.
Ja, das könnte man noch mit slicing lösen.
2 Zeilen Code mehr.

Kann man in der Pandas-Lösung aber auch leicht übersehen. Da hätte ich statt magischem Slicing mit Schrittweite wahrscheinlich die Spalten nach danach selektiert ob die mit "Station" anfangen. Ist für den menschlichen Leser besser verständlich und wahrscheinlich auch sicherer.
Alle wollen mit Pandas irgendwas an Daten verarbeiten, haben noch nicht einmal die Grundkonzepte des Slicings verstanden. Das führt dazu, dass die Anfänger nicht Python lernen, sondern Pandas. Mein Einwand richtet sich nicht gegen Pandas selbst, sondern die Art und Weise wie damit umgegangen wird. Ich fange doch auch nicht mit der Elektroinstallation in einem Haus an, wenn das Haus noch gar nicht steht.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18252
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: viele wollen nicht programmieren, sondern Daten analysieren, und dazu muß man meist nur Pandas beherrschen. Die einen bauen Häuser, andere machen Elektroinstallation. Daran ist nichts auszusetzen.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

@DeaD_EyE: Aber ist das nicht auch ein bischen eine Eigenart von Python, dass man sich nicht lange überlegt wie etwas sinnvoll und gut zu programmieren wäre, sondern dass man dafür ein Modul sucht, welches das für einen löst?
Eine Eigenschaft, die Leute die nicht gerne programmieren mögen und worüber C Programmierer etwas die Nase rümpfen :wink:
Da ist zumindest mein Eindruck...
Antworten