Data frame sum of a Time column

Simple (I hope): how to sum the Time column (d) of 15:52:31 + 15:52:31 + 15:52:31 giving 47:34:33
Screenshot from 2022-10-14 16-02-50

Your current type is Time, which is a time in a day. You can’t add two times in a day together.

What you want is a Period

julia> t1 = Dates.CompoundPeriod(Hour(15), Minute(52), Second(31));

julia> t2 = Dates.CompoundPeriod(Hour(2), Minute(25), Second(10));

julia> t1 + t2
17 hours, 77 minutes, 41 seconds
1 Like

NB: the sum should be 47:37:33

Also, the addition of CompoundPeriods does not seem sufficient:

t1 = Dates.CompoundPeriod(Hour(15), Minute(52), Second(31))
t1 + t1 + t1
45 hours, 156 minutes, 93 seconds

One attempt here below, based on this Rosetta code:

using Dates, DataFrames

function HHMMSS_duration(sec)
    t = Int[]
    for dm in (60, 60)
        sec, m = divrem(sec, dm)
        pushfirst!(t, m)
    end
    pushfirst!(t, sec)
    return Dates.CompoundPeriod(Hour(t[1]), Minute(t[2]), Second(t[3]))
end

df = DataFrame(a=1:3, d=Dates.Time.(["15:52:31","15:52:31","15:52:31"], "H:M:S"))

s = cumsum(Dates.value.(df.d))/1e9
df.cumsum = HHMMSS_duration.(s)


julia> df
 Row β”‚ a      d         cumsum
     β”‚ Int64  Time      Compound…
─────┼───────────────────────────────────────────────────
   1 β”‚     1  15:52:31  15 hours, 52 minutes, 31 seconds
   2 β”‚     2  15:52:31  31 hours, 45 minutes, 2 seconds
   3 β”‚     3  15:52:31  47 hours, 37 minutes, 33 seconds

canonicalize might help. It does what its name implies or what this example shows:

julia> cp = Dates.CompoundPeriod(Hour(2),Minute(100),Second(5))
2 hours, 100 minutes, 5 seconds

julia> canonicalize(cp)
3 hours, 40 minutes, 5 seconds

If we take the OP example, it produces:

t1 = Dates.CompoundPeriod(Hour(15), Minute(52), Second(31))
canonicalize(t1 + t1 + t1)

1 day, 23 hours, 37 minutes, 33 seconds

Adding times of the day makes no sense. (15:00)*3=45:00 assumes that there is something special about midnight, but it just depends on which offset you choose.

Of course it doesn’t, and Peter already mentioned it above: a different type should be used. Meanwhile, if we reinterpret them as durations, how can we proceed?

1 Like

Time is essentially nanoseconds so this should work?

julia> t1 = Time(15, 52, 31)
julia> t2 = Time(15, 52, 31)
julia> t3 = Time(15, 52, 31)

julia> canonicalize(t1.instant + t2.instant + t3.instant)
1 day, 23 hours, 37 minutes, 33 seconds

Thank you all and so I did two things with the second function feeling rather clumsy…(but I’m learning stuff). Q. How do you pass a data column name by reference to a function i.e. total_time(accepted.Time).

β€œTotal a dataframe Time column in Weeks, Days, Hours, Minutes and Seconds”
function total_time_formal(xxx)
t = Dates.CompoundPeriod(Hour(0), Minute(0), Second(0))
for i in 1:nrow(xxx)
t += Dates.CompoundPeriod(xxx.Time[i])
end
return canonicalize(t)
end

β€œTotal a dataframe Time column for HH:MM:SS i.e. hundreds of hours:minutes:seconds”
function total_time()
t1 = t2 = t3 = 0.0 # hours, minutes, seconds
for i in 1:nrow(accepted)
t1 += Dates.hour(accepted.Time[i])
t2 += Dates.minute(accepted.Time[i])
t3 += Dates.second(accepted.Time[i])
end
t2 += modf(t3 / 60)[2] # whole seconds to minutes
t3 = modf(t3 / 60)[1] * 60 # residual seconds
t1 += modf(t2 / 60)[2] # whole minutes to hours
t2 = modf(t2 / 60)[1] * 60 # residual minutes
return t1, t2, t3
end

Another way to convert higher periods (days, weeks) to hours:

julia> hourify(t) = begin
  tt = canonicalize(t) ; 
  pushfirst!(tt.periods, sum(Hour.(splice!(tt.periods,1:findlast(in([Hour,Day,Week]), typeof.(tt.periods))))))
end
hourify (generic function with 1 method)

julia> hourify(Second(123121414))
3-element Vector{Period}:
 34200 hours
 23 minutes
 34 seconds

The positive about this method is keeping the calculations hidden at the Dates module. Perhaps the technique willl be of sum use.