【问题标题】:Get average time of day in SQLite from datetimes从日期时间获取 SQLite 中一天中的平均时间
【发布时间】:2023-03-26 19:29:01
【问题描述】:

我在 SQLite 中有时间以 '2012-02-21 00:00:00.000000' 的形式出现,并且想平均一天中的时间。日期无关紧要——只是时间。因此,例如,如果数据是:

'2012-02-18 20:00:00.000000' 
'2012-02-19 21:00:00.000000' 
'2012-02-20 22:00:00.000000' 
'2012-02-21 23:00:00.000000' 

20、21、22、23 的平均值应为 21.5 或 21:30(或美国晚上 9:30)。

Q1) 在 SQLite 的 SELECT 查询中是否有最好的方法来做到这一点?

但更困难的是:如果一个或多个日期时间跨过午夜怎么办?他们肯定会在我的数据集中。示例:

'2012-02-18 22:00:00.000000'
'2012-02-19 23:00:00.000000' 
'2012-02-21 01:00:00.000000'

现在平均值似乎应该是 (22 + 23 + 1)/3 = 15.33 或 15:20(下午 3:20)。但这会歪曲数据,因为这些事件都发生在晚上,从 22:00 到 01:00(晚上 10 点到凌晨 1 点)。实际上,更好的方法是将它们平均为 (22 + 23 + 25)/3 = 23.33 或 23:20(晚上 11:20)。

Q2) 我应该对我的 SELECT 查询做些什么来考虑到这一点,还是我必须用 Python 编写代码?

【问题讨论】:

  • 你需要更好地定义你的“平均”。似乎有时您希望日期很重要,但有时您不希望。
  • @muistooshort 你能解释一下是什么让你这么说吗?除非我误解了我的需求(也许我是),否则我不希望日期变得重要。事实上,这些字段也可能没有日期部分,所以它们可能像“22:00:00”、“23:00:00”和“01:00:00”,我想要平均值那些在 23:20 之前出来的人。
  • 您为“平均值”提供了两个可能的值:15:20 和 23:20。第一个只查看时间,第二个使用日期转换知识来避免对小时处理进行 mod-24 调整。
  • @muistooshort 好吧,我所追求的只是 23:20。问题是,我不会拥有每个日期的所有数据(会有差距)。因此,如果我使用您的原始代码,日期信息将很重要,但如果我的值之间有几天的差距,则不会给我(在此示例中)23:20。我宁愿进行 mod-24 调整,只是不确定如何最好地在 SQLITE 或 Python 中进行调整。这更有意义吗?感谢您的耐心等待。
  • 您是否考虑过您只是从有限的样本中得出错误结论的可能性?如果您查看整个数据集,异常值问题可能会消失。

标签: python sql sqlite time average


【解决方案1】:

你真正想要计算什么?

  • 日期时间(或 1 天内的时间)通常表示为实数
  • 24 小时制的时间坐标是复数,但是
  • 时间的实数表示的平均值会给您带来可疑的结果...

我不知道你想如何处理像 [1:00, 13:00] 这样的边缘情况,但让我们考虑以下 示例:[01:30, 06:30, @ 987654326@,15:3016:1516:4517:10]

我建议在 Python 中实现这个算法:

  1. 将时间转换为复数 - 例如在半径 = 1 的圆上计算它们的坐标
  2. 使用向量加法计算平均值
  3. 将结果向量角度转换为分钟 + 计算此结果的相关性(例如,由于舍入误差,无论角度如何计算,[1:00, 13:00] 的平均值的相关性都应为 0)时间>
import math
def complex_average(minutes):
    # first convert the times from minutes (0:00 - 23:59) to radians
    # so we get list for quasi polar coordinates (1, radians)
    # (no point in rotating/flipping to get real polar coordinates)
    # 180° = 1/2 day = 24*60/2 minutes
    radians = [t*math.pi/(24*60/2) for t in minutes]
    xs = []
    ys = []
    for r in radians:
        # convert polar coordinates (1, r) to cartesian (x, y)
        # the vectors start at (0, 0) and end in (x, y)
        x, y = (math.cos(r), math.sin(r))
        xs.append(x)
        ys.append(y)

    # result vector = vector addition
    sum_x, sum_y = (sum(ys), sum(xs))

    # convert result vector coordinates to radians, then to minutes
    # note the cumulative ROUNDING ERRORS, however
    result_radians = math.atan2(sum_x, sum_y)
    result_minutes = int(result_radians / math.pi * (24*60/2))
    if result_minutes < 0:
        result_minutes += 24*60

    # relevance = magnitude of the result vector / number of data points
    # (<0.0001 means that all vectors cancel each other, e.g. [1:00, 13:00]
    #  => result_minutes would be random due to rounding error)
    # FYI: standart_deviation = 6*60 - 6*60*relevance
    relevance = round(math.sqrt(sum_x**2 + sum_y**2) / len(minutes), 4)

    return result_minutes, relevance

并像这样测试它:

# let's say the select returned a bunch of integers in minutes representing times
selected_times = [90, 390, 800, 930, 975, 1005, 1030]
# or create other test data:
#selected_times = [hour*60 for hour in [23,22,1]]

complex_avg_minutes, relevance = complex_average(selected_times)
print("complex_avg_minutes = {:02}:{:02}".format(complex_avg_minutes//60,
                                                 complex_avg_minutes%60),
      "(relevance = {}%)".format(int(round(relevance*100))))

simple_avg = int(sum(selected_times) / len(selected_times))
print("simple_avg = {:02}:{:02}".format(simple_avg//60,
                                        simple_avg%60))

hh_mm = ["{:02}:{:02}".format(t//60, t%60) for t in selected_times]
print("\ntimes = {}".format(hh_mm))

我的例子的输出:

complex_avg_minutes = 15:45 (relevance = 44%)
simple_avg = 12:25

【讨论】:

  • 或者也许一维有限空间会比复数更好地表示 - 因为只有圆圈上的点代表时间点,但向量结果在圆圈内:(
【解决方案2】:

我不确定你是否可以平均日期。

我要做的是获取行值和固定日期之间小时差的平均值,然后将该平均值添加到固定日期。使用分钟可能会导致 int 溢出并需要进行一些类型转换

有点……

select dateadd(hh,avg(datediff(hh,getdate(),myrow)),getdate()) 
from mytable;

【讨论】:

    【解决方案3】:

    如果我理解正确,你想得到从午夜开始的平均距离?

    这个怎么样?

    SELECT SUM(mins) / COUNT(*) from
    ( SELECT
        CASE 
        WHEN strftime('%H', t) * 1 BETWEEN 0 AND 11 
        THEN (strftime('%H', t)) * 60 + strftime('%M', t)
        ELSE strftime('%H', t) * 60 + strftime('%M', t) - 24 * 60
        END mins
      FROM timestamps
    );
    

    所以我们计算从午夜开始的分钟偏移量:中午之后我们得到一个负值,中午之前我们得到一个正值。第一行对它们进行平均,并在几分钟内给我们一个结果。将其转换回 hh:mm 时间留作“学生练习”;-)

    【讨论】:

    • (11:00, 12:00, 13:00) 的平均值呢? 00:00 肯定不是预期的结果 ;))
    • @deathApril - 正如我所指定的,我猜想该要求与上下文相关,并假设时间集中在午夜左右。如果是这样,这是一个合理的解决方案。如果不是,那么正如您指出的那样,这不是很合适;-)
    【解决方案4】:

    站点罗塞塔代码有一个task 和关于这个主题的代码,在研究中我发现了这个维基百科link。查看谈话/讨论页面,了解适用性等方面的讨论。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多