使用 python 从时间列表中创建平均值

Posted

技术标签:

【中文标题】使用 python 从时间列表中创建平均值【英文标题】:Using python to create an average out of a list of times 【发布时间】:2012-08-15 13:28:43 【问题描述】:

我有一个巨大的时间列表 (HH:MM:SS),我知道如果我想创建一个平均值,我可以将小时、秒和分钟分开,然后平均每个时间,然后将它们连接在一起。但是我觉得必须有更好的方法来做到这一点。有谁知道更好的方法吗?

谢谢!

【问题讨论】:

给出一个正确的 Python 输入和输出的(简短)示例。 只花一个绝对时间total # seconds = hours * 3600 + minutes * 60 + seconds 并将其平均并将其作为时间重新组合起来怎么样? 请您提供一些示例数据。您是否有字符串、timedelta 等格式的数据... 请注意,如果您的平均不是持续时间,而是时间点,那么它会环绕并且平均值变成一个模糊的概念 - - 你需要定义午夜前后发生的事情。例如,23:59:5800:00:00 的平均值是多少?是23:59:59 还是11:59:59 【参考方案1】:

转换为自午夜以来的秒数和平均值时出现问题。如果你在 23:50 和 00:10 这样做,你会得到 12:00,而你想要的时间是 00:00。

更好的方法是average the angles。

import datetime
import math
import numpy

def datetime_to_radians(x):
    # radians are calculated using a 24-hour circle, not 12-hour, starting at north and moving clockwise
    time_of_day = x.time()
    seconds_from_midnight = 3600 * time_of_day.hour + 60 * time_of_day.minute + time_of_day.second
    radians = float(seconds_from_midnight) / float(12 * 60 * 60) * 2.0 * math.pi
    return radians

def average_angle(angles):
    # angles measured in radians
    x_sum = numpy.sum([math.sin(x) for x in angles])
    y_sum = numpy.sum([math.cos(x) for x in angles])
    x_mean = x_sum / float(len(angles))
    y_mean = y_sum / float(len(angles))
    return numpy.arctan2(x_mean, y_mean)

def radians_to_time_of_day(x):
    # radians are measured clockwise from north and represent time in a 24-hour circle
    seconds_from_midnight = int(float(x) / (2.0 * math.pi) * 12.0 * 60.0 * 60.0)
    hour = seconds_from_midnight / 3600
    minute = (seconds_from_midnight % 3600) / 60
    second = seconds_from_midnight % 60
    return datetime.time(hour, minute, second)

def average_times_of_day(x):
    # input datetime.datetime array and output datetime.time value
    angles = [datetime_to_radians(y) for y in x]
    avg_angle = average_angle(angles)
    return radians_to_time_of_day(avg_angle)

average_times_of_day([datetime.datetime(2017, 6, 9, 0, 10), datetime.datetime(2017, 6, 9, 0, 20)])
# datetime.time(0, 15)

average_times_of_day([datetime.datetime(2017, 6, 9, 23, 50), datetime.datetime(2017, 6, 9, 0, 10)])
# datetime.time(0, 0)

【讨论】:

我认为这个应该是公认的答案。它对我来说非常有效。总之,有一点需要调整:hour = seconds_from_midnight / 3600 行应该修改为hour = (seconds_from_midnight / 3600) % 24 以处理hour 为负值的情况。该错误可以通过计算平均值来重现,例如 18:20 和 20:35 我认为应该是弧度 = float(seconds_from_midnight) / float(24 * 60 * 60) * 2.0 * math.pi 和 seconds_from_midnight = int(float(x) / (2.0 * math.pi ) * 24.0 * 60.0 * 60.0) 注意 24 而不是 12 - 使 24 小时 = 2pi(360 度)。并且上述评论仍然有效。 不错。但是您在解决方案中引入了 numpy;罗塞塔版本就目前而言是完美的。不是每个人都会有/想要手头上的 numpy。【参考方案2】:

您不想以这种方式在小时、分钟和秒上“平均”时间:

00:59:00
01:01:00

平均到 01:00:00,但不是你提出的逻辑。

而是将所有时间间隔转换为秒,计算平均值并转换回HH:MM:SS

00:59:00 -> 3540 seconds
01:01:00 -> 3660 seconds
            ============
average:    3600 seconds converted to HH:MM:SS -> 01:00:00

【讨论】:

【参考方案3】:

这是@eumiro 的答案的一种可能实现,但这种逻辑仅在这些是持续时间而不是时间时才有效,正如@lazyr 所指出的那样:

from datetime import timedelta

times = ['00:58:00','00:59:00','01:00:00','01:01:00','01:02:00']

print(str(timedelta(seconds=sum(map(lambda f: int(f[0])*3600 + int(f[1])*60 + int(f[2]), map(lambda f: f.split(':'), times)))/len(times))))

Also thanks to a post@SilentGhost,and a post@Herms

【讨论】:

【参考方案4】:

首先使用strptime将时间从字符串格式解析为时间结构,然后使用mktime将时间从纪元转换为秒,然后你应该将所有秒相加除以次数,然后再转换回来使用localtime对结构进行计时

这是一个例子:

import time


a = time.strptime("2000:11:12:13","%Y:%H:%M:%S")
b = time.strptime("2000:11:14:13","%Y:%H:%M:%S")

avg_time = time.localtime(((time.mktime(a)+time.mktime(b))/2))

>> time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=11, tm_min=13, tm_sec=13, tm_wday=5, tm_yday=1, tm_isdst=0)

请注意,我添加了 2000 年,因为 mktimeOverflowError 指定为默认的 1900 年

【讨论】:

【参考方案5】:

您需要将其转换为复数,取参数,然后平均度数。

最后你需要解析日期来得到你想要的,然后转换回原来的小时。

from cmath import rect, phase
from math import radians, degrees

def meanAngle(deg):
    complexDegree = sum(rect(1, radians(d)) for d in deg) / len(deg)
    argument = phase(complexDegree)
    meanAngle = degrees(argument)
    return meanAngle

def meanTime(times):
    t = (time.split(':') for time in times)
    seconds = ((float(s) + int(m) * 60 + int(h) * 3600) 
               for h, m, s in t)
    day = 24 * 60 * 60
    toAngles = [s * 360. / day for s in seconds]
    meanAsAngle = meanAngle(toAngles)
    meanSeconds = meanAsAngle * day / 360.
    if meanSeconds < 0:
        meanSeconds += day
    h, m = divmod(meanSeconds, 3600)
    m, s = divmod(m, 60)
    return('%02i:%02i:%02i' % (h, m, s))

print(meanTime(["15:00:00", "21:00:00"]))
# 18:00:00
print(meanTime(["23:00:00", "01:00:00"]))
# 00:00:00

【讨论】:

【参考方案6】:

我认为最好的办法是将所有这些值转换为秒数并对整个列表进行平均。我假设这些时间是mylist 中的字符串。

 time_list = map(lambda s: int(s[6:8]) + 60*(int(s[3:5]) + 60*int(s[0:2])), mylist)
 average = sum(time_list)/len(time_list)
 bigmins, secs = divmod(average, 60)
 hours, mins = divmod(bigmins, 60)
 print "%02d:%02d:%02d" % (hours, mins, secs)

这基本上是eumiro推荐的。第一行计算每个字符串的秒数。第二行对它们进行平均。接下来的两行计算出秒数/分钟数/小时数,第三行很好地格式化了输出。

【讨论】:

【参考方案7】:

对于已经提供的出色答案,可能有另一种方法,但它是针对特定情况的。例如,如果您有兴趣平均一天中人们上床睡觉的时间,通常是下午 6 点到早上 6 点之间的某个时间,您可以首先将小时和分钟转换为小数,这样 12:30 = 12.5 ,在那之后,您只需将 24 添加到估计平均值的时间范围内。对于睡眠情况,从 0:00 到 6:00 AM 之间的时间变为 24.0 和 30。现在您可以像往常一样估计平均值。最后,如果平均值大于 24,则只需再次减去 24 即可:

def hourtoDec(data):
    '''
    Transforms the hour string values in the list data
    to decimal. The format assumed is HH:mm.
    Values are transformed to float
    For example for 5:30pm the equivalent is 17.5 
    This funtion preserves NaN values
    '''
    dataOutput=[]
    for i in data:
        if not(pd.isnull(i)):
            if type(i)==type("a"):
                    h,m=i.split(':')
                    h=int(h)
                    m=int(m)
                    dataOutput.append(h+m/60.0)
            if isinstance(i, (np.float, float)):
                    dataOutput.append(i)
        else:
            dataOutput.append(i)
    return dataOutput



timestr=pd.DataFrame([ "2020-04-26T23:00:30.000", 
                      "2020-04-25T22:00:30.000", 
                      "2020-04-24T01:00:30.000", 
                      "2020-04-23T02:00:30.000"],columns=["timestamp"])
hours=timestr['timestamp'].apply(lambda x: ":".join(x.split("T")[1].split(":")[0:2]))
hoursDec=hourtoDec(hours)

times2=[]
for i in hoursDec:
    if i>=0 and i<6:
        times2.append(i+24)
    else:
        times2.append(i)

average=np.mean(times2)
if average>=24:
    average=average-24
print(average)

【讨论】:

以上是关于使用 python 从时间列表中创建平均值的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中创建一个函数,它将在 pandas 数据框中估算均值或中值

在 ggplot2 中创建显示平均值的分组条形图(我不想手动输入)

标头函数平均在 C 中给了我错误的结果。如果我调用我在标头中创建的“平均”函数,结果与预期不符

在R中创建连续热图

matplotlib / pyplot:绘制多个图的平均曲线

如何在 grafana 图表中创建趋势线