Ausgabe
Ich bin ziemlich neu in Python und Pandas (Anfänger)
Ich habe einen Pandas-Datenrahmen, mit dem ich versuche zu berechnen, wie viele Stunden / Minuten / Sekunden morgens, nachmittags und abends gearbeitet werden.
morgens liegen innerhalb des Zeitbereichs (03:00 und 12:00) nachmittags liegen innerhalb des Zeitbereichs (12:00 und 17:00)) abends liegen innerhalb des Zeitbereichs (17:00 und 03:00)
data = [ ('employee1', '2022-10-28', '12:06', '13:00:00', '00:00:00', '00:00:23'),
('employee2','2022-10-28', '10:00', '06:00:00', '00:00:00', '00:00:16'),
('employee3', '2022-05-06', '16:13', '08:00:00', '00:54:00', '00:00:09'),
('employee4', '2022-06-03', '2:33', '09:00:00', '00:19:00', '00:00:56'),
('employee5', '2022-08-12', '9:50', '20:00:00', '00:27:00', '00:00:22'),
('employee6', '2022-02-15', '6:52', '00:00:00', '00:35:00','00:00:35')]
df = pd.DataFrame(data, columns =['Name','date','start_time','hours_worked','minutes_worked','seconds_worked'])
df[['enddate_time','time_worked_morning','time_worked_afternoon','time_worked_evening','total_time_wkd']]=None
Kann mir bitte jemand helfen, wie man berechnet, wie viel der Gesamtarbeitszeit in diese 3 Bereiche fällt?
Lösung
Da Sie erwähnt haben, dass Sie ein Anfänger sind, werde ich einige zusätzliche Kommentare in meinen Code aufnehmen. Ich werde auch zwei Lösungen vorstellen: eine “langsame”, die hauptsächlich Python-Schleifen verwendet, und eine “schnelle”, die numpy vektorisierten Code verwendet.
Beide Lösungen verwenden das folgende Code-Snippet. Alle Daten/Zeiten in Ihrem Datenrahmen werden als Zeichenfolgen gespeichert, mit denen schwer zu arbeiten ist. Konvertieren Sie sie in pd.Timestamp
und pd.Timedelta
für eine einfachere Handhabung:
start_time = pd.to_datetime(df["date"] + " " + df["start_time"])
time_worked = (
pd.to_timedelta(df["hours_worked"])
+ pd.to_timedelta(df["minutes_worked"])
+ pd.to_timedelta(df["seconds_worked"])
)
end_time = start_time + time_worked
Die langsame Lösung
Führen Sie eine Schleife über jeden Mitarbeiter und dann über jede Schicht durch, um zu sehen, wie viel Zeit sie in dieser Schicht arbeiten. Zählen Sie dann die Ergebnisse zusammen, um sie als neue Spalten in Ihrem Datenrahmen zuzuweisen:
# You did not specify a unit for your time_worked_morning, _aternoon, etc.
# I assume you want to measure them in hours.
unit = pd.Timedelta(1, "h")
# The shift boundaries. There are 4 boundaries for 3 shifts because we need to
# account for the start and end time of each shift.
# Shift start time: 3, 12, 17
# Shift end time: 12, 17, 27
# The numbers represent hours since midnight of the day
boundaries = [pd.Timedelta(i, "h") for i in [3, 12, 17, 27]]
# Now loop through each employee's work record to break down their working hours
# into shifts
data = []
for work_st, work_et in zip(start_time, end_time):
# Get the midnight of the day
start_of_day = work_st.normalize()
# Work time in each shift starts with 0
work_time = [0] * (len(boundaries) - 1)
# loop through the shifts
for i, (lb, ub) in enumerate(zip(boundaries[:-1], boundaries[1:])):
# Calculate the start and end time of each shift
shift_st = start_of_day + lb
shift_et = start_of_day + ub
# Work time in shift = (effective end time) - (effective start time)
# Effective end time = (work end time) or (shift end time) , whichever is EARLIER => use min
# Effective start time = (work start time) or (shift start time), whichever is LATER => use max
# The "/ unit" operation is to convert the pd.Timedelta object into a
# float representing the hour
t = (min(work_et, shift_et) - max(work_st, shift_st)) / unit
# Our algorithm above sometimes cause an `Effective end time` that is
# before the `Effective start time`. An employee can't spend negative
# time in a shift so clip it to 0 if negative.
work_time[i] = max(0, t)
data.append(work_time)
# Convert `data` numpy array for easier slicing
data = np.array(data)
# Add the extra columns to your data frame
df["enddate_time"] = end_time
df["time_worked_morning"] = data[:, 0]
df["time_worked_afternoon"] = data[:, 1]
df["time_worked_evening"] = data[:, 2]
df["total_time_wkd"] = time_worked / unit
Die schnelle Lösung
Anstatt mit einem Mitarbeiter und einer Schicht gleichzeitig zu arbeiten, werden wir uns mit 2D-Arrays befassen, die alle Mitarbeiter in allen Schichten enthalten. Dies ermöglicht es uns, mehrere vektorisierte Operationen zu verwenden, die von numpy angeboten werden.
# The boundaries here are pretty much the same as in the "slow" solution, only
# as a numpy array instead of list
boundaries = np.array([np.timedelta64(i, "h") for i in [3, 12, 17, 27]])
n = len(boundaries) - 1
# For every employee, repeat the work start time and end time to once per shift
work_st = np.tile(start_time.to_numpy(), (n, 1)).T
work_et = np.tile(end_time.to_numpy(), (n, 1)).T
# Calculate the shift's start and end time for each day
start_of_day = start_time.dt.normalize().to_numpy()[:, None]
shift_st = start_of_day + boundaries[:-1]
shift_et = start_of_day + boundaries[1:]
# The effective start and end time are calculated identically to above, only
# in vectorized form
effective_st = np.max([work_st, shift_st], axis=0)
effective_et = np.min([work_et, shift_et], axis=0)
# The time worked per shift uses the same calculation as the solution: effective
# end time - effective start time, with a minimum of 0
data = np.clip((effective_et - effective_st) / unit, 0, None)
# Add the extra columns to your data frame
df["enddate_time"] = end_time
df["time_worked_morning"] = data[:, 0]
df["time_worked_afternoon"] = data[:, 1]
df["time_worked_evening"] = data[:, 2]
df["total_time_wkd"] = time_worked / unit
Beantwortet von – Code anders
Antwort geprüft von – Marie Seifert (FixError Admin)