Skip to content

Commit

Permalink
Meteostat 1.6.1 (#84)
Browse files Browse the repository at this point in the history
* Meteostat 1.6.1

* Linting

* Change Monthly tests + examples

* Clean DF based on met cols only
  • Loading branch information
clampr authored Feb 7, 2022
1 parent 78c37be commit 5a5602d
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 98 deletions.
5 changes: 3 additions & 2 deletions examples/monthly/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
end = datetime(2018, 12, 31)

# Get monthly data
data = Monthly('10637', start, end)
# Then, aggregate annually
data = Monthly('72202', start, end)
data = data.normalize().aggregate(freq='1Y').fetch()

# Plot chart
data.plot(y=['tavg', 'tmin', 'tmax'])
data.plot(y='tavg')
plt.show()
2 changes: 1 addition & 1 deletion meteostat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
__version__ = '1.6.0'

from .interface.base import Base
from .interface.timeseries import Timeseries
from .interface.timeseries import TimeSeries
from .interface.stations import Stations
from .interface.point import Point
from .interface.hourly import Hourly
Expand Down
9 changes: 6 additions & 3 deletions meteostat/interface/daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
import pandas as pd
from meteostat.enumerations.granularity import Granularity
from meteostat.utilities.aggregations import degree_mean
from meteostat.interface.timeseries import Timeseries
from meteostat.interface.timeseries import TimeSeries
from meteostat.interface.point import Point


class Daily(Timeseries):
class Daily(TimeSeries):

"""
Retrieve daily weather observations for one or multiple weather stations or
Expand All @@ -33,6 +33,9 @@ class Daily(Timeseries):
# Default frequency
_freq: str = '1D'

# Flag which represents model data
_model_flag = 'G'

# Columns
_columns: list = [
'date',
Expand Down Expand Up @@ -94,7 +97,7 @@ def __init__(
) -> None:

# Initialize time series
self._init_timeseries(loc, start, end, model, flags)
self._init_time_series(loc, start, end, model, flags)

def expected_rows(self) -> int:
"""
Expand Down
9 changes: 6 additions & 3 deletions meteostat/interface/hourly.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import pandas as pd
from meteostat.enumerations.granularity import Granularity
from meteostat.utilities.aggregations import degree_mean
from meteostat.interface.timeseries import Timeseries
from meteostat.interface.timeseries import TimeSeries
from meteostat.interface.point import Point


class Hourly(Timeseries):
class Hourly(TimeSeries):

"""
Retrieve hourly weather observations for one or multiple weather stations or
Expand All @@ -41,6 +41,9 @@ class Hourly(Timeseries):
# Default frequency
_freq: str = '1H'

# Flag which represents model data
_model_flag = 'E'

# Raw data columns
_columns: list = [
'date',
Expand Down Expand Up @@ -155,7 +158,7 @@ def __init__(
self._set_time(start, end, timezone)

# Initialize time series
self._init_timeseries(loc, start, end, model, flags)
self._init_time_series(loc, start, end, model, flags)

def expected_rows(self) -> int:
"""
Expand Down
57 changes: 6 additions & 51 deletions meteostat/interface/meteo.py → meteostat/interface/meteodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@
The code is licensed under the MIT license.
"""

from datetime import datetime
from typing import Union
import numpy as np
import pandas as pd
from meteostat.enumerations.granularity import Granularity
from meteostat.core.cache import get_local_file_path, file_in_cache
from meteostat.core.loader import processing_handler, load_handler
from meteostat.utilities.mutations import localize, filter_time, adjust_temp
from meteostat.utilities.validations import validate_series
from meteostat.utilities.aggregations import weighted_average
from meteostat.utilities.endpoint import generate_endpoint_path
from meteostat.interface.base import Base


class Meteo(Base):
class MeteoData(Base):

"""
A parent class for both time series and
Expand All @@ -37,33 +36,6 @@ class Meteo(Base):
# The data frame
_data: pd.DataFrame = pd.DataFrame()

@staticmethod
def _localize(df: pd.DataFrame, timezone: str) -> pd.DataFrame:
"""
Convert time data to any time zone
"""

return df.tz_localize(
'UTC',
level='time'
).tz_convert(
timezone,
level='time'
)

@staticmethod
def _filter_time(df: pd.DataFrame, start: datetime,
end: datetime) -> pd.DataFrame:
"""
Filter time series data based on start and end date
"""

# Get time index
time = df.index.get_level_values('time')

# Filter & return
return df.loc[(time >= start) & (time <= end)]

def _load_data(
self,
station: str,
Expand Down Expand Up @@ -118,7 +90,7 @@ def _load_data(
# Localize time column
if self.granularity == Granularity.HOURLY and self._timezone is not None and len(
df.index) > 0:
df = Meteo._localize(df, self._timezone)
df = localize(df, self._timezone)

# Filter time period and append to DataFrame
# pylint: disable=no-else-return
Expand All @@ -128,7 +100,7 @@ def _load_data(
# Filter & return
return df.loc[end == self._end]
elif not self.granularity == Granularity.NORMALS:
df = Meteo._filter_time(df, self._start, self._end)
df = filter_time(df, self._start, self._end)

# Return
return df
Expand Down Expand Up @@ -167,23 +139,6 @@ def _get_data(self) -> None:
# Empty DataFrame
return pd.DataFrame(columns=[*self._types])

@staticmethod
def adjust_temp(df: pd.DataFrame, alt: int):
"""
Adjust temperature-like data based on altitude
"""

# Temperature-like columns
temp_like = ('temp', 'dwpt', 'tavg', 'tmin', 'tmax')

# Adjust values for all temperature-like data
for col_name in temp_like:
if col_name in df.columns:
df.loc[df[col_name] != np.NaN, col_name] = df[col_name] + \
((2 / 3) * ((df['elevation'] - alt) / 100))

return df

# pylint: disable=too-many-branches
def _resolve_point(
self,
Expand All @@ -208,7 +163,7 @@ def _resolve_point(
stations['elevation'], on='station')

# Adapt temperature-like data based on altitude
data = Meteo.adjust_temp(data, alt)
data = adjust_temp(data, alt)

# Drop elevation & round
data = data.drop('elevation', axis=1).round(1)
Expand Down Expand Up @@ -236,7 +191,7 @@ def _resolve_point(

# Adapt temperature-like data based on altitude
if adapt_temp:
data = Meteo.adjust_temp(data, alt)
data = adjust_temp(data, alt)

# Exclude non-mean data & perform aggregation
if not self.granularity == Granularity.NORMALS:
Expand Down
19 changes: 6 additions & 13 deletions meteostat/interface/monthly.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
from datetime import datetime
from typing import Union
import pandas as pd
from meteostat.utilities.aggregations import degree_mean
from meteostat.enumerations.granularity import Granularity
from meteostat.interface.timeseries import Timeseries
from meteostat.interface.timeseries import TimeSeries
from meteostat.interface.point import Point


class Monthly(Timeseries):
class Monthly(TimeSeries):

"""
Retrieve monthly weather data for one or multiple weather stations or
Expand All @@ -33,6 +32,9 @@ class Monthly(Timeseries):
# Default frequency
_freq: str = '1MS'

# Flag which represents model data
_model_flag = 'I'

# Columns
_columns: list = [
'year',
Expand All @@ -41,10 +43,7 @@ class Monthly(Timeseries):
'tmin',
'tmax',
'prcp',
'snow',
'wdir',
'wspd',
'wpgt',
'pres',
'tsun'
]
Expand All @@ -58,10 +57,7 @@ class Monthly(Timeseries):
'tmin': 'float64',
'tmax': 'float64',
'prcp': 'float64',
'snow': 'float64',
'wdir': 'float64',
'wspd': 'float64',
'wpgt': 'float64',
'pres': 'float64',
'tsun': 'float64'
}
Expand All @@ -77,10 +73,7 @@ class Monthly(Timeseries):
'tmin': 'mean',
'tmax': 'mean',
'prcp': 'sum',
'snow': 'max',
'wdir': degree_mean,
'wspd': 'mean',
'wpgt': 'max',
'pres': 'mean',
'tsun': 'sum'
}
Expand All @@ -99,7 +92,7 @@ def __init__(
start = start.replace(day=1)

# Initialize time series
self._init_timeseries(loc, start, end, model, flags)
self._init_time_series(loc, start, end, model, flags)

def expected_rows(self) -> int:
"""
Expand Down
4 changes: 2 additions & 2 deletions meteostat/interface/normals.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import pandas as pd
from meteostat.enumerations.granularity import Granularity
from meteostat.core.warn import warn
from meteostat.interface.meteo import Meteo
from meteostat.interface.meteodata import MeteoData
from meteostat.interface.point import Point


class Normals(Meteo):
class Normals(MeteoData):

"""
Retrieve climate normals for one or multiple weather stations or
Expand Down
28 changes: 17 additions & 11 deletions meteostat/interface/timeseries.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Timeseries Class
TimeSeries Class
Meteorological data provided by Meteostat (https://dev.meteostat.net)
under the terms of the Creative Commons Attribution-NonCommercial
Expand All @@ -15,16 +15,17 @@
from meteostat.enumerations.granularity import Granularity
from meteostat.core.cache import get_local_file_path, file_in_cache
from meteostat.core.loader import processing_handler, load_handler
from meteostat.utilities.mutations import localize, filter_time
from meteostat.utilities.validations import validate_series
from meteostat.utilities.endpoint import generate_endpoint_path
from meteostat.interface.point import Point
from meteostat.interface.meteo import Meteo
from meteostat.interface.meteodata import MeteoData


class Timeseries(Meteo):
class TimeSeries(MeteoData):

"""
Timeseries class which provides features which are
TimeSeries class which provides features which are
used across all time series classes
"""

Expand Down Expand Up @@ -76,7 +77,9 @@ def _load_flags(
self.endpoint,
file,
self._columns,
None,
{
key: 'string' for key in self._columns[self._first_met_col:]
},
self._parse_dates)

# Validate Series
Expand All @@ -89,11 +92,11 @@ def _load_flags(
# Localize time column
if self.granularity == Granularity.HOURLY and self._timezone is not None and len(
df.index) > 0:
df = Timeseries._localize(df, self._timezone)
df = localize(df, self._timezone)

# Filter time period and append to DataFrame
if self._start and self._end:
df = Timeseries._filter_time(df, self._start, self._end)
df = filter_time(df, self._start, self._end)

return df

Expand Down Expand Up @@ -126,8 +129,11 @@ def _filter_model(self) -> None:
columns = self._columns[self._first_met_col:]

for col_name in columns:
self._data.loc[self._data[f'{col_name}_flag']
== 'M', col_name] = np.NaN
self._data.loc[
(pd.isna(self._data[f'{col_name}_flag'])) |
(self._data[f'{col_name}_flag'].str.contains(self._model_flag)),
col_name
] = np.NaN

# Conditionally, remove flags from DataFrame
if not self._flags:
Expand All @@ -137,9 +143,9 @@ def _filter_model(self) -> None:
inplace=True)

# Drop NaN-only rows
self._data.dropna(how='all', inplace=True)
self._data.dropna(how='all', subset=columns, inplace=True)

def _init_timeseries(
def _init_time_series(
self,
loc: Union[pd.DataFrame, Point, list, str], # Station(s) or geo point
start: datetime = None,
Expand Down
Loading

0 comments on commit 5a5602d

Please sign in to comment.