Skip to content

Commit 382bf27

Browse files
authored
Meteostat 1.5.0 (#53)
* Meteostat 1.5.0 * Linting 1.5.0
1 parent 470b75e commit 382bf27

File tree

16 files changed

+273
-253
lines changed

16 files changed

+273
-253
lines changed

examples/daily/aggregate_regional.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,34 @@
88
The code is licensed under the MIT license.
99
"""
1010

11-
from datetime import datetime
12-
import matplotlib.pyplot as plt
13-
from meteostat import Stations, Daily
11+
if __name__ == '__main__':
1412

15-
# Configuration
16-
Daily.max_threads = 5
13+
from datetime import datetime
14+
import matplotlib.pyplot as plt
15+
from meteostat import Stations, Daily
1716

18-
# Time period
19-
start = datetime(1980, 1, 1)
20-
end = datetime(2019, 12, 31)
17+
# Configuration
18+
Daily.cores = 12
2119

22-
# Get random weather stations in the US
23-
stations = Stations()
24-
stations = stations.region('US')
25-
stations = stations.inventory('daily', (start, end))
26-
stations = stations.fetch(limit=20, sample=True)
20+
# Time period
21+
start = datetime(1980, 1, 1)
22+
end = datetime(2019, 12, 31)
2723

28-
# Get daily data
29-
data = Daily(stations, start, end)
24+
# Get random weather stations in the US
25+
stations = Stations()
26+
stations = stations.region('US')
27+
stations = stations.inventory('daily', (start, end))
28+
stations = stations.fetch(limit=50, sample=True)
3029

31-
# Normalize & aggregate
32-
data = data.normalize().aggregate('1Y', spatial=True).fetch()
30+
# Get daily data
31+
data = Daily(stations, start, end)
3332

34-
# Chart title
35-
TITLE = 'Average US Annual Temperature from 1980 to 2019'
33+
# Normalize & aggregate
34+
data = data.normalize().aggregate('1Y', spatial=True).fetch()
3635

37-
# Plot chart
38-
data.plot(y=['tavg'], title=TITLE)
39-
plt.show()
36+
# Chart title
37+
TITLE = 'Average US Annual Temperature from 1980 to 2019'
38+
39+
# Plot chart
40+
data.plot(y=['tavg'], title=TITLE)
41+
plt.show()

examples/hourly/performance.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Example: Hourly point data performance
3+
4+
Meteorological data provided by Meteostat (https://dev.meteostat.net)
5+
under the terms of the Creative Commons Attribution-NonCommercial
6+
4.0 International Public License.
7+
8+
The code is licensed under the MIT license.
9+
"""
10+
11+
if __name__ == '__main__':
12+
13+
from timeit import default_timer as timer
14+
15+
# Get start time
16+
s = timer()
17+
18+
# Run script
19+
from datetime import datetime
20+
from meteostat import Hourly
21+
22+
Hourly.cores = 12
23+
24+
start = datetime(1960, 1, 1)
25+
end = datetime(2021, 1, 1, 23, 59)
26+
27+
data = Hourly('10637', start, end, timezone='Europe/Berlin')
28+
data = data.fetch()
29+
30+
# Get end time
31+
e = timer()
32+
33+
# Print performance
34+
print(e - s)

meteostat/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
"""
1313

1414
__appname__ = 'meteostat'
15-
__version__ = '1.4.6'
15+
__version__ = '1.5.0'
1616

17+
from .interface.base import Base
18+
from .interface.timeseries import Timeseries
1719
from .interface.stations import Stations
1820
from .interface.point import Point
1921
from .interface.hourly import Hourly

meteostat/core/cache.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ def file_in_cache(
4242

4343
# Make sure the cache directory exists
4444
if not os.path.exists(directory):
45-
os.makedirs(directory)
45+
try:
46+
os.makedirs(directory)
47+
except FileExistsError:
48+
pass
4649

4750
# Return the file path if it exists
4851
if os.path.isfile(path) and time.time() - \

meteostat/core/config.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

meteostat/core/loader.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"""
1010

1111
from urllib.error import HTTPError
12+
from multiprocessing import Pool
1213
from multiprocessing.pool import ThreadPool
1314
from typing import Callable
1415
import pandas as pd
@@ -18,27 +19,49 @@
1819
def processing_handler(
1920
datasets: list,
2021
load: Callable[[dict], None],
21-
max_threads: int
22+
cores: int,
23+
threads: int
2224
) -> None:
2325
"""
24-
Load multiple datasets simultaneously
26+
Load multiple datasets (simultaneously)
2527
"""
2628

27-
# Single-thread processing
28-
if max_threads < 2:
29+
# Data output
30+
output = []
2931

30-
for dataset in datasets:
31-
load(*dataset)
32+
# Multi-core processing
33+
if cores > 1 and len(datasets) > 1:
34+
35+
# Create process pool
36+
with Pool(cores) as pool:
37+
38+
# Process datasets in pool
39+
output = pool.starmap(load, datasets)
40+
41+
# Wait for Pool to finish
42+
pool.close()
43+
pool.join()
3244

3345
# Multi-thread processing
46+
elif threads > 1 and len(datasets) > 1:
47+
48+
# Create process pool
49+
with ThreadPool(cores) as pool:
50+
51+
# Process datasets in pool
52+
output = pool.starmap(load, datasets)
53+
54+
# Wait for Pool to finish
55+
pool.close()
56+
pool.join()
57+
58+
# Single-thread processing
3459
else:
3560

36-
pool = ThreadPool(max_threads)
37-
pool.starmap(load, datasets)
61+
for dataset in datasets:
62+
output.append(load(*dataset))
3863

39-
# Wait for Pool to finish
40-
pool.close()
41-
pool.join()
64+
return pd.concat(output)
4265

4366

4467
def load_handler(

meteostat/interface/base.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,27 @@
88
The code is licensed under the MIT license.
99
"""
1010

11+
import os
12+
1113

1214
class Base:
1315

1416
"""
1517
Base class that provides features which are used across the package
1618
"""
1719

18-
# Import configuration
19-
from meteostat.core.config import endpoint, cache_dir, max_age, max_threads
20+
# Base URL of the Meteostat bulk data interface
21+
endpoint: str = 'https://bulk.meteostat.net/v2/'
22+
23+
# Location of the cache directory
24+
cache_dir: str = os.path.expanduser(
25+
'~') + os.sep + '.meteostat' + os.sep + 'cache'
26+
27+
# Maximum age of a cached file in seconds
28+
max_age: int = 24 * 60 * 60
29+
30+
# Number of cores used for processing files
31+
cores: int = 1
32+
33+
# Number of threads used for processing files
34+
threads: int = 1

meteostat/interface/daily.py

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
from meteostat.core.loader import processing_handler, load_handler
1717
from meteostat.utilities.validations import validate_series
1818
from meteostat.utilities.aggregations import degree_mean, weighted_average
19-
from meteostat.interface.base import Base
19+
from meteostat.interface.timeseries import Timeseries
2020
from meteostat.interface.point import Point
2121

2222

23-
class Daily(Base):
23+
class Daily(Timeseries):
2424

2525
"""
2626
Retrieve daily weather observations for one or multiple weather stations or
@@ -30,21 +30,6 @@ class Daily(Base):
3030
# The cache subdirectory
3131
cache_subdir: str = 'daily'
3232

33-
# The list of weather Stations
34-
_stations: pd.Index = None
35-
36-
# The start date
37-
_start: datetime = None
38-
39-
# The end date
40-
_end: datetime = None
41-
42-
# Include model data?
43-
_model: bool = True
44-
45-
# The data frame
46-
_data: pd.DataFrame = pd.DataFrame()
47-
4833
# Default frequency
4934
_freq: str = '1D'
5035

@@ -143,14 +128,11 @@ def _load(
143128
# Get time index
144129
time = df.index.get_level_values('time')
145130

146-
# Filter & append
147-
self._data = self._data.append(
148-
df.loc[(time >= self._start) & (time <= self._end)])
131+
# Filter & return
132+
return df.loc[(time >= self._start) & (time <= self._end)]
149133

150-
else:
151-
152-
# Append
153-
self._data = self._data.append(df)
134+
# Return
135+
return df
154136

155137
def _get_data(self) -> None:
156138
"""
@@ -168,12 +150,11 @@ def _get_data(self) -> None:
168150
))
169151

170152
# Data Processing
171-
processing_handler(datasets, self._load, self.max_threads)
153+
return processing_handler(
154+
datasets, self._load, self.cores, self.threads)
172155

173-
else:
174-
175-
# Empty DataFrame
176-
self._data = pd.DataFrame(columns=[*self._types])
156+
# Empty DataFrame
157+
return pd.DataFrame(columns=[*self._types])
177158

178159
def _resolve_point(
179160
self,
@@ -263,7 +244,7 @@ def __init__(
263244
self._model = model
264245

265246
# Get data for all weather stations
266-
self._get_data()
247+
self._data = self._get_data()
267248

268249
# Interpolate data
269250
if isinstance(loc, Point):
@@ -279,14 +260,3 @@ def expected_rows(self) -> int:
279260
"""
280261

281262
return (self._end - self._start).days + 1
282-
283-
# Import methods
284-
from meteostat.series.normalize import normalize
285-
from meteostat.series.interpolate import interpolate
286-
from meteostat.series.aggregate import aggregate
287-
from meteostat.series.convert import convert
288-
from meteostat.series.coverage import coverage
289-
from meteostat.series.count import count
290-
from meteostat.series.fetch import fetch
291-
from meteostat.series.stations import stations
292-
from meteostat.core.cache import clear_cache

0 commit comments

Comments
 (0)