一个快速的,向量化Python版本的suncalc.js
项目描述
suncalc-py
一个快速的,向量化Python实现,用于计算给定位置和时间的太阳位置和阳光阶段(日出、日落、黄昏等时间)的suncalc.js
。
虽然存在其他类似的库,但我最初没有遇到任何满足我要求的库,即公开授权、向量化且易于使用 1。
安装
pip install suncalc
使用
示例
suncalc
旨在与单个值和值的数组一起使用。
首先,导入模块
from suncalc import get_position, get_times
from datetime import datetime
目前有两种方法:get_position
,用于获取给定日期和位置的太阳方位角和高度,以及get_times
,用于获取给定日期和位置的阳光阶段。
date = datetime.now()
lon = 20
lat = 45
get_position(date, lon, lat)
# {'azimuth': -0.8619668996997687, 'altitude': 0.5586446727994595}
get_times(date, lon, lat)
# {'solar_noon': Timestamp('2020-11-20 08:47:08.410863770'),
# 'nadir': Timestamp('2020-11-19 20:47:08.410863770'),
# 'sunrise': Timestamp('2020-11-20 03:13:22.645455322'),
# 'sunset': Timestamp('2020-11-20 14:20:54.176272461'),
# 'sunrise_end': Timestamp('2020-11-20 03:15:48.318936035'),
# 'sunset_start': Timestamp('2020-11-20 14:18:28.502791748'),
# 'dawn': Timestamp('2020-11-20 02:50:00.045539551'),
# 'dusk': Timestamp('2020-11-20 14:44:16.776188232'),
# 'nautical_dawn': Timestamp('2020-11-20 02:23:10.019832520'),
# 'nautical_dusk': Timestamp('2020-11-20 15:11:06.801895264'),
# 'night_end': Timestamp('2020-11-20 01:56:36.144269287'),
# 'night': Timestamp('2020-11-20 15:37:40.677458252'),
# 'golden_hour_end': Timestamp('2020-11-20 03:44:46.795967773'),
# 'golden_hour': Timestamp('2020-11-20 13:49:30.025760010')}
这些方法也适用于数组数据,并且由于实现是向量化,因此比Python中的for循环快得多。
import pandas as pd
df = pd.DataFrame({
'date': [date] * 10,
'lon': [lon] * 10,
'lat': [lat] * 10
})
pd.DataFrame(get_position(df['date'], df['lon'], df['lat']))
# azimuth altitude
# 0 -1.485509 -1.048223
# 1 -1.485509 -1.048223
# ...
pd.DataFrame(get_times(df['date'], df['lon'], df['lat']))['solar_noon']
# 0 2020-11-20 08:47:08.410863872+00:00
# 1 2020-11-20 08:47:08.410863872+00:00
# ...
# Name: solar_noon, dtype: datetime64[ns, UTC]
如果您想将这些数据合并回您的DataFrame
,可以使用pd.concat
times = pd.DataFrame(get_times(df['date'], df['lon'], df['lat']))
pd.concat([df, times], axis=1)
API
get_position
计算给定日期和经纬度的太阳位置(方位角和高度)
date
(datetime
或 pandas的datetime系列):查找太阳位置的日期和时间。日期时间必须为UTC。lng
(float
或float
的 numpy 数组):查找太阳位置的长经度lat
(float
或float
的 numpy 数组):查找太阳位置的纬度
返回一个包含两个键的 dict
:azimuth
和 altitude
。如果输入值是单个值,则 dict
的值将是浮点数。否则,它们将是浮点数的 numpy 数组。
get_times
-
date
(datetime
或 pandas 日期时间的系列):查找阳光阶段的时间和日期。必须使用 UTC。 -
lng
(float
或float
的 numpy 数组):查找阳光阶段的经度 -
lat
(float
或float
的 numpy 数组):查找阳光阶段的纬度 -
height
(float
或float
的 numpy 数组,默认0
):观测者的高度(米) -
times
(Iterable[Tuple[float, str, str]]
):一个可迭代的对象,定义了地平线以上的角度和自定义阳光阶段字符串。默认值是# (angle, morning name, evening name) DEFAULT_TIMES = [ (-0.833, 'sunrise', 'sunset'), (-0.3, 'sunrise_end', 'sunset_start'), (-6, 'dawn', 'dusk'), (-12, 'nautical_dawn', 'nautical_dusk'), (-18, 'night_end', 'night'), (6, 'golden_hour_end', 'golden_hour') ]
返回一个 dict
,其键为 solar_noon
、nadir
以及 times
参数中传递的任何键。如果输入值是单个值,则 dict
的值将是 datetime.datetime
类型(如果您已安装 pandas,则为 pd.Timestamp
,它是 datetime.datetime
的子类,因此与 datetime.datetime
兼容)。否则,它们将是 pandas DateTime
系列。返回的时间将使用 UTC。
基准测试
此基准测试旨在证明向量化实现几乎比 Python 中的 for 循环快 100 倍。
首先设置一个包含随机数据的 DataFrame
。这里我创建了 100,000 行。
from suncalc import get_position, get_times
import pandas as pd
def random_dates(start, end, n=10):
"""Create an array of random dates"""
start_u = start.value//10**9
end_u = end.value//10**9
return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')
start = pd.to_datetime('2015-01-01')
end = pd.to_datetime('2018-01-01')
dates = random_dates(start, end, n=100_000)
lons = np.random.uniform(low=-179, high=179, size=(100_000,))
lats = np.random.uniform(low=-89, high=89, size=(100_000,))
df = pd.DataFrame({'date': dates, 'lat': lats, 'lon': lons})
然后两种方式计算 SunCalc.get_position
:第一种使用向量化实现,第二种使用 df.apply
,这相当于 for 循环。第一种比第二种快超过 100倍。
%timeit get_position(df['date'], df['lon'], df['lat'])
# 41.4 ms ± 437 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df.apply(lambda row: get_position(row['date'], row['lon'], row['lat']), axis=1)
# 4.89 s ± 184 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
同样,两种方式计算 SunCalc.get_times
:第一种使用向量化实现,第二种使用 df.apply
。第一种比第二种快 2800倍!这里的一些差异在于,在底层,非向量化方法使用 pd.to_datetime
,而向量化实现使用 np.astype('datetime64[ns, UTC]')
。pd.to_datetime
真的非常慢!!
%timeit get_times(df['date'], df['lon'], df['lat'])
# 55.3 ms ± 1.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%time df.apply(lambda row: get_times(row['date'], row['lon'], row['lat']), axis=1)
# CPU times: user 2min 33s, sys: 288 ms, total: 2min 34s
# Wall time: 2min 34s
1: pyorbital
看起来很棒,但许可证是 GPL3;pysolar
也是 GPL3 许可证;pyEphem
是 LGPL3 许可证。 suncalcPy
是 suncalc.js
的另一个端口,许可证是 MIT,但未使用 Numpy,因此没有向量化。我最近发现了 sunpy
和 astropy
,两者都可能工作,但最初我没有看到它们,而且它们看起来对这个简单任务来说相当复杂...
变更日志
[0.1.3] - 2023-04-18
- 确保 pandas 2.0 兼容性(修复日期时间的整型转换)
[0.1.2] - 2020-12-02
- 在传递给
datetime.utcfromtimestamp
之前尝试捕获 NaN
[0.1.1] - 2020-11-20
- 通过添加
MANIFEST.in
修复 PyPI 安装 - 更新文档
[0.1.0] - 2020-11-19
- 在 PyPI 上进行初始发布