meteostat 1.7.6__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meteostat/__init__.py +38 -19
- meteostat/api/config.py +158 -0
- meteostat/api/daily.py +76 -0
- meteostat/api/hourly.py +80 -0
- meteostat/api/interpolate.py +378 -0
- meteostat/api/inventory.py +59 -0
- meteostat/api/merge.py +103 -0
- meteostat/api/monthly.py +73 -0
- meteostat/api/normals.py +144 -0
- meteostat/api/point.py +30 -0
- meteostat/api/stations.py +234 -0
- meteostat/api/timeseries.py +334 -0
- meteostat/core/cache.py +212 -59
- meteostat/core/data.py +203 -0
- meteostat/core/logger.py +9 -0
- meteostat/core/network.py +82 -0
- meteostat/core/parameters.py +112 -0
- meteostat/core/providers.py +184 -0
- meteostat/core/schema.py +170 -0
- meteostat/core/validator.py +38 -0
- meteostat/enumerations.py +149 -0
- meteostat/interpolation/idw.py +120 -0
- meteostat/interpolation/lapserate.py +91 -0
- meteostat/interpolation/nearest.py +31 -0
- meteostat/parameters.py +354 -0
- meteostat/providers/dwd/climat.py +166 -0
- meteostat/providers/dwd/daily.py +144 -0
- meteostat/providers/dwd/hourly.py +218 -0
- meteostat/providers/dwd/monthly.py +138 -0
- meteostat/providers/dwd/mosmix.py +351 -0
- meteostat/providers/dwd/poi.py +117 -0
- meteostat/providers/dwd/shared.py +155 -0
- meteostat/providers/eccc/daily.py +87 -0
- meteostat/providers/eccc/hourly.py +104 -0
- meteostat/providers/eccc/monthly.py +66 -0
- meteostat/providers/eccc/shared.py +45 -0
- meteostat/providers/index.py +496 -0
- meteostat/providers/meteostat/daily.py +65 -0
- meteostat/providers/meteostat/daily_derived.py +110 -0
- meteostat/providers/meteostat/hourly.py +66 -0
- meteostat/providers/meteostat/monthly.py +45 -0
- meteostat/providers/meteostat/monthly_derived.py +106 -0
- meteostat/providers/meteostat/shared.py +93 -0
- meteostat/providers/metno/forecast.py +186 -0
- meteostat/providers/noaa/ghcnd.py +228 -0
- meteostat/providers/noaa/isd_lite.py +142 -0
- meteostat/providers/noaa/metar.py +163 -0
- meteostat/typing.py +113 -0
- meteostat/utils/conversions.py +231 -0
- meteostat/utils/data.py +194 -0
- meteostat/utils/geo.py +28 -0
- meteostat/utils/guards.py +51 -0
- meteostat/utils/parsers.py +161 -0
- meteostat/utils/types.py +113 -0
- meteostat/utils/validators.py +31 -0
- meteostat-2.0.1.dist-info/METADATA +130 -0
- meteostat-2.0.1.dist-info/RECORD +64 -0
- {meteostat-1.7.6.dist-info → meteostat-2.0.1.dist-info}/WHEEL +1 -2
- meteostat/core/loader.py +0 -103
- meteostat/core/warn.py +0 -34
- meteostat/enumerations/granularity.py +0 -22
- meteostat/interface/base.py +0 -39
- meteostat/interface/daily.py +0 -118
- meteostat/interface/hourly.py +0 -154
- meteostat/interface/meteodata.py +0 -210
- meteostat/interface/monthly.py +0 -109
- meteostat/interface/normals.py +0 -245
- meteostat/interface/point.py +0 -143
- meteostat/interface/stations.py +0 -252
- meteostat/interface/timeseries.py +0 -237
- meteostat/series/aggregate.py +0 -48
- meteostat/series/convert.py +0 -28
- meteostat/series/count.py +0 -17
- meteostat/series/coverage.py +0 -20
- meteostat/series/fetch.py +0 -28
- meteostat/series/interpolate.py +0 -47
- meteostat/series/normalize.py +0 -76
- meteostat/series/stations.py +0 -22
- meteostat/units.py +0 -149
- meteostat/utilities/__init__.py +0 -0
- meteostat/utilities/aggregations.py +0 -37
- meteostat/utilities/endpoint.py +0 -33
- meteostat/utilities/helpers.py +0 -70
- meteostat/utilities/mutations.py +0 -89
- meteostat/utilities/validations.py +0 -30
- meteostat-1.7.6.dist-info/METADATA +0 -112
- meteostat-1.7.6.dist-info/RECORD +0 -39
- meteostat-1.7.6.dist-info/top_level.txt +0 -1
- /meteostat/{core → api}/__init__.py +0 -0
- /meteostat/{enumerations → interpolation}/__init__.py +0 -0
- /meteostat/{interface → providers}/__init__.py +0 -0
- /meteostat/{interface/interpolate.py → py.typed} +0 -0
- /meteostat/{series → utils}/__init__.py +0 -0
- {meteostat-1.7.6.dist-info → meteostat-2.0.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TimeSeries Class
|
|
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
|
-
from datetime import datetime
|
|
12
|
-
from typing import Optional, Union
|
|
13
|
-
import pandas as pd
|
|
14
|
-
from meteostat.core.cache import file_in_cache, get_local_file_path
|
|
15
|
-
from meteostat.core.loader import load_handler
|
|
16
|
-
from meteostat.enumerations.granularity import Granularity
|
|
17
|
-
from meteostat.utilities.endpoint import generate_endpoint_path
|
|
18
|
-
from meteostat.utilities.mutations import filter_time, localize
|
|
19
|
-
from meteostat.utilities.validations import validate_series
|
|
20
|
-
from meteostat.utilities.helpers import get_flag_from_source_factory, with_suffix
|
|
21
|
-
from meteostat.interface.point import Point
|
|
22
|
-
from meteostat.interface.meteodata import MeteoData
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class TimeSeries(MeteoData):
|
|
26
|
-
"""
|
|
27
|
-
TimeSeries class which provides features which are
|
|
28
|
-
used across all time series classes
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
# Base URL of the Meteostat bulk data interface
|
|
32
|
-
endpoint = "https://data.meteostat.net/"
|
|
33
|
-
|
|
34
|
-
# The list of origin weather Stations
|
|
35
|
-
_origin_stations: Optional[pd.Index] = None
|
|
36
|
-
|
|
37
|
-
# The start date
|
|
38
|
-
_start: Optional[datetime] = None
|
|
39
|
-
|
|
40
|
-
# The end date
|
|
41
|
-
_end: Optional[datetime] = None
|
|
42
|
-
|
|
43
|
-
# Include model data?
|
|
44
|
-
_model = True
|
|
45
|
-
|
|
46
|
-
# Fetch source flags?
|
|
47
|
-
_flags = False
|
|
48
|
-
|
|
49
|
-
def _load_data(self, station: str, year: Optional[int] = None) -> None:
|
|
50
|
-
"""
|
|
51
|
-
Load file for a single station from Meteostat
|
|
52
|
-
"""
|
|
53
|
-
# File name
|
|
54
|
-
file = generate_endpoint_path(self.granularity, station, year)
|
|
55
|
-
|
|
56
|
-
# Get local file path
|
|
57
|
-
path = get_local_file_path(self.cache_dir, self.cache_subdir, file)
|
|
58
|
-
|
|
59
|
-
# Check if file in cache
|
|
60
|
-
if self.max_age > 0 and file_in_cache(path, self.max_age):
|
|
61
|
-
# Read cached data
|
|
62
|
-
df = pd.read_pickle(path)
|
|
63
|
-
|
|
64
|
-
else:
|
|
65
|
-
# Get data from Meteostat
|
|
66
|
-
df = load_handler(
|
|
67
|
-
self.endpoint,
|
|
68
|
-
file,
|
|
69
|
-
self.proxy,
|
|
70
|
-
default_df=pd.DataFrame(
|
|
71
|
-
columns=self._raw_columns
|
|
72
|
-
+ with_suffix(self._raw_columns, "_source")
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
# Add time column and drop original columns
|
|
77
|
-
if len(self._parse_dates) < 3:
|
|
78
|
-
df["day"] = 1
|
|
79
|
-
|
|
80
|
-
df["time"] = pd.to_datetime(
|
|
81
|
-
df[
|
|
82
|
-
(
|
|
83
|
-
self._parse_dates
|
|
84
|
-
if len(self._parse_dates) > 2
|
|
85
|
-
else self._parse_dates + ["day"]
|
|
86
|
-
)
|
|
87
|
-
]
|
|
88
|
-
)
|
|
89
|
-
df = df.drop(self._parse_dates, axis=1)
|
|
90
|
-
|
|
91
|
-
# Validate and prepare data for further processing
|
|
92
|
-
df = validate_series(df, station)
|
|
93
|
-
|
|
94
|
-
# Rename columns
|
|
95
|
-
df = df.rename(columns=self._renamed_columns, errors="ignore")
|
|
96
|
-
|
|
97
|
-
# Convert sources to flags
|
|
98
|
-
for col in df.columns:
|
|
99
|
-
basecol = col[:-7] if col.endswith("_source") else col
|
|
100
|
-
|
|
101
|
-
if basecol not in self._processed_columns:
|
|
102
|
-
df.drop(col, axis=1, inplace=True)
|
|
103
|
-
continue
|
|
104
|
-
|
|
105
|
-
if basecol == col:
|
|
106
|
-
df[col] = df[col].astype("Float64")
|
|
107
|
-
|
|
108
|
-
if col.endswith("_source"):
|
|
109
|
-
flagcol = f"{basecol}_flag"
|
|
110
|
-
df[flagcol] = pd.NA
|
|
111
|
-
df[flagcol] = df[flagcol].astype("string")
|
|
112
|
-
mask = df[col].notna()
|
|
113
|
-
df.loc[mask, flagcol] = df.loc[mask, col].apply(
|
|
114
|
-
get_flag_from_source_factory(
|
|
115
|
-
self._source_mappings, self._model_flag
|
|
116
|
-
)
|
|
117
|
-
)
|
|
118
|
-
df.drop(col, axis=1, inplace=True)
|
|
119
|
-
|
|
120
|
-
# Process virtual columns
|
|
121
|
-
for key, value in self._virtual_columns.items():
|
|
122
|
-
df = value(df, key)
|
|
123
|
-
|
|
124
|
-
# Save as Pickle
|
|
125
|
-
if self.max_age > 0:
|
|
126
|
-
df.to_pickle(path)
|
|
127
|
-
|
|
128
|
-
# Localize time column
|
|
129
|
-
if (
|
|
130
|
-
self.granularity == Granularity.HOURLY
|
|
131
|
-
and self._timezone is not None
|
|
132
|
-
and len(df.index) > 0
|
|
133
|
-
):
|
|
134
|
-
df = localize(df, self._timezone)
|
|
135
|
-
|
|
136
|
-
# Filter time period and append to DataFrame
|
|
137
|
-
df = filter_time(df, self._start, self._end)
|
|
138
|
-
|
|
139
|
-
# Return
|
|
140
|
-
return df
|
|
141
|
-
|
|
142
|
-
def _filter_model(self) -> None:
|
|
143
|
-
"""
|
|
144
|
-
Remove model data from time series
|
|
145
|
-
"""
|
|
146
|
-
|
|
147
|
-
for col_name in self._processed_columns:
|
|
148
|
-
self._data.loc[
|
|
149
|
-
(pd.isna(self._data[f"{col_name}_flag"]))
|
|
150
|
-
| (self._data[f"{col_name}_flag"].str.contains(self._model_flag)),
|
|
151
|
-
col_name,
|
|
152
|
-
] = pd.NA
|
|
153
|
-
|
|
154
|
-
# Drop nan-only rows
|
|
155
|
-
self._data.dropna(how="all", subset=self._processed_columns, inplace=True)
|
|
156
|
-
|
|
157
|
-
def _init_time_series(
|
|
158
|
-
self,
|
|
159
|
-
loc: Union[pd.DataFrame, Point, list, str], # Station(s) or geo point
|
|
160
|
-
start: datetime = None,
|
|
161
|
-
end: datetime = None,
|
|
162
|
-
model=True, # Include model data?
|
|
163
|
-
flags=False, # Load source flags?
|
|
164
|
-
) -> None:
|
|
165
|
-
"""
|
|
166
|
-
Common initialization for all time series, regardless
|
|
167
|
-
of its granularity
|
|
168
|
-
"""
|
|
169
|
-
|
|
170
|
-
# Set list of weather stations based on user
|
|
171
|
-
# input or retrieve list of stations programatically
|
|
172
|
-
# if location is a geographical point
|
|
173
|
-
if isinstance(loc, pd.DataFrame):
|
|
174
|
-
self._stations = loc.index
|
|
175
|
-
elif isinstance(loc, Point):
|
|
176
|
-
stations = loc.get_stations("daily", start, end, model)
|
|
177
|
-
self._stations = stations.index
|
|
178
|
-
else:
|
|
179
|
-
if not isinstance(loc, list):
|
|
180
|
-
loc = [loc]
|
|
181
|
-
self._stations = pd.Index(loc)
|
|
182
|
-
|
|
183
|
-
# Preserve settings
|
|
184
|
-
self._start = start if self._start is None else self._start
|
|
185
|
-
self._end = end if self._end is None else self._end
|
|
186
|
-
self._model = model
|
|
187
|
-
self._flags = flags
|
|
188
|
-
|
|
189
|
-
# Get data for all weather stations
|
|
190
|
-
self._data = self._get_data()
|
|
191
|
-
|
|
192
|
-
# Fill columns if they don't exist
|
|
193
|
-
for col in self._processed_columns:
|
|
194
|
-
if col not in self._data.columns:
|
|
195
|
-
self._data[col] = pd.NA
|
|
196
|
-
self._data[col] = self._data[col].astype("Float64")
|
|
197
|
-
if (flagcol := f"{col}_flag") not in self._data.columns:
|
|
198
|
-
self._data[flagcol] = pd.NA
|
|
199
|
-
self._data[flagcol] = self._data[flagcol].astype("string")
|
|
200
|
-
|
|
201
|
-
# Reorder the DataFrame
|
|
202
|
-
self._data = self._data[
|
|
203
|
-
self._processed_columns + with_suffix(self._processed_columns, "_flag")
|
|
204
|
-
]
|
|
205
|
-
|
|
206
|
-
# Remove model data from DataFrame
|
|
207
|
-
if not model:
|
|
208
|
-
self._filter_model()
|
|
209
|
-
|
|
210
|
-
# Conditionally, remove flags from DataFrame
|
|
211
|
-
if not self._flags:
|
|
212
|
-
self._data.drop(
|
|
213
|
-
with_suffix(self._processed_columns, "_flag"),
|
|
214
|
-
axis=1,
|
|
215
|
-
errors="ignore",
|
|
216
|
-
inplace=True,
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
# Interpolate data spatially if requested
|
|
220
|
-
# location is a geographical point
|
|
221
|
-
if isinstance(loc, Point):
|
|
222
|
-
self._resolve_point(loc.method, stations, loc.alt, loc.adapt_temp)
|
|
223
|
-
|
|
224
|
-
# Clear cache if auto cleaning is enabled
|
|
225
|
-
if self.max_age > 0 and self.autoclean:
|
|
226
|
-
self.clear_cache()
|
|
227
|
-
|
|
228
|
-
# Import methods
|
|
229
|
-
from meteostat.series.normalize import normalize
|
|
230
|
-
from meteostat.series.interpolate import interpolate
|
|
231
|
-
from meteostat.series.aggregate import aggregate
|
|
232
|
-
from meteostat.series.convert import convert
|
|
233
|
-
from meteostat.series.coverage import coverage
|
|
234
|
-
from meteostat.series.count import count
|
|
235
|
-
from meteostat.series.fetch import fetch
|
|
236
|
-
from meteostat.series.stations import stations
|
|
237
|
-
from meteostat.core.cache import clear_cache
|
meteostat/series/aggregate.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Aggregate Data
|
|
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
|
-
from copy import copy
|
|
12
|
-
import pandas as pd
|
|
13
|
-
from meteostat.core.warn import warn
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def aggregate(self, freq: str = None, spatial: bool = False):
|
|
17
|
-
"""
|
|
18
|
-
Aggregate observations
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
if self.count() > 0 and not self._data.isnull().values.all():
|
|
22
|
-
# Create temporal instance
|
|
23
|
-
temp = copy(self)
|
|
24
|
-
|
|
25
|
-
# Set default frequency if not set
|
|
26
|
-
if freq is None:
|
|
27
|
-
freq = self._freq
|
|
28
|
-
|
|
29
|
-
# Time aggregation
|
|
30
|
-
temp._data = temp._data.groupby(
|
|
31
|
-
["station", pd.Grouper(level="time", freq=freq)]
|
|
32
|
-
).agg(temp.aggregations)
|
|
33
|
-
|
|
34
|
-
# Spatial aggregation
|
|
35
|
-
if spatial:
|
|
36
|
-
temp._data = temp._data.groupby(
|
|
37
|
-
[pd.Grouper(level="time", freq=freq)]
|
|
38
|
-
).mean()
|
|
39
|
-
|
|
40
|
-
# Round
|
|
41
|
-
temp._data = temp._data.round(1)
|
|
42
|
-
|
|
43
|
-
# Return class instance
|
|
44
|
-
return temp
|
|
45
|
-
|
|
46
|
-
# Show warning & return self
|
|
47
|
-
warn("Skipping aggregation on empty DataFrame")
|
|
48
|
-
return self
|
meteostat/series/convert.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Convert Data Units
|
|
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
|
-
from copy import copy
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def convert(self, units: dict):
|
|
15
|
-
"""
|
|
16
|
-
Convert columns to a different unit
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
# Create temporal instance
|
|
20
|
-
temp = copy(self)
|
|
21
|
-
|
|
22
|
-
# Change data units
|
|
23
|
-
for parameter, unit in units.items():
|
|
24
|
-
if parameter in temp._processed_columns:
|
|
25
|
-
temp._data[parameter] = temp._data[parameter].apply(unit)
|
|
26
|
-
|
|
27
|
-
# Return class instance
|
|
28
|
-
return temp
|
meteostat/series/count.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Get Number Of Rows
|
|
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
|
-
|
|
12
|
-
def count(self) -> int:
|
|
13
|
-
"""
|
|
14
|
-
Return number of rows in DataFrame
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
return len(self._data.index)
|
meteostat/series/coverage.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Calculate Data Coverage
|
|
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
|
-
|
|
12
|
-
def coverage(self, parameter: str = None) -> float:
|
|
13
|
-
"""
|
|
14
|
-
Calculate data coverage (overall or by parameter)
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
if parameter is None:
|
|
18
|
-
return len(self._data.index) / self.expected_rows()
|
|
19
|
-
|
|
20
|
-
return round(self._data[parameter].count() / self.expected_rows(), 2)
|
meteostat/series/fetch.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Fetch Data
|
|
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
|
-
from copy import copy
|
|
12
|
-
import pandas as pd
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def fetch(self) -> pd.DataFrame:
|
|
16
|
-
"""
|
|
17
|
-
Fetch DataFrame
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
# Copy DataFrame
|
|
21
|
-
temp = copy(self._data)
|
|
22
|
-
|
|
23
|
-
# Remove station index if it's a single station
|
|
24
|
-
if len(self._stations) == 1 and "station" in temp.index.names:
|
|
25
|
-
temp = temp.reset_index(level="station", drop=True)
|
|
26
|
-
|
|
27
|
-
# Return data frame
|
|
28
|
-
return temp
|
meteostat/series/interpolate.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Interpolate Data
|
|
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
|
-
from copy import copy
|
|
12
|
-
import numpy as np
|
|
13
|
-
from meteostat.core.warn import warn
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def interpolate(self, limit: int = 3):
|
|
17
|
-
"""
|
|
18
|
-
Interpolate NULL values
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
if self.count() > 0 and not self._data.isnull().values.all():
|
|
22
|
-
# Create temporal instance
|
|
23
|
-
temp = copy(self)
|
|
24
|
-
|
|
25
|
-
# Convert to float64
|
|
26
|
-
temp._data = temp._data.astype("float64")
|
|
27
|
-
|
|
28
|
-
# Apply interpolation
|
|
29
|
-
temp._data = temp._data.groupby("station", group_keys=False).apply(
|
|
30
|
-
lambda group: group.interpolate(
|
|
31
|
-
method="linear",
|
|
32
|
-
limit=limit,
|
|
33
|
-
limit_direction="both",
|
|
34
|
-
axis=0,
|
|
35
|
-
fill_value=np.nan,
|
|
36
|
-
)
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
# Convert to original type
|
|
40
|
-
temp._data = temp._data.astype("Float64")
|
|
41
|
-
|
|
42
|
-
# Return class instance
|
|
43
|
-
return temp
|
|
44
|
-
|
|
45
|
-
# Show warning & return self
|
|
46
|
-
warn("Skipping interpolation on empty DataFrame")
|
|
47
|
-
return self
|
meteostat/series/normalize.py
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Normalize Data
|
|
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
|
-
from copy import copy
|
|
12
|
-
from numpy import nan
|
|
13
|
-
import pandas as pd
|
|
14
|
-
import pytz
|
|
15
|
-
from meteostat.core.warn import warn
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def normalize(self):
|
|
19
|
-
"""
|
|
20
|
-
Normalize the DataFrame
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
if self.count() == 0:
|
|
24
|
-
warn("Pointless normalization of empty DataFrame")
|
|
25
|
-
|
|
26
|
-
# Create temporal instance
|
|
27
|
-
temp = copy(self)
|
|
28
|
-
|
|
29
|
-
if temp._start and temp._end and temp.coverage() < 1:
|
|
30
|
-
# Create result DataFrame
|
|
31
|
-
result = pd.DataFrame(columns=temp._processed_columns, dtype="Float64")
|
|
32
|
-
|
|
33
|
-
# Handle tz-aware date ranges
|
|
34
|
-
if hasattr(temp, "_timezone") and temp._timezone is not None:
|
|
35
|
-
timezone = pytz.timezone(temp._timezone)
|
|
36
|
-
start = temp._start.astimezone(timezone)
|
|
37
|
-
end = temp._end.astimezone(timezone)
|
|
38
|
-
else:
|
|
39
|
-
start = temp._start
|
|
40
|
-
end = temp._end
|
|
41
|
-
|
|
42
|
-
# Go through list of weather stations
|
|
43
|
-
for station in temp._stations:
|
|
44
|
-
# Create data frame
|
|
45
|
-
df = pd.DataFrame(columns=temp._processed_columns, dtype="Float64")
|
|
46
|
-
# Add time series
|
|
47
|
-
df["time"] = pd.date_range(
|
|
48
|
-
start,
|
|
49
|
-
end,
|
|
50
|
-
freq=self._freq,
|
|
51
|
-
tz=temp._timezone if hasattr(temp, "_timezone") else None,
|
|
52
|
-
)
|
|
53
|
-
# Add station ID
|
|
54
|
-
df["station"] = station
|
|
55
|
-
# Add columns
|
|
56
|
-
for column in temp._processed_columns:
|
|
57
|
-
# Add column to DataFrame
|
|
58
|
-
df[column] = nan
|
|
59
|
-
|
|
60
|
-
result = pd.concat([result, df], axis=0)
|
|
61
|
-
|
|
62
|
-
# Set index
|
|
63
|
-
result = result.set_index(["station", "time"])
|
|
64
|
-
|
|
65
|
-
# Merge data
|
|
66
|
-
temp._data = (
|
|
67
|
-
pd.concat([temp._data, result], axis=0)
|
|
68
|
-
.groupby(["station", "time"], as_index=True)
|
|
69
|
-
.first()
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
# None -> nan
|
|
73
|
-
temp._data = temp._data.fillna(pd.NA)
|
|
74
|
-
|
|
75
|
-
# Return class instance
|
|
76
|
-
return temp
|
meteostat/series/stations.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Get Weather Stations
|
|
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
|
-
from copy import copy
|
|
12
|
-
import pandas as pd
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@property
|
|
16
|
-
def stations(self) -> pd.Index:
|
|
17
|
-
"""
|
|
18
|
-
Fetch Weather Stations
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
# Return index of weather stations
|
|
22
|
-
return copy(self._stations)
|
meteostat/units.py
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Meteorological Data Units
|
|
3
|
-
|
|
4
|
-
Convert a Pandas Series to any meteorological data unit
|
|
5
|
-
|
|
6
|
-
The code is licensed under the MIT license.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from numpy import nan, isnan
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def fahrenheit(value):
|
|
13
|
-
"""
|
|
14
|
-
Convert Celsius to Fahrenheit
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
return round((value * 9 / 5) + 32, 1)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def kelvin(value):
|
|
21
|
-
"""
|
|
22
|
-
Convert Celsius to Kelvin
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
return round(value + 273.15, 1)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def inches(value):
|
|
29
|
-
"""
|
|
30
|
-
Convert millimeters to inches
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
return round(value / 25.4, 3)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def feet(value):
|
|
37
|
-
"""
|
|
38
|
-
Convert meters to feet
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
return round(value / 0.3048, 1)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def ms(value):
|
|
45
|
-
"""
|
|
46
|
-
Convert kilometers per hour to meters per second
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
return round(value / 3.6, 1)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def mph(value):
|
|
53
|
-
"""
|
|
54
|
-
Convert kilometers per hour to miles per hour
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
return round(value * 0.6214, 1)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def direction(value):
|
|
61
|
-
"""
|
|
62
|
-
Convert degrees to wind direction
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
wdir = nan
|
|
66
|
-
|
|
67
|
-
if (337 <= value <= 360) or value <= 23:
|
|
68
|
-
wdir = "N"
|
|
69
|
-
if 24 <= value <= 68:
|
|
70
|
-
wdir = "NE"
|
|
71
|
-
if 69 <= value <= 113:
|
|
72
|
-
wdir = "E"
|
|
73
|
-
if 114 <= value <= 158:
|
|
74
|
-
wdir = "SE"
|
|
75
|
-
if 159 <= value <= 203:
|
|
76
|
-
wdir = "S"
|
|
77
|
-
if 204 <= value <= 248:
|
|
78
|
-
wdir = "SW"
|
|
79
|
-
if 249 <= value <= 293:
|
|
80
|
-
wdir = "W"
|
|
81
|
-
if 294 <= value <= 336:
|
|
82
|
-
wdir = "NW"
|
|
83
|
-
|
|
84
|
-
return wdir
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def condition(value):
|
|
88
|
-
"""
|
|
89
|
-
Convert Meteostat condition code to descriptive string
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
if isnan(value) or value < 1 or value > 27:
|
|
93
|
-
return nan
|
|
94
|
-
|
|
95
|
-
return [
|
|
96
|
-
"Clear",
|
|
97
|
-
"Fair",
|
|
98
|
-
"Cloudy",
|
|
99
|
-
"Overcast",
|
|
100
|
-
"Fog",
|
|
101
|
-
"Freezing Fog",
|
|
102
|
-
"Light Rain",
|
|
103
|
-
"Rain",
|
|
104
|
-
"Heavy Rain",
|
|
105
|
-
"Freezing Rain",
|
|
106
|
-
"Heavy Freezing Rain",
|
|
107
|
-
"Sleet",
|
|
108
|
-
"Heavy Sleet",
|
|
109
|
-
"Light Snowfall",
|
|
110
|
-
"Snowfall",
|
|
111
|
-
"Heavy Snowfall",
|
|
112
|
-
"Rain Shower",
|
|
113
|
-
"Heavy Rain Shower",
|
|
114
|
-
"Sleet Shower",
|
|
115
|
-
"Heavy Sleet Shower",
|
|
116
|
-
"Snow Shower",
|
|
117
|
-
"Heavy Snow Shower",
|
|
118
|
-
"Lightning",
|
|
119
|
-
"Hail",
|
|
120
|
-
"Thunderstorm",
|
|
121
|
-
"Heavy Thunderstorm",
|
|
122
|
-
"Storm",
|
|
123
|
-
][int(value) - 1]
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# Imperial units
|
|
127
|
-
imperial = {
|
|
128
|
-
"temp": fahrenheit,
|
|
129
|
-
"tavg": fahrenheit,
|
|
130
|
-
"tmin": fahrenheit,
|
|
131
|
-
"tmax": fahrenheit,
|
|
132
|
-
"dwpt": fahrenheit,
|
|
133
|
-
"prcp": inches,
|
|
134
|
-
"snow": inches,
|
|
135
|
-
"wspd": mph,
|
|
136
|
-
"wpgt": mph,
|
|
137
|
-
"distance": feet,
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
# Scientific units
|
|
141
|
-
scientific = {
|
|
142
|
-
"temp": kelvin,
|
|
143
|
-
"tavg": kelvin,
|
|
144
|
-
"tmin": kelvin,
|
|
145
|
-
"tmax": kelvin,
|
|
146
|
-
"dwpt": kelvin,
|
|
147
|
-
"wspd": ms,
|
|
148
|
-
"wpgt": ms,
|
|
149
|
-
}
|
meteostat/utilities/__init__.py
DELETED
|
File without changes
|