From 7cc44592eb0c1014f1cd24a8ff5978a14993fa6e Mon Sep 17 00:00:00 2001 From: Christian Lamprecht Date: Tue, 29 Jun 2021 00:08:59 +0200 Subject: [PATCH] Meteostat 1.5.1 (#54) * Meteostat 1.5.1 * Update README * Update README --- README.md | 2 ++ examples/daily/compare.py | 13 ++--------- meteostat/__init__.py | 2 +- meteostat/interface/daily.py | 39 +++++++++++++++++++++++++++------ meteostat/interface/hourly.py | 35 ++++++++++++++++++++++++----- meteostat/interface/monthly.py | 37 ++++++++++++++++++++++++++----- meteostat/interface/point.py | 38 ++++++++++++++++++++++---------- meteostat/interface/stations.py | 2 +- setup.py | 2 +- 9 files changed, 126 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 41ddbe5..dc98862 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ The Meteostat Python library provides a simple API for accessing open weather an Among the data sources are national weather services like the National Oceanic and Atmospheric Administration (NOAA) and Germany's national meteorological service (DWD). +Are you looking for a **hosted solution**? Try our [JSON API](https://rapidapi.com/meteostat/api/meteostat/). + ## Installation The Meteostat Python package is available through [PyPI](https://pypi.org/project/meteostat/): diff --git a/examples/daily/compare.py b/examples/daily/compare.py index 0777738..f8b9ba3 100644 --- a/examples/daily/compare.py +++ b/examples/daily/compare.py @@ -10,22 +10,14 @@ from datetime import datetime import matplotlib.pyplot as plt -from meteostat import Stations, Daily - -# Get weather stations by WMO ID -stations = Stations() -stations = stations.id('wmo', ('71624', '72295', '68816', '94767')) -stations = stations.fetch() - -# Get names of weather stations -names = stations['name'].to_list() +from meteostat import Daily # Time period start = datetime(2019, 1, 1) end = datetime(2019, 12, 31) # Get daily data -data = Daily(stations, start, end) +data = Daily(['71624', '72295', '68816', '94767'], start, end) data = data.fetch() # Plot chart @@ -33,5 +25,4 @@ legend=True, ylabel='Avg. Daily Temperature °C', title='Average Temperature Report for 2019') -plt.legend(names) plt.show() diff --git a/meteostat/__init__.py b/meteostat/__init__.py index 566681d..800f2da 100644 --- a/meteostat/__init__.py +++ b/meteostat/__init__.py @@ -12,7 +12,7 @@ """ __appname__ = 'meteostat' -__version__ = '1.5.0' +__version__ = '1.5.1' from .interface.base import Base from .interface.timeseries import Timeseries diff --git a/meteostat/interface/daily.py b/meteostat/interface/daily.py index 4e21b0b..6b01ffe 100644 --- a/meteostat/interface/daily.py +++ b/meteostat/interface/daily.py @@ -170,9 +170,39 @@ def _resolve_point( if self._stations.size == 0 or self._data.size == 0: return None + def adjust_temp(data: pd.DataFrame): + """ + Adjust temperature-like data based on altitude + """ + + data.loc[data['tavg'] != np.NaN, 'tavg'] = data['tavg'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + data.loc[data['tmin'] != np.NaN, 'tmin'] = data['tmin'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + data.loc[data['tmax'] != np.NaN, 'tmax'] = data['tmax'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + + return data + if method == 'nearest': - self._data = self._data.groupby( + if adapt_temp: + + # Join elevation of involved weather stations + data = self._data.join( + stations['elevation'], on='station') + + # Adapt temperature-like data based on altitude + data = adjust_temp(data) + + # Drop elevation & round + data = data.drop('elevation', axis=1).round(1) + + else: + + data = self._data + + self._data = data.groupby( pd.Grouper(level='time', freq=self._freq)).agg('first') else: @@ -183,12 +213,7 @@ def _resolve_point( # Adapt temperature-like data based on altitude if adapt_temp: - data.loc[data['tavg'] != np.NaN, 'tavg'] = data['tavg'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) - data.loc[data['tmin'] != np.NaN, 'tmin'] = data['tmin'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) - data.loc[data['tmax'] != np.NaN, 'tmax'] = data['tmax'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) + data = adjust_temp(data) # Exclude non-mean data & perform aggregation excluded = data['wdir'] diff --git a/meteostat/interface/hourly.py b/meteostat/interface/hourly.py index 6f918df..9e81799 100644 --- a/meteostat/interface/hourly.py +++ b/meteostat/interface/hourly.py @@ -238,9 +238,37 @@ def _resolve_point( if self._stations.size == 0 or self._data.size == 0: return None + def adjust_temp(data: pd.DataFrame): + """ + Adjust temperature-like data based on altitude + """ + + data.loc[data['temp'] != np.NaN, 'temp'] = data['temp'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + data.loc[data['dwpt'] != np.NaN, 'dwpt'] = data['dwpt'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + + return data + if method == 'nearest': - self._data = self._data.groupby(pd.Grouper( + if adapt_temp: + + # Join elevation of involved weather stations + data = self._data.join( + stations['elevation'], on='station') + + # Adapt temperature-like data based on altitude + data = adjust_temp(data) + + # Drop elevation & round + data = data.drop('elevation', axis=1).round(1) + + else: + + data = self._data + + self._data = data.groupby(pd.Grouper( level='time', freq=self._freq)).agg('first') else: @@ -251,10 +279,7 @@ def _resolve_point( # Adapt temperature-like data based on altitude if adapt_temp: - data.loc[data['temp'] != np.NaN, 'temp'] = data['temp'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) - data.loc[data['dwpt'] != np.NaN, 'dwpt'] = data['dwpt'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) + data = adjust_temp(data) # Exclude non-mean data & perform aggregation excluded = data[['wdir', 'coco']] diff --git a/meteostat/interface/monthly.py b/meteostat/interface/monthly.py index 57774d4..bb1e0bd 100644 --- a/meteostat/interface/monthly.py +++ b/meteostat/interface/monthly.py @@ -170,8 +170,38 @@ def _resolve_point( if self._stations.size == 0 or self._data.size == 0: return None + def adjust_temp(data: pd.DataFrame): + """ + Adjust temperature-like data based on altitude + """ + + data.loc[data['tavg'] != np.NaN, 'tavg'] = data['tavg'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + data.loc[data['tmin'] != np.NaN, 'tmin'] = data['tmin'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + data.loc[data['tmax'] != np.NaN, 'tmax'] = data['tmax'] + \ + ((2 / 3) * ((data['elevation'] - alt) / 100)) + + return data + if method == 'nearest': + if adapt_temp: + + # Join elevation of involved weather stations + data = self._data.join( + stations['elevation'], on='station') + + # Adapt temperature-like data based on altitude + data = adjust_temp(data) + + # Drop elevation & round + data = data.drop('elevation', axis=1).round(1) + + else: + + data = self._data + self._data = self._data.groupby( pd.Grouper(level='time', freq=self._freq)).agg('first') @@ -183,12 +213,7 @@ def _resolve_point( # Adapt temperature-like data based on altitude if adapt_temp: - data.loc[data['tavg'] != np.NaN, 'tavg'] = data['tavg'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) - data.loc[data['tmin'] != np.NaN, 'tmin'] = data['tmin'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) - data.loc[data['tmax'] != np.NaN, 'tmax'] = data['tmax'] + \ - ((2 / 3) * ((data['elevation'] - alt) / 100)) + data = adjust_temp(data) # Exclude non-mean data & perform aggregation excluded = data['wdir'] diff --git a/meteostat/interface/point.py b/meteostat/interface/point.py index 3004db4..f8890e4 100644 --- a/meteostat/interface/point.py +++ b/meteostat/interface/point.py @@ -20,7 +20,7 @@ class Point: """ # The interpolation method (weighted or nearest) - method: str = 'weighted' + method: str = 'nearest' # Maximum radius for nearby stations radius: int = 35000 @@ -41,7 +41,7 @@ class Point: weight_alt: float = 0.4 # The list of weather stations - stations: pd.Index = None + _stations: pd.Index = None # The latitude _lat: float = None @@ -83,8 +83,9 @@ def get_stations(self, freq: str = None, start: datetime = None, # Captue unfiltered weather stations unfiltered = stations.fetch() - unfiltered = unfiltered[abs(self._alt - - unfiltered['elevation']) <= self.alt_range] + if self.alt_range: + unfiltered = unfiltered[abs(self._alt - + unfiltered['elevation']) <= self.alt_range] # Apply inventory filter if freq and start and end: @@ -92,8 +93,9 @@ def get_stations(self, freq: str = None, start: datetime = None, # Apply altitude filter stations = stations.fetch() - stations = stations[abs(self._alt - - stations['elevation']) <= self.alt_range] + if self.alt_range: + stations = stations[abs(self._alt - + stations['elevation']) <= self.alt_range] # Fill up stations selected: int = len(stations.index) @@ -102,15 +104,18 @@ def get_stations(self, freq: str = None, start: datetime = None, unfiltered.head( self.max_count - selected)) - # Calculate score values - stations['score'] = ((1 - (stations['distance'] / self.radius)) * self.weight_dist) + ( - (1 - (abs(self._alt - stations['elevation']) / self.alt_range)) * self.weight_alt) + # Score values + if self.radius: - # Sort by score (descending) - stations = stations.sort_values('score', ascending=False) + # Calculate score values + stations['score'] = ((1 - (stations['distance'] / self.radius)) * self.weight_dist) + ( + (1 - (abs(self._alt - stations['elevation']) / self.alt_range)) * self.weight_alt) + + # Sort by score (descending) + stations = stations.sort_values('score', ascending=False) # Capture result - self.stations = stations.index[:self.max_count] + self._stations = stations.index[:self.max_count] return stations.head(self.max_count) @@ -122,3 +127,12 @@ def alt(self) -> int: # Return altitude return self._alt + + @property + def stations(self) -> pd.Index: + """ + Returns the point's weather stations + """ + + # Return weather stations + return self._stations diff --git a/meteostat/interface/stations.py b/meteostat/interface/stations.py index 849da8b..5aa070f 100644 --- a/meteostat/interface/stations.py +++ b/meteostat/interface/stations.py @@ -151,7 +151,7 @@ def distance(lat1, lon1, lat2, lon2): lat, lon, temp._data['latitude'], temp._data['longitude']) # Filter by radius - if radius is not None: + if radius: temp._data = temp._data[temp._data['distance'] <= radius] # Sort stations by distance diff --git a/setup.py b/setup.py index cfa9d9b..9adba2e 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ # Setup setup( name='meteostat', - version='1.5.0', + version='1.5.1', author='Meteostat', author_email='info@meteostat.net', description='Access and analyze historical weather and climate data with Python.',