diff --git a/.github/workflows/flake8.yaml b/.github/workflows/flake8.yaml index 286cece7..c71678e4 100644 --- a/.github/workflows/flake8.yaml +++ b/.github/workflows/flake8.yaml @@ -29,6 +29,6 @@ jobs: - name: List changed files run: | git diff --compact-summary "origin/$GITHUB_BASE_REF" - - name: Run linter on changed files + - name: Run linter run: | - git diff "origin/$GITHUB_BASE_REF" -- "*.py" | flake8 . --config=.flake8 --diff --count --statistics --show-source + flake8 . --config=.flake8 --count --statistics --show-source diff --git a/docs/sphinx/source/changelog.rst b/docs/sphinx/source/changelog.rst index 5ff6ed6b..4260e3db 100644 --- a/docs/sphinx/source/changelog.rst +++ b/docs/sphinx/source/changelog.rst @@ -1,6 +1,7 @@ RdTools Change Log ================== .. include:: changelog/pending.rst +.. include:: changelog/v2.1.4.rst .. include:: changelog/v2.2.0-beta.0.rst .. include:: changelog/v2.1.3.rst .. include:: changelog/v2.1.2.rst diff --git a/docs/sphinx/source/changelog/pending.rst b/docs/sphinx/source/changelog/pending.rst deleted file mode 100644 index 43f76aa0..00000000 --- a/docs/sphinx/source/changelog/pending.rst +++ /dev/null @@ -1,9 +0,0 @@ -************************ -Pending -************************ - -Requirements ------------- -* Bump ``Pillow==9.3.0`` in ``requirements.txt`` (:pull:`349`) -* Bump ``jupyter-core==4.11.2`` in ``docs\notebook_requirements.txt`` (:pull:`350`) - diff --git a/docs/sphinx/source/changelog/v2.1.4.rst b/docs/sphinx/source/changelog/v2.1.4.rst new file mode 100644 index 00000000..e7701d4c --- /dev/null +++ b/docs/sphinx/source/changelog/v2.1.4.rst @@ -0,0 +1,35 @@ +************************* +v2.1.4 (December 1, 2022) +************************* + +Bug Fixes +--------- +* :py:func:`~rdtools.degradation.degradation_year_on_year` no longer raises + an error for inputs exactly two years long (:pull:`339`) +* :py:func:`~rdtools.plotting.soiling_interval_plot` no longer ignores the optional + ``point_color``, ``profile_color``, ``point_alpha``, and ``profile_alpha`` parameters. + (:issue:`343`, :pull:`345`) + +Testing +------- +* Added a CI notebook check (:pull:`270`) + +Requirements +------------ +* Upgrade the notebook environment from python 3.7 to python 3.10. + Several dependency versions in ``docs/notebook_requirements.txt`` are + updated as well. (:issue:`319`, :pull:`326`) +* Bump ``ipython==7.16.3``, ``jupyter-console==6.4.0``, + and ``prompt-toolkit==3.0.27`` in ``docs/notebook_requirements.txt`` + and bump ``Pillow==9.3.0`` in ``requirements.txt`` (:pull:`314`, :pull:`349`) +* Bump ``jupyter-core==4.11.2`` in ``docs\notebook_requirements.txt`` (:pull:`350`) +* Bump ``sphinx`` version from 3.2 to 4.5 and ``nbsphinx`` version + from 0.8.5 to 0.8.8 in the optional ``[doc]`` requirements (:pull:`317`, :pull:`325`) +* A number of other requirements updates (:pull:`337`) + +Contributors +------------ +* Sandra Villamar (:ghuser:`SandraVillamar`) +* Michael Deceglie (:ghuser:`mdeceglie`) +* Chris Deline (:ghuser:`cdeline`) +* Kevin Anderson (:ghuser:`kanderso-nrel`) diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index ee084ec7..ff39965b 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -127,6 +127,7 @@ def setup(app): # based on # https://gist.github.com/flying-sheep/b65875c0ce965fbdd1d9e5d0b9851ef1 + def get_obj_module(qualname): """ Get a module/class/attribute and its original module by qualname. diff --git a/rdtools/degradation.py b/rdtools/degradation.py index c7db959f..36a6fbcb 100644 --- a/rdtools/degradation.py +++ b/rdtools/degradation.py @@ -244,11 +244,19 @@ def degradation_year_on_year(energy_normalized, recenter=True, raise ValueError('energy_normalized must not be ' 'more frequent than daily') - # Detect less than 2 years of data - if energy_normalized.index[-1] - energy_normalized.index[0] < \ - pd.Timedelta('730d'): - raise ValueError('must provide at least two years of ' - 'normalized energy') + # Detect less than 2 years of data. This is complicated by two things: + # - leap days muddle the precise meaning of "two years of data". + # - can't just check the number of days between the first and last + # index values, since non-daily (e.g. weekly) inputs span + # a longer period than their index values directly indicate. + # See the unit tests for several motivating cases. + if energy_normalized.index.inferred_freq is not None: + step = pd.tseries.frequencies.to_offset(energy_normalized.index.inferred_freq) + else: + step = energy_normalized.index.to_series().diff().median() + + if energy_normalized.index[-1] < energy_normalized.index[0] + pd.DateOffset(years=2) - step: + raise ValueError('must provide at least two years of normalized energy') # If circular block bootstrapping... if uncertainty_method == 'circular_block': @@ -287,6 +295,7 @@ def degradation_year_on_year(energy_normalized, recenter=True, df.index = df.dt yoy_result = df.yoy.dropna() + df_right = df.set_index(df.dt_right).drop_duplicates('dt_right') df['usage_of_points'] = df.yoy.notnull().astype(int).add( df_right.yoy.notnull().astype(int), fill_value=0) diff --git a/rdtools/plotting.py b/rdtools/plotting.py index 7c5b2991..cb86329e 100644 --- a/rdtools/plotting.py +++ b/rdtools/plotting.py @@ -238,8 +238,8 @@ def soiling_interval_plot(soiling_info, normalized_yield, point_alpha=0.5, sratio = soiling_info['soiling_ratio_perfect_clean'] fig, ax = plt.subplots() renormalized = normalized_yield / soiling_info['renormalizing_factor'] - ax.plot(renormalized.index, renormalized, 'o') - ax.plot(sratio.index, sratio, 'o') + ax.plot(renormalized.index, renormalized, 'o', c=point_color, alpha=point_alpha) + ax.plot(sratio.index, sratio, 'o', c=profile_color, alpha=profile_alpha) ax.set_ylim(ymin, ymax) ax.set_ylabel('Renormalized energy') diff --git a/rdtools/test/degradation_test.py b/rdtools/test/degradation_test.py index 676a4a6f..8e5ae557 100644 --- a/rdtools/test/degradation_test.py +++ b/rdtools/test/degradation_test.py @@ -1,6 +1,7 @@ """ Degradation Module Tests. """ import unittest +import pytest import sys import pandas as pd @@ -185,6 +186,32 @@ def test_usage_of_points(self): self.assertTrue((np.sum(rd_result[2]['usage_of_points'])) == 1462) +@pytest.mark.parametrize('start,end,freq', [ + ('2014-01-01', '2015-12-31', 'D'), # no leap day + ('2015-01-01', '2016-12-31', 'D'), # leap day included in index + ('2015-01-01', '2016-12-29', '7D'), # leap day in period but not in index + ('2016-06-01', '2018-05-31', 'D'), # leap year, but no leap day in period + # ('2016-02-29', '2018-02-28', 'd'), # starts on leap day (doesn't work) + ('2014-03-01', '2016-02-29', 'D'), # ends on leap day + ('2015-01-01', '2016-12-31', 'M'), # month end + ('2015-01-01', '2016-12-31', 'MS'), # month start +]) +def test_yoy_two_years_error(start, end, freq): + # GH 339 + times = pd.date_range(start, end, freq=freq) + series = pd.Series(1, index=times) + # introduce NaN at the end to ensure that the 2 year requirement applies to + # timestamps, not non-nan values: + series.iloc[-5:] = np.nan + # should not raise an error + _ = degradation_year_on_year(series) + # but if we shorten it by one element, then it should: + with pytest.raises(ValueError, match='must provide at least two years'): + _ = degradation_year_on_year(series.iloc[:-1]) + with pytest.raises(ValueError, match='must provide at least two years'): + _ = degradation_year_on_year(series.iloc[1:]) + + if __name__ == '__main__': # Initialize logger when run as a module: # python -m tests.degradation_test