Skip to content

Commit

Permalink
Meteostat 1.5.1 (#54)
Browse files Browse the repository at this point in the history
* Meteostat 1.5.1

* Update README

* Update README
  • Loading branch information
clampr committed Jun 28, 2021
1 parent 382bf27 commit 7cc4459
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 44 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/):
Expand Down
13 changes: 2 additions & 11 deletions examples/daily/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,19 @@

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
data.unstack('station')['tavg'].plot(
legend=True,
ylabel='Avg. Daily Temperature °C',
title='Average Temperature Report for 2019')
plt.legend(names)
plt.show()
2 changes: 1 addition & 1 deletion meteostat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"""

__appname__ = 'meteostat'
__version__ = '1.5.0'
__version__ = '1.5.1'

from .interface.base import Base
from .interface.timeseries import Timeseries
Expand Down
39 changes: 32 additions & 7 deletions meteostat/interface/daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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']
Expand Down
35 changes: 30 additions & 5 deletions meteostat/interface/hourly.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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']]
Expand Down
37 changes: 31 additions & 6 deletions meteostat/interface/monthly.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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']
Expand Down
38 changes: 26 additions & 12 deletions meteostat/interface/point.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -83,17 +83,19 @@ 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:
stations = stations.inventory(freq, (start, end))

# 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)
Expand All @@ -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)

Expand All @@ -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
2 changes: 1 addition & 1 deletion meteostat/interface/stations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# Setup
setup(
name='meteostat',
version='1.5.0',
version='1.5.1',
author='Meteostat',
author_email='[email protected]',
description='Access and analyze historical weather and climate data with Python.',
Expand Down

0 comments on commit 7cc4459

Please sign in to comment.