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
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.