Search code examples
pythondatetimepyephem

calculate sunrise and sunset times from a datetime index using ephem in Python


I have a daily time series with DateTime index. I want to calculate the sunrise and sunset times for each day in the DataFrame. The result will be presented in columns rise and set. Below is my script using pyephem:

import ephem
import datetime

AliceS = ephem.Observer()
AliceS.lat = '-23.762'
AliceS.lon = '133.875'

AliceS.date = df.index

sun = ephem.Sun()

df['rise'] = ephem.localtime(AliceS.next_rising(sun))
df['set'] = ephem.localtime(AliceS.next_setting(sun))

This raises

ValueError: dates must be initialized from a number, string, tuple, or datetime

I believe that the cause of the error is AliceS.date = df.index, but I don't know how to fix it.

Below is an example of the datetime index:

DateTime
2016-04-02
2016-04-03
2016-04-04
2016-04-07
2016-04-08

Solution

  • From the front page of the docs:

    PyEphem does not interoperate with NumPy and so is awkward to use in a modern IPython Notebook.

    This basically means that the next_rising and next_setting methods can only operate on scalars. The quick and dirty solution is to write a loop to convert each element of your index to a compatible format and compute the values that way:

    import ephem
    import datetime
    
    AliceS = ephem.Observer()
    AliceS.lat = '-23.762'
    AliceS.lon = '133.875'
    
    sun = ephem.Sun()
    
    def get_time(obs, obj, func):
        func = getattr(obs, func)
        def inner(date)
            obs.date = date
            return ephem.localtime(func(obj))
        return inner
    
    df['rise'] = pd.Series(df.index).apply(get_time(AliceS, sun, 'next_rising'))
    df['set'] = pd.Series(df.index).apply(get_time(AliceS, sun, 'next_setting'))
    

    Don't let the compact(-ish) notation fool you, apply is still just a for loop.

    A much better solution would be to follow the advice in the docs:

    I recommend using Skyfield instead of PyEphem if it’s possible for your new project to do so! (The only thing missing at this point is predicting positions from Kelperian orbital elements for comets and asteroids.)

    Here is a link to Skyfield. It is available via normal channels such as pypi and GitHub.