Python 存储预计算日出/日落数据的最佳策略?

Python 存储预计算日出/日落数据的最佳策略?,python,google-app-engine,python-2.7,Python,Google App Engine,Python 2.7,我正在开发一个基于NDB的Google App Engine应用程序,它需要跟踪大量(约2000个)固定地点的昼夜周期。因为纬度和经度永远不会改变,所以我可以使用类似PyEphem的东西提前计算它们。我用的是NDB。在我看来,可能的策略是: 要将一年的日出预计算为datetime对象,请将 将它们放入一个列表中,对列表进行pickle并将其放入pickle属性中 ,但将列表放入JsonProperty中 使用DateTimeProperty并设置repeated=True 现在,我希望对下一个s

我正在开发一个基于NDB的Google App Engine应用程序,它需要跟踪大量(约2000个)固定地点的昼夜周期。因为纬度和经度永远不会改变,所以我可以使用类似PyEphem的东西提前计算它们。我用的是NDB。在我看来,可能的策略是:

  • 要将一年的日出预计算为datetime对象,请将 将它们放入一个列表中,对列表进行pickle并将其放入pickle属性中

  • ,但将列表放入JsonProperty中

  • 使用DateTimeProperty并设置repeated=True

  • 现在,我希望对下一个sunrise/sunset属性进行索引,但是可以从列表中弹出并将其放入自己的DateTimeProperty中,这样我就可以定期使用查询来确定哪些位置已更改为周期的不同部分。整个列表不需要编制索引

    有人知道这三种方法在索引和CPU负载方面的相对工作量吗?repeated=True对索引是否有影响

    谢谢,
    Dave

    针对2000个不可变的数据点—只需在实例启动或首次使用时计算它们,然后将其保存在内存中。这将是最便宜和最快的。

    我要说的是预计算这些结构,并将它们输出到硬编码的python结构中,然后保存在生成的python文件中

    只要将这些结构作为实例启动的一部分读入内存即可


    根据您的描述,没有理由在运行时计算这些值,也没有理由将其存储在数据存储中,因为这会带来相关的成本以及RPC的一些延迟。

    答案建议“仅在实例启动时计算它们”或“预计算这些结构并将其输出到硬编码的python结构”似乎忽略了存储一年日出所需的times-365乘数,或者如果在实例启动时进行计算,则忽略了times-2000乘数。使用pyEphem,2000日出和日落需要两秒钟以上的时间进行计算。在源代码中存储2000个位置的一年日出和日落可能需要20米以上的时间egabytes。如果数字被有效地pickle,则需要2*365*2000*8=1168000字节

    一种更快更好的方法是,根据一个位置的时间和其他位置的时间,建立一个最小二乘模型。这将使总使用空间减少大约70倍,如下所述

    首先,如果A点和B点位于同一纬度,并且具有相似的高度和地平线参数,那么A点的日出与B点的日出相比,在一个恒定的时间偏移处发生。例如,如果A点位于B点以西15度,则A点的日出比B点晚一个小时。第二,如果A点、B点、C点位于同一经度和低纬度,则日出时间为ti一个点上的mes可以相当准确地作为其他两个点的线性组合进行计算。在高纬度地区或为了获得更高的精度,可以使用多条时间曲线的线性组合。第三,可以使用3月20日,即第二天,a点的日出时间作为标准化点,因此所有计算都可以标准化为相同纬度托德

    下表显示了使用四条时间曲线的线性组合得到的精度结果。对于距离赤道46°的经度,结果保持在半秒钟以内。对于48°到60°的经度,结果保持在5秒钟以内。在64°时,结果误差可能高达2分钟,在65°时,误差可能高达6分钟。但是对于大多数实际用途来说,时间可能已经足够好了。请注意,在66°时,下面显示的程序会崩溃,因为它不会处理Pypehem抛出的异常;“AlwaysUpError:‘Sun’在2013/6/14 07:20:15仍在地平线上”,即使66°低于,66.5622°N

    很容易修改程序,使其使用所需的任意多条时间曲线(请参阅程序中的各种
    lata=…
    语句),给出所需的精度,但代价是存储更多曲线和更多系数。当然,可以改变模型以使用时间曲线的子集;例如,可以存储10条曲线,并根据离任何给定目标纬度最近的4个纬度进行计算。但是,对于此演示程序,此类改进不适用到位

    Lat.  0.0:  Error range: -0.000000 to 0.000000 seconds
    Lat.  5.0:  Error range: -0.370571 to 0.424092 seconds
    Lat. 10.0:  Error range: -0.486193 to 0.557997 seconds
    Lat. 15.0:  Error range: -0.414288 to 0.477041 seconds
    Lat. 20.0:  Error range: -0.213614 to 0.247057 seconds
    Lat. 25.0:  Error range: -0.065826 to 0.056358 seconds
    Lat. 30.0:  Error range: -0.382425 to 0.323623 seconds
    Lat. 35.0:  Error range: -0.585914 to 0.488351 seconds
    Lat. 40.0:  Error range: -0.490303 to 0.400563 seconds
    Lat. 45.0:  Error range: -0.164706 to 0.207415 seconds
    Lat. 47.0:  Error range: -0.590103 to 0.756647 seconds
    Lat. 48.0:  Error range: -0.852844 to 1.102608 seconds
    Lat. 50.0:  Error range: -1.478688 to 1.940351 seconds
    Lat. 55.0:  Error range: -3.342506 to 4.696076 seconds
    Lat. 60.0:  Error range: -0.000002 to 0.000003 seconds
    Lat. 61.0:  Error range: -7.012057 to 4.273954 seconds
    Lat. 62.0:  Error range: -21.374033 to 12.347188 seconds
    Lat. 63.0:  Error range: -51.872753 to 27.853411 seconds
    Lat. 64.0:  Error range: -124.000365 to 59.661029 seconds
    Lat. 65.0:  Error range: -351.425224 to 139.656187 seconds
    
    使用上述方法,对于2000个位置中的每个位置,您需要存储五个浮点数:3月20日日出的时间,以及四条时间曲线的四个乘数系数。(前面提到的70倍减少是因为每个位置存储5个数字,而不是365个数字。)对于每个时间曲线,存储365个数字,条目i是日出时间与3月20日的时间差。存储四条时间曲线使用的空间是存储2000条时间曲线的1/500,因此曲线存储空间主要是乘数系数的空间

    在我给出使用scipy.optimize.leastsq求解系数的程序之前,这里有两个代码片段,可以在ipython解释器中用于制作精度表和绘制用于可视化错误的绘图

    import sunrise as sr
    for lat in range(0, 65, 5):
        sr.lsr(lat, -110, 2013, 4)
    
    上面给出了前面显示的大部分错误表。
    lsr
    的第三个参数称为
    daySkip
    ,值4使
    lsr
    每四天(即一年中只有大约90天)工作一次,以便更快地进行测试。使用
    sr.lsr(lat,-1102013,1)
    产生类似的结果,但需要四倍的时间

    sr.plotData(15,1./(24*3600))
    
    上面告诉sunrise.plotData绘制所有内容(要近似的sunrise数据;模型得到的近似值;以秒为单位缩放的残差;以及基数曲线)

    该程序如下所示。请注意,它主要针对北半球经度进行了测试。如果时间曲线足够对称,该程序将按原样处理南半球经度
    #!/usr/bin/python
    import ephem, numpy, scipy, scipy.optimize
    
    # Make a set of observers (observation points)
    def observers(lata, lona):
        def makeIter(x):
            if hasattr(x, '__iter__'):
                return x
            return [x]
        lata, lona = makeIter(lata), makeIter(lona)
        arr = []
        for lat in lata:
            for lon in lona:
                o = ephem.Observer()
                o.lat, o.lon, o.elevation, o.temp = str(lat), str(lon), 1400, 0
                arr.append(o)
        return tuple(arr)
    
    # Make a year of data for an observer, equinox-relative
    def riseData(observr, year, skip):
        yy = ephem.Date('{} 00:00'.format(year))
        rr = numpy.arange(0.0, 366.0, skip)
        springEquinox = 78
        observr.date = ephem.Date(yy + springEquinox)
        seDelta = observr.previous_rising(ephem.Sun()) - yy - springEquinox + 1
        for i, day in enumerate(range(0, 366, skip)):
            observr.date = ephem.Date(yy + day)
            t = observr.previous_rising(ephem.Sun()) - yy - day + 1 - seDelta
            rr[i] = t
        return numpy.array(rr)
    
    # Make a set of time curves
    def makeRarSet(lata, lona, year, daySkip):
        rar=[]
        for o in observers(lata, lona):
            r = riseData(o, year, daySkip)
            rar.append(r)
        x = numpy.arange(0., 366., daySkip)
        return (x, rar)
    
    # data() is an object that stores curves + results
    def data(s):
        return data.ss[s]
    
    # Initialize data.ss
    def setData(lata, lona, year, daySkip):
        x, rar = makeRarSet(lata, lona, year, daySkip)
        data.ss = rar
    
    # Compute y values from model, given vector x and given params in p
    def yModel(x, p):
        t = numpy.zeros(len(x))
        for i in range(len(p)):
            t += p[i] * data(i)
        return t
    
    # Compute residuals, given params in p and data in x, y vectors.
    # x = independent var,  y = dependent = observations
    def residuals(p, y, x):
        err = y - yModel(x, p)
        return err
    
    # Compute least squares result
    def lsr(lat, lon, year, daySkip):
        latStep = 13.
        lata = numpy.arange(0., 66.4, latStep)
        lata = [ 88 * (1 - 1.2**-i) for i in range(8)]
        l, lata, lstep, ldown = 0, [], 20, 3
        l, lata, lstep, ldown = 0, [], 24, 4
        while l < 65:
            lata.append(l); l += lstep; lstep -= ldown
        #print 'lata =', lata
        setData(lata, lon, year, daySkip)
        x, ya = makeRarSet(lat, lon, year, daySkip)
        x, za = makeRarSet(lat, 10+lon, year, daySkip)
        data.ss.append(za[0])
        y = ya[0]
        pini = [(0 if abs(lat-l)>latStep else 0.5) for l in lata]
        pars = scipy.optimize.leastsq(residuals, pini, args=(y, x))
        data.x, data.y, data.pv = x, y, yModel(x, pars[0])
        data.par, data.err = pars, residuals(pars[0], y, x)
        #print 'pars[0] = ', pars[0]
        variance = numpy.inner(data.err, data.err)/len(y)
        #print 'variance:', variance
        sec = 1.0/(24*60*60)
        emin, emax = min(data.err), max(data.err)
        print ('Lat. {:4.1f}:  Error range: {:.6f} to {:.6f} seconds'.format(lat, emin/sec, emax/sec))
    
    def plotData(iopt, emul):
        import matplotlib.pyplot as plt
        plt.clf()
        x = data.x
        if iopt == 0:
            iopt = 15
            emul = 1
        if iopt & 1:
            plt.plot(x, data.y)
            plt.plot(x, data.y + 0.001)
            plt.plot(x, data.y - 0.001)
        if iopt & 2:
            plt.plot(x, data.pv)
        if iopt & 4:
            plt.plot(x, emul*data.err)
        if iopt & 8:
            for ya in data.ss:
                plt.plot(x, ya)
        plt.show()