diff --git a/.github/workflows/build_tests.yml b/.github/workflows/build_tests.yml index 2304b6b..744dd34 100644 --- a/.github/workflows/build_tests.yml +++ b/.github/workflows/build_tests.yml @@ -1,4 +1,4 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python +# This workflow will install Python dependencies, run tests and coverage # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: build @@ -12,13 +12,17 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10'] + os: [windows-latest, macOS-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python --version @@ -30,141 +34,74 @@ jobs: python setup.py bdist_wheel ls dist/* - name: Save wheel - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: - name: wheel - path: dist/pecos*.whl + name: pecos_${{ matrix.python-version }}_${{ matrix.os }}.whl + path: dist/pecos* test: - name: Import test + name: Test install and usage of pecos needs: build runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ['3.8', '3.9', '3.10'] os: [windows-latest, macOS-latest, ubuntu-latest] steps: - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Download wheel - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: - name: wheel + name: pecos_${{ matrix.python-version }}_${{ matrix.os }}.whl - name: Install pecos run: | python -m pip install --upgrade pip - pip install wheel - pip install --find-links=. pecos - - name: Import test + pip install wheel numpy pandas jinja2 matplotlib pytest + pip install --no-index --pre --find-links=. pecos + - name: Usage of pecos run: | python -c "import pecos" - windows: - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - os: [windows-latest] - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python --version - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install coveralls - python setup.py develop - - name: Run Tests - run: | - coverage erase - coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=pecos --omit="*/tests/*" -m nose -v --nologcapture --with-doctest --traverse-namespace pecos - env: - COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - - name: Save coverage - uses: actions/upload-artifact@v2 - # if: ${{ matrix.os != 'windows-latest' }} - with: - name: coverage - path: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - - macOS: + create_coverage_reports: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - os: [macOS-latest] + python-version: ['3.8', '3.9', '3.10'] + os: [windows-latest, macOS-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python --version - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install coveralls - python setup.py develop - - name: Run Tests - run: | - coverage erase - coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=pecos --omit="*/tests/*" -m nose -v --nologcapture --with-doctest --traverse-namespace --doctest-extension=.rst documentation/*.rst pecos - env: - COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - - name: Save coverage - uses: actions/upload-artifact@v2 - # if: ${{ matrix.os != 'windows-latest' }} + uses: actions/setup-python@v4 with: - name: coverage - path: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - - linux: - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v2 - - name: Setup conda - uses: s-weigand/setup-conda@v1 - with: - update-conda: true python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - conda config --set always_yes yes --set changeps1 no python --version python -m pip install --upgrade pip pip install -r requirements.txt - pip install coveralls - python setup.py develop + python -m pip install -e . - name: Run Tests run: | coverage erase - coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=pecos --omit="*/tests/*" -m nose -v --nologcapture --with-doctest --traverse-namespace pecos + coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=pecos --omit="*/tests/*" -m pytest --doctest-modules --doctest-glob="*.rst" pecos + coverage run --context=${{ matrix.os }}.py${{ matrix.python-version }} --source=pecos --omit="*/tests/*" --append -m pytest --doctest-glob="*.rst" documentation env: COVERAGE_FILE: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - name: Save coverage uses: actions/upload-artifact@v2 - # if: ${{ matrix.os != 'windows-latest' }} with: name: coverage path: .coverage.${{ matrix.python-version }}.${{ matrix.os }} - coverage: - needs: [ windows, macOS, linux ] - runs-on: windows-latest + combine_reports: + needs: [ create_coverage_reports ] + runs-on: ubuntu-latest steps: - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 - uses: actions/checkout@v2 @@ -172,8 +109,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install coveralls - python setup.py develop + python -m pip install -e . - name: Download coverage artifacts from test matrix uses: actions/download-artifact@v2 with: @@ -204,13 +140,13 @@ jobs: name: coverage path: htmlcov - coveralls: - needs: [ windows, macOS, linux ] + combine_reports_upload_coveralls: + needs: [ create_coverage_reports ] runs-on: ubuntu-latest continue-on-error: true steps: - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 - uses: actions/checkout@v2 @@ -219,7 +155,7 @@ jobs: python -m pip install --upgrade pip pip install coveralls pip install -r requirements.txt - python setup.py develop + python -m pip install -e . - name: Download coverage artifacts from test matrix uses: actions/download-artifact@v2 with: diff --git a/.github/workflows/quick_check.yml b/.github/workflows/quick_check.yml index 3185935..24eafc1 100644 --- a/.github/workflows/quick_check.yml +++ b/.github/workflows/quick_check.yml @@ -17,15 +17,16 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 - name: Install packages run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install --upgrade coverage nose + pip install --upgrade coverage pytest python setup.py develop - name: Run tests run: | - nosetests -v --nologcapture --with-doctest --with-coverage --cover-package=pecos --cover-min-percentage=70 pecos + coverage run --source=pecos -m pytest --doctest-modules --doctest-glob="*.rst" pecos + coverage report --fail-under=70 diff --git a/documentation/developers.rst b/documentation/developers.rst index 540f33b..ffbb44d 100644 --- a/documentation/developers.rst +++ b/documentation/developers.rst @@ -8,9 +8,9 @@ The following services are used for software quality assurance: * Test coverage statistics are collected using Coveralls at https://coveralls.io/github/sandialabs/pecos. * The current release is hosted on PyPI at https://pypi.python.org/pypi/pecos. -Tests can be run locally using nosetests:: +Tests can be run locally using pytest:: - nosetests -v --with-coverage --cover-package=pecos pecos + pytest pecos Software developers are expected to follow standard practices to document and test new code. Pull requests will be reviewed by the core development team. diff --git a/documentation/installation.rst b/documentation/installation.rst index 2030cdb..d27f468 100644 --- a/documentation/installation.rst +++ b/documentation/installation.rst @@ -1,7 +1,7 @@ Installation ====================================== -Pecos requires Python (tested on 3.6, 3.7, 3.8, and 3.9) along with several Python +Pecos requires Python (tested on 3.8, 3.9, and 3.10) along with several Python package dependencies. Information on installing and using Python can be found at https://www.python.org/. Python distributions, such as Anaconda, are recommended to manage the Python interface. diff --git a/documentation/results.rst b/documentation/results.rst index 9adc909..6f1dea5 100644 --- a/documentation/results.rst +++ b/documentation/results.rst @@ -53,8 +53,8 @@ A subset of quality control test results from the :ref:`simple_example` are show >>> pd.set_option('display.expand_frame_repr', False) >>> from os.path import abspath, dirname, join >>> import pecos - >>> docdir = dirname(abspath(__file__)) - >>> datadir = join(docdir,'..','pecos', 'tests', 'data') + >>> packagedir = dirname(abspath(pecos.__file__)) + >>> datadir = join(packagedir, 'tests', 'data') >>> pm = pecos.monitoring.PerformanceMonitoring() >>> test_results = pd.read_csv(join(datadir,'Simple_test_results.csv'), index_col=0) # This file is tested against output from the Simple example >>> pm.test_results = test_results.loc[0:7,:] diff --git a/documentation/whatsnew.rst b/documentation/whatsnew.rst index 242edd6..159adc1 100644 --- a/documentation/whatsnew.rst +++ b/documentation/whatsnew.rst @@ -1,7 +1,7 @@ Release Notes ================ -.. include:: whatsnew/v0.2.1.rst +.. include:: whatsnew/v0.3.0.rst .. include:: whatsnew/v0.2.0.rst diff --git a/documentation/whatsnew/v0.2.1.rst b/documentation/whatsnew/v0.3.0.rst similarity index 52% rename from documentation/whatsnew/v0.2.1.rst rename to documentation/whatsnew/v0.3.0.rst index 29b48c8..af74840 100644 --- a/documentation/whatsnew/v0.2.1.rst +++ b/documentation/whatsnew/v0.3.0.rst @@ -1,10 +1,10 @@ -.. _whatsnew_021: +.. _whatsnew_030: -v0.2.1 (main) +v0.3.0 (main) -------------------------- * Bug fix in custom static and streaming quality control tests to use a specific column * Minor updates to address pandas and yaml depreciation warnings -* Minor updates for testing and documentation -* Added GitHub Actions and Python 3.9 tests - +* Updated test to use pytest instead of nose +* Added GitHub Actions +* Added Python 3.9 and 3.10 tests, dropped 3.6 and 3.7 tests diff --git a/pecos/__init__.py b/pecos/__init__.py index 7f274ba..406b65e 100644 --- a/pecos/__init__.py +++ b/pecos/__init__.py @@ -6,7 +6,7 @@ from pecos import utils from pecos import pv -__version__ = '0.2.1' +__version__ = '0.3.0' __copyright__ = """Copyright 2016 National Technology & Engineering Solutions of Sandia, LLC (NTESS). Under the terms of Contract diff --git a/pecos/graphics.py b/pecos/graphics.py index 76a0bf1..34168cd 100644 --- a/pecos/graphics.py +++ b/pecos/graphics.py @@ -16,13 +16,8 @@ import textwrap import os import logging +import pytest -try: - from nose.tools import nottest as _nottest -except ImportError: - def _nottest(afunction): - return afunction - NoneType = type(None) logger = logging.getLogger(__name__) @@ -425,7 +420,7 @@ def plot_doy_heatmap(data, cmap='nipy_spectral', vmin=None, vmax=None, ax.set_ylabel("Time of day (minutes)") plt.tight_layout() -@_nottest +@pytest.mark.skip() def plot_test_results(data, test_results, tfilter=None, image_format='png', dpi=500, figsize=(7.0,3.0), date_formatter=None, filename_root='test_results'): @@ -489,7 +484,7 @@ def plot_test_results(data, test_results, tfilter=None, image_format='png', 'Missing timestamp', 'Nonmonotonic timestamp'] test_results = test_results[-test_results['Error Flag'].isin(remove_error_flags)] - grouped = test_results.groupby(['Variable Name']) + grouped = test_results.groupby('Variable Name') for col_name, test_results_group in grouped: logger.info("Creating graphic for " + col_name) diff --git a/pecos/io.py b/pecos/io.py index 4a5ddb2..2f878ee 100644 --- a/pecos/io.py +++ b/pecos/io.py @@ -21,12 +21,8 @@ import minimalmodbus except: pass +import pytest -try: - from nose.tools import nottest as _nottest -except ImportError: - def _nottest(afunction): - return afunction logger = logging.getLogger(__name__) @@ -56,11 +52,12 @@ def read_campbell_scientific(filename, index_col='TIMESTAMP', encoding=None): try: df = pd.read_csv(filename, skiprows=1, encoding=encoding, index_col=index_col, - parse_dates=True, dtype ='unicode', error_bad_lines=False) #, low_memory=False) + parse_dates=True, dtype ='unicode', on_bad_lines="skip") #, low_memory=False) df = df[2:] index = pd.to_datetime(df.index) Unnamed = df.filter(regex='Unnamed') - df = df.drop(Unnamed.columns, 1) + if Unnamed.columns.shape[0] > 0: + df = df.drop(Unnamed.columns, 1) df = pd.DataFrame(data = df.values, index = index, columns = df.columns, dtype='float64') except: logger.warning("Cannot extract database, CSV file reader failed " + filename) @@ -166,11 +163,11 @@ def write_metrics(metrics, filename='metrics.csv'): logger.info("Write metrics file") try: - previous_metrics = pd.read_csv(filename, index_col='TIMESTEP') #, parse_dates=True) + previous_metrics = pd.read_csv(filename, index_col='TIMESTEP', parse_dates=True) except: previous_metrics = pd.DataFrame() - metrics.index = metrics.index.to_native_types() # this is necessary when using time zones + #metrics.index = metrics.index.to_native_types() # no longer needed when using time zones metrics = metrics.combine_first(previous_metrics) if os.path.dirname(filename) == '': @@ -183,7 +180,7 @@ def write_metrics(metrics, filename='metrics.csv'): return full_filename -@_nottest +@pytest.mark.skip() def write_test_results(test_results, filename='test_results.csv'): """ Write test results file. diff --git a/pecos/monitoring.py b/pecos/monitoring.py index 17c784b..ecb3895 100644 --- a/pecos/monitoring.py +++ b/pecos/monitoring.py @@ -340,8 +340,8 @@ def check_timestamp(self, frequency, expected_start_time=None, timestamp_test=True, min_failures=min_failures) - # If not monotonic, sort df by timestamp - if not self.df.index.is_monotonic: + # If not monotonically increasing, sort df by timestamp + if not self.df.index.is_monotonic_increasing: self.df = self.df.sort_index() # Check for duplicate timestamps @@ -511,7 +511,7 @@ def check_delta(self, bound, window, key=None, direction=None, assert isinstance(key, (NoneType, str)), 'key must be None or of type string' assert direction in [None, 'positive', 'negative'], "direction must None or the string 'positive' or 'negative'" assert isinstance(min_failures, int), 'min_failures must be of type int' - assert self.df.index.is_monotonic, 'index must be monotonic' + assert self.df.index.is_monotonic_increasing, 'index must be monotonically increasing' logger.info("Check for stagant data and/or abrupt changes using delta (max-min) within a rolling window") @@ -637,7 +637,7 @@ def check_outlier(self, bound, window=None, key=None, absolute_value=False, stre assert isinstance(absolute_value, bool), 'absolute_value must be of type bool' assert isinstance(streaming, bool), 'streaming must be of type bool' assert isinstance(min_failures, int), 'min_failures must be type int' - assert self.df.index.is_monotonic, 'index must be monotonic' + assert self.df.index.is_monotonic_increasing, 'index must be monotonically increasing' def outlier(data_pt, history): diff --git a/pecos/tests/test_graphics.py b/pecos/tests/test_graphics.py index 9222b1c..24a19aa 100644 --- a/pecos/tests/test_graphics.py +++ b/pecos/tests/test_graphics.py @@ -1,205 +1,211 @@ -from nose.tools import * +import unittest from os.path import abspath, dirname, join, isfile import os -import pecos import pandas as pd import numpy as np import inspect import matplotlib.pylab as plt +import pecos + testdir = dirname(abspath(inspect.getfile(inspect.currentframe()))) -datadir = abspath(join(testdir, 'data')) +datadir = abspath(join(testdir, 'data')) + -def test_plot_scatter1(): - filename = abspath(join(testdir, 'plot_scatter1.png')) - if isfile(filename): - os.remove(filename) +class TestGraphics(unittest.TestCase): + + def test_plot_scatter1(self): + filename = abspath(join(testdir, 'plot_scatter1.png')) + if isfile(filename): + os.remove(filename) + + x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) + y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - - plt.figure() - pecos.graphics.plot_scatter(x,y,xaxis_min=0.5, xaxis_max=6.5, yaxis_min=0.5, yaxis_max=3.5) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) + plt.figure() + pecos.graphics.plot_scatter(x,y,xaxis_min=0.5, xaxis_max=6.5, yaxis_min=0.5, yaxis_max=3.5) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) -def test_plot_scatter2(): - filename = abspath(join(testdir, 'plot_scatter2.png')) - if isfile(filename): - os.remove(filename) + def test_plot_scatter2(self): + filename = abspath(join(testdir, 'plot_scatter2.png')) + if isfile(filename): + os.remove(filename) + + x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), + 'x2' : pd.Series([4., 5., 6.], index=['a', 'b', 'c'])}) + y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), - 'x2' : pd.Series([4., 5., 6.], index=['a', 'b', 'c'])}) - y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - - plt.figure() - pecos.graphics.plot_scatter(x,y,title='scatter2') - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) + plt.figure() + pecos.graphics.plot_scatter(x,y,title='scatter2') + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) -def test_plot_scatter3(): - filename = abspath(join(testdir, 'plot_scatter3.png')) - if isfile(filename): - os.remove(filename) + def test_plot_scatter3(self): + filename = abspath(join(testdir, 'plot_scatter3.png')) + if isfile(filename): + os.remove(filename) + + y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), + 'y2' : pd.Series([4., 5., 6.], index=['a', 'b', 'c'])}) + x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - y = pd.DataFrame({'y1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), - 'y2' : pd.Series([4., 5., 6.], index=['a', 'b', 'c'])}) - x = pd.DataFrame({'x1' : pd.Series([1., 2., 3.], index=['a', 'b', 'c'])}) - - plt.figure() - pecos.graphics.plot_scatter(x,y,xaxis_min=0.5, xaxis_max=3.5, yaxis_min=0.5, yaxis_max=6.5) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) - -def test_plot_timeseries1(): - filename = abspath(join(testdir, 'plot_timeseries1.png')) - if isfile(filename): - os.remove(filename) - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - plt.figure() - pecos.graphics.plot_timeseries(df,yaxis_min=0, yaxis_max=20) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) - -def test_plot_timeseries2(): - filename = abspath(join(testdir, 'plot_timeseries2.png')) - if isfile(filename): - os.remove(filename) - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - tfilter = pd.Series(data = (df.index < index[3]), index = df.index) - - plt.figure() - pecos.graphics.plot_timeseries(df,tfilter, yaxis_min=0, yaxis_max=20) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) + plt.figure() + pecos.graphics.plot_scatter(x,y,xaxis_min=0.5, xaxis_max=3.5, yaxis_min=0.5, yaxis_max=6.5) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) -def test_plot_interactive_timeseries1(): - filename = abspath(join(testdir, 'plot_interactive_timeseries1.html')) - if isfile(filename): - os.remove(filename) + def test_plot_timeseries1(self): + filename = abspath(join(testdir, 'plot_timeseries1.png')) + if isfile(filename): + os.remove(filename) + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='D') - data = np.random.rand(periods, 4) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C', 'D']) + plt.figure() + pecos.graphics.plot_timeseries(df,yaxis_min=0, yaxis_max=20) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) - pecos.graphics.plot_interactive_timeseries(df, filename=filename, auto_open=False) - - assert_true(isfile(filename)) - -def test_plot_heatmap1(): - filename = abspath(join(testdir, 'plot_heatmap1.png')) - if isfile(filename): - os.remove(filename) - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='D') - data = np.random.rand(periods, 4) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C', 'D']) - - plt.figure() - pecos.graphics.plot_heatmap(df) - plt.savefig(filename, format='png', bbox_inches='tight', pad_inches = 0) - plt.close() - - assert_true(isfile(filename)) + def test_plot_timeseries2(self): + filename = abspath(join(testdir, 'plot_timeseries2.png')) + if isfile(filename): + os.remove(filename) + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + tfilter = pd.Series(data = (df.index < index[3]), index = df.index) + + plt.figure() + pecos.graphics.plot_timeseries(df,tfilter, yaxis_min=0, yaxis_max=20) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) -def test_plot_heatmap2(): - filename = abspath(join(testdir, 'plot_heatmap2.png')) - if isfile(filename): - os.remove(filename) + def test_plot_interactive_timeseries1(self): + filename = abspath(join(testdir, 'plot_interactive_timeseries1.html')) + if isfile(filename): + os.remove(filename) + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='D') + data = np.random.rand(periods, 4) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C', 'D']) + + pecos.graphics.plot_interactive_timeseries(df, filename=filename, auto_open=False) - data = np.array([[1,2],[3,4]]) - - plt.figure() - pecos.graphics.plot_heatmap(data, cmap='jet', show_axis=True) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) - -def test_plot_doy_heatmap1(): - filename = abspath(join(testdir, 'plot_doy_heatmap1.png')) - if isfile(filename): - os.remove(filename) - - periods = 5*24 # 5 days - index = pd.date_range('3/1/2016', periods=periods, freq='H') - data = np.random.rand(periods) - df = pd.DataFrame(data=data, index=index, columns=['A']) - - plt.figure() - pecos.graphics.plot_doy_heatmap(df['A']) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) - -def test_plot_doy_heatmap2(): - filename = abspath(join(testdir, 'plot_doy_heatmap2.png')) - if isfile(filename): - os.remove(filename) - - periods = 365*12 - index = pd.date_range('1/1/2016', periods=periods, freq='2H') - data = np.random.rand(periods) - df = pd.DataFrame(data=data, index=index, columns=['A']) - overlay = pd.DataFrame(index=[1,100,200,300,365], - data={'A': [40000,20000,60000,10000,5000], - 'B': [60000,70000,75000,50000,65000]}) - - plt.figure() - pecos.graphics.plot_doy_heatmap(df['A'], cmap='gray', overlay=overlay) - plt.savefig(filename, format='png') - plt.close() - - assert_true(isfile(filename)) - -def test_plot_test_results1(): - filename_root = abspath(join(testdir, 'plot_test_results1')) - pm = pecos.monitoring.PerformanceMonitoring() - - graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, pm.tfilter, - filename_root=filename_root) - - assert_equals(graphics,[]) - -def test_plot_test_results2(): - filename_root = abspath(join(testdir, 'plot_test_results2')) - pm = pecos.monitoring.PerformanceMonitoring() - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - tfilter = pd.Series(data = (df.index < index[3]), index = df.index) - - pm.add_dataframe(df) - pm.add_time_filter(tfilter) - - pm.check_range([0,7]) # 2 test failures - - graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, pm.tfilter, - filename_root=filename_root) - - assert_equals(len(graphics),2) + self.assertTrue(isfile(filename)) + + def test_plot_heatmap1(self): + filename = abspath(join(testdir, 'plot_heatmap1.png')) + if isfile(filename): + os.remove(filename) + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='D') + data = np.random.rand(periods, 4) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C', 'D']) + + plt.figure() + pecos.graphics.plot_heatmap(df) + plt.savefig(filename, format='png', bbox_inches='tight', pad_inches = 0) + plt.close() + + self.assertTrue(isfile(filename)) + + def test_plot_heatmap2(self): + filename = abspath(join(testdir, 'plot_heatmap2.png')) + if isfile(filename): + os.remove(filename) + + data = np.array([[1,2],[3,4]]) + + plt.figure() + pecos.graphics.plot_heatmap(data, cmap='jet', show_axis=True) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) + + def test_plot_doy_heatmap1(self): + filename = abspath(join(testdir, 'plot_doy_heatmap1.png')) + if isfile(filename): + os.remove(filename) + + periods = 5*24 # 5 days + index = pd.date_range('3/1/2016', periods=periods, freq='H') + data = np.random.rand(periods) + df = pd.DataFrame(data=data, index=index, columns=['A']) + + plt.figure() + pecos.graphics.plot_doy_heatmap(df['A']) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) + + def test_plot_doy_heatmap2(self): + filename = abspath(join(testdir, 'plot_doy_heatmap2.png')) + if isfile(filename): + os.remove(filename) + + periods = 365*12 + index = pd.date_range('1/1/2016', periods=periods, freq='2H') + data = np.random.rand(periods) + df = pd.DataFrame(data=data, index=index, columns=['A']) + overlay = pd.DataFrame(index=[1,100,200,300,365], + data={'A': [40000,20000,60000,10000,5000], + 'B': [60000,70000,75000,50000,65000]}) + + plt.figure() + pecos.graphics.plot_doy_heatmap(df['A'], cmap='gray', overlay=overlay) + plt.savefig(filename, format='png') + plt.close() + + self.assertTrue(isfile(filename)) + + def test_plot_test_results1(self): + filename_root = abspath(join(testdir, 'plot_test_results1')) + pm = pecos.monitoring.PerformanceMonitoring() + + graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, pm.tfilter, + filename_root=filename_root) + + self.assertEqual(graphics,[]) + + def test_plot_test_results2(self): + filename_root = abspath(join(testdir, 'plot_test_results2')) + pm = pecos.monitoring.PerformanceMonitoring() + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + tfilter = pd.Series(data = (df.index < index[3]), index = df.index) + + pm.add_dataframe(df) + pm.add_time_filter(tfilter) + + pm.check_range([0,7]) # 2 test failures + + graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, pm.tfilter, + filename_root=filename_root) + + self.assertEqual(len(graphics),2) + - \ No newline at end of file +if __name__ == '__main__': + unittest.main() diff --git a/pecos/tests/test_io.py b/pecos/tests/test_io.py index aad27a3..a62ff74 100644 --- a/pecos/tests/test_io.py +++ b/pecos/tests/test_io.py @@ -1,198 +1,201 @@ -from nose.tools import * +import unittest +import os from os.path import abspath, dirname, join, isfile -import pecos import pandas as pd -import os import numpy as np import inspect import matplotlib.pylab as plt import logging +import pecos + testdir = dirname(abspath(inspect.getfile(inspect.currentframe()))) datadir = abspath(join(testdir, 'data')) -def test_read_campbell_scientific(): - file_name = join(datadir,'TEST_db1_2014_01_01.dat') - assert_true(isfile(file_name)) - - df = pecos.io.read_campbell_scientific(file_name, 'TIMESTAMP') - assert_equals((48,11), df.shape) - -def test_write_metrics1(): - filename = abspath(join(testdir, 'metrics.csv')) - if isfile(filename): - os.remove(filename) - - metrics = pd.DataFrame({'metric1' : pd.Series([1.], index=[pd.datetime(2016,1,1)])}) - filename = pecos.io.write_metrics(metrics, filename) - assert_true(isfile(filename)) - - from_file1 = pd.read_csv(filename) - assert_equals(from_file1.shape, (1,2)) - - # append another date - metrics = pd.DataFrame({'metric1' : pd.Series([2.], index=[pd.datetime(2016,1,2)])}) - filename = pecos.io.write_metrics(metrics, filename) - - from_file2 = pd.read_csv(filename) - assert_equals(from_file2.shape, (2,2)) - - # append another metric - metrics = pd.DataFrame({'metric2' : pd.Series([3.], index=[pd.datetime(2016,1,2)])}) - filename = pecos.io.write_metrics(metrics, filename) - - from_file3 = pd.read_csv(filename) - assert_equals(from_file3.shape, (2,3)) - -def test_write_test_results1(): - filename = abspath(join(testdir, 'test_results.csv')) - if isfile(filename): - os.remove(filename) - - pm = pecos.monitoring.PerformanceMonitoring() - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - tfilter = pd.Series(data = (df.index < index[3]), index = df.index) - pm.add_dataframe(df) - pm.add_time_filter(tfilter) - pm.check_range([0,7]) # 2 test failures - - filename = pecos.io.write_test_results(pm.test_results) - from_file = pd.read_csv(filename) +class TestIO(unittest.TestCase): - assert_true(isfile(filename)) - assert_equals(from_file.shape, (2,6)) - -def test_write_monitoring_report1(): # empty database - filename = abspath(join(testdir, 'monitoring_report.html')) - if isfile(filename): - os.remove(filename) + def test_read_campbell_scientific(self): + file_name = join(datadir,'TEST_db1_2014_01_01.dat') + self.assertTrue(isfile(file_name)) - pm = pecos.monitoring.PerformanceMonitoring() - - filename = pecos.io.write_monitoring_report(pm.df, pm.test_results) - - assert_true(isfile(filename)) - -def test_write_monitoring_report2():# with test results and graphics (encoded and linked) - filename1 = abspath(join(testdir, 'test_write_monitoring_report2_linked_graphics.html')) - filename2 = abspath(join(testdir, 'test_write_monitoring_report2_encoded_graphics.html')) - graphics_filename = abspath(join(testdir, 'custom_graphic.png')) - if isfile(filename1): - os.remove(filename1) - if isfile(filename2): - os.remove(filename2) - if isfile(graphics_filename): - os.remove(graphics_filename) - - pecos.logger.initialize() - logger = logging.getLogger('pecos') - - pm = pecos.monitoring.PerformanceMonitoring() - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - tfilter = pd.Series(data = (df.index < index[3]), index = df.index) - pm.add_dataframe(df) - pm.add_time_filter(tfilter) - pm.check_range([0,7]) # 2 test failures - - filename_root = abspath(join(testdir, 'monitoring_report_graphic')) - test_results_graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, - filename_root=filename_root) - - plt.figure() - plt.plot([1, 2, 3],[1, 2, 3]) - plt.savefig(graphics_filename, format='png') - plt.close() - custom_graphics = [graphics_filename] - - logger.warning('Add a note') - - filename1 = pecos.io.write_monitoring_report(pm.df, pm.test_results, - test_results_graphics, custom_graphics, encode=False, - filename='test_write_monitoring_report2_linked_graphics.html') - - assert_true(isfile(filename1)) - - filename2 = pecos.io.write_monitoring_report(pm.df, pm.test_results, - test_results_graphics, custom_graphics, encode=True, - filename=filename2) - - assert_true(isfile(filename2)) - - -def test_write_dashboard1(): # empty content - filename = abspath(join(testdir, 'dashboard.html')) - if isfile(filename): - os.remove(filename) - - column_names = ['loc1', 'loc2'] - row_names = ['sys1', 'sys2'] - content = {} - content[('sys1', 'loc1')] = {} - content[('sys1', 'loc2')] = {} - content[('sys2', 'loc1')] = {} - content[('sys2', 'loc2')] = {} - - filename = pecos.io.write_dashboard(column_names, row_names, content) - - assert_true(isfile(filename)) - - -def test_write_dashboard2(): # with text, graphics (encoded and linked), tables, and links - filename1 = abspath(join(testdir, 'test_write_dashboard2_linked_graphics.html.html')) - filename2 = abspath(join(testdir, 'test_write_dashboard2_encoded_graphics.html.html')) - graphics_filename = abspath(join(testdir, 'dashboard_graphic.png')) - if isfile(filename1): - os.remove(filename1) - if isfile(filename2): - os.remove(filename2) - if isfile(graphics_filename): - os.remove(graphics_filename) - - plt.figure() - plt.plot([1, 2, 3],[1, 2, 3]) - plt.savefig(graphics_filename, format='png') - plt.close() - - column_names = ['loc1', 'loc2'] - row_names = ['sys1', 'sys2'] - content = {} - content[('sys1', 'loc1')] = {'text': 'sys1-loc1 text', - 'graphics': [graphics_filename], - 'link': {'Google': 'https://www.google.com', 'Pecos': 'http://pecos.readthedocs.io'} } - content[('sys1', 'loc2')] = {'text': 'sys1-loc2 text', - 'table': pd.DataFrame({'sys1': [1,2,3]}).to_html()} - content[('sys2', 'loc1')] = {'text': 'sys2-loc1 text', - 'graphics': [graphics_filename], - 'link': {'Google': 'https://www.google.com', 'Pecos': 'http://pecos.readthedocs.io'} } - content[('sys2', 'loc2')] = {'text': 'sys2-loc2 text', - 'table': pd.DataFrame({'sys2': [2,4,6]}).to_html()} - - filename1 = pecos.io.write_dashboard(column_names, row_names, content, filename1, encode=False) - - assert_true(isfile(filename1)) + df = pecos.io.read_campbell_scientific(file_name, 'TIMESTAMP') + self.assertEqual((48,11), df.shape) + + def test_write_metrics1(self): + filename = abspath(join(testdir, 'metrics.csv')) + if isfile(filename): + os.remove(filename) + + metrics = pd.DataFrame({'metric1' : pd.Series([1.], index=[pd.to_datetime("2016-01-01")])}) + filename = pecos.io.write_metrics(metrics, filename) + self.assertTrue(isfile(filename)) + + from_file1 = pd.read_csv(filename) + self.assertEqual(from_file1.shape, (1,2)) + + # append another date + metrics = pd.DataFrame({'metric1' : pd.Series([2.], index=[pd.to_datetime("2016-01-02")])}) + filename = pecos.io.write_metrics(metrics, filename) + + from_file2 = pd.read_csv(filename) + self.assertEqual(from_file2.shape, (2,2)) + + # append another metric + metrics = pd.DataFrame({'metric2' : pd.Series([3.], index=[pd.to_datetime("2016-01-02")])}) + filename = pecos.io.write_metrics(metrics, filename) + + from_file3 = pd.read_csv(filename) + self.assertEqual(from_file3.shape, (2,3)) + + def test_write_test_results1(self): + filename = abspath(join(testdir, 'test_results.csv')) + if isfile(filename): + os.remove(filename) + + pm = pecos.monitoring.PerformanceMonitoring() + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + tfilter = pd.Series(data = (df.index < index[3]), index = df.index) + pm.add_dataframe(df) + pm.add_time_filter(tfilter) + pm.check_range([0,7]) # 2 test failures + + filename = pecos.io.write_test_results(pm.test_results) + from_file = pd.read_csv(filename) + + self.assertTrue(isfile(filename)) + self.assertEqual(from_file.shape, (2,6)) - filename2 = pecos.io.write_dashboard(column_names, row_names, content, filename2, encode=True) + def test_write_monitoring_report1(self): # empty database + filename = abspath(join(testdir, 'monitoring_report.html')) + if isfile(filename): + os.remove(filename) + + pm = pecos.monitoring.PerformanceMonitoring() - assert_true(isfile(filename2)) + filename = pecos.io.write_monitoring_report(pm.df, pm.test_results) + + self.assertTrue(isfile(filename)) + + def test_write_monitoring_report2(self):# with test results and graphics (encoded and linked) + filename1 = abspath(join(testdir, 'test_write_monitoring_report2_linked_graphics.html')) + filename2 = abspath(join(testdir, 'test_write_monitoring_report2_encoded_graphics.html')) + graphics_filename = abspath(join(testdir, 'custom_graphic.png')) + if isfile(filename1): + os.remove(filename1) + if isfile(filename2): + os.remove(filename2) + if isfile(graphics_filename): + os.remove(graphics_filename) + + pecos.logger.initialize() + logger = logging.getLogger('pecos') + + pm = pecos.monitoring.PerformanceMonitoring() + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + tfilter = pd.Series(data = (df.index < index[3]), index = df.index) + pm.add_dataframe(df) + pm.add_time_filter(tfilter) + pm.check_range([0,7]) # 2 test failures + + filename_root = abspath(join(testdir, 'monitoring_report_graphic')) + test_results_graphics = pecos.graphics.plot_test_results(pm.df, pm.test_results, + filename_root=filename_root) + + plt.figure() + plt.plot([1, 2, 3],[1, 2, 3]) + plt.savefig(graphics_filename, format='png') + plt.close() + custom_graphics = [graphics_filename] + + logger.warning('Add a note') + + filename1 = pecos.io.write_monitoring_report(pm.df, pm.test_results, + test_results_graphics, custom_graphics, encode=False, + filename='test_write_monitoring_report2_linked_graphics.html') + + self.assertTrue(isfile(filename1)) + + filename2 = pecos.io.write_monitoring_report(pm.df, pm.test_results, + test_results_graphics, custom_graphics, encode=True, + filename=filename2) + + self.assertTrue(isfile(filename2)) -def test_email_message(): - subject = 'test subject' - body = 'test body' - recipient = ['recipient.email.address'] - sender = 'sender.email.address' + + def test_write_dashboard1(self): # empty content + filename = abspath(join(testdir, 'dashboard.html')) + if isfile(filename): + os.remove(filename) + + column_names = ['loc1', 'loc2'] + row_names = ['sys1', 'sys2'] + content = {} + content[('sys1', 'loc1')] = {} + content[('sys1', 'loc2')] = {} + content[('sys2', 'loc1')] = {} + content[('sys2', 'loc2')] = {} + + filename = pecos.io.write_dashboard(column_names, row_names, content) + + self.assertTrue(isfile(filename)) + + + def test_write_dashboard2(self): # with text, graphics (encoded and linked), tables, and links + filename1 = abspath(join(testdir, 'test_write_dashboard2_linked_graphics.html.html')) + filename2 = abspath(join(testdir, 'test_write_dashboard2_encoded_graphics.html.html')) + graphics_filename = abspath(join(testdir, 'dashboard_graphic.png')) + if isfile(filename1): + os.remove(filename1) + if isfile(filename2): + os.remove(filename2) + if isfile(graphics_filename): + os.remove(graphics_filename) + + plt.figure() + plt.plot([1, 2, 3],[1, 2, 3]) + plt.savefig(graphics_filename, format='png') + plt.close() + + column_names = ['loc1', 'loc2'] + row_names = ['sys1', 'sys2'] + content = {} + content[('sys1', 'loc1')] = {'text': 'sys1-loc1 text', + 'graphics': [graphics_filename], + 'link': {'Google': 'https://www.google.com', 'Pecos': 'http://pecos.readthedocs.io'} } + content[('sys1', 'loc2')] = {'text': 'sys1-loc2 text', + 'table': pd.DataFrame({'sys1': [1,2,3]}).to_html()} + content[('sys2', 'loc1')] = {'text': 'sys2-loc1 text', + 'graphics': [graphics_filename], + 'link': {'Google': 'https://www.google.com', 'Pecos': 'http://pecos.readthedocs.io'} } + content[('sys2', 'loc2')] = {'text': 'sys2-loc2 text', + 'table': pd.DataFrame({'sys2': [2,4,6]}).to_html()} + + filename1 = pecos.io.write_dashboard(column_names, row_names, content, filename1, encode=False) + + self.assertTrue(isfile(filename1)) + + filename2 = pecos.io.write_dashboard(column_names, row_names, content, filename2, encode=True) + + self.assertTrue(isfile(filename2)) + + def test_email_message(self): + subject = 'test subject' + body = 'test body' + recipient = ['recipient.email.address'] + sender = 'sender.email.address' + + msg = pecos.io._create_email_message(subject, body, recipient, sender) - msg = pecos.io._create_email_message(subject, body, recipient, sender) - - assert_true(subject in msg.as_string()) - assert_true(body in msg.as_string()) - assert_true(recipient[0] in msg.as_string()) - assert_true(sender in msg.as_string()) + self.assertTrue(subject in msg.as_string()) + self.assertTrue(body in msg.as_string()) + self.assertTrue(recipient[0] in msg.as_string()) + self.assertTrue(sender in msg.as_string()) if __name__ == '__main__': - test_write_metrics1() \ No newline at end of file + unittest.main() \ No newline at end of file diff --git a/pecos/tests/test_logger.py b/pecos/tests/test_logger.py index 5677ad4..6de7122 100644 --- a/pecos/tests/test_logger.py +++ b/pecos/tests/test_logger.py @@ -1,8 +1,14 @@ -from nose.tools import * -import pandas as pd +import unittest import pecos -def test_initialize(): - pecos.logger.initialize() - - assert_equals(0, 0) + +class TestLogger(unittest.TestCase): + + def test_initialize(self): + pecos.logger.initialize() + + self.assertEqual(0, 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/pecos/tests/test_metrics.py b/pecos/tests/test_metrics.py index 435ef5c..fcf26e3 100644 --- a/pecos/tests/test_metrics.py +++ b/pecos/tests/test_metrics.py @@ -1,166 +1,171 @@ -from nose.tools import * +import unittest from pandas.testing import assert_frame_equal from os.path import abspath, dirname, join -import pecos import numpy as np import pandas as pd +import pecos + testdir = dirname(abspath(__file__)) datadir = join(testdir,'data') -def test_pd_far(): - index = pd.date_range('1/1/2016', periods=4, freq='H') - - actual = np.array([[True, False, False], - [False, False, True], - [True, False, False], - [True, True, True]]) - actual = pd.DataFrame(data=actual, index=index, columns=['A', 'B', 'C']) - - obser = np.array([[True, False, True], - [True, False, True], - [True, True, False], - [True, False, False]]) - obser = pd.DataFrame(data=obser, index=index, columns=['A', 'B', 'C']) - - prob_detection = pecos.metrics.probability_of_detection(obser, actual) - false_alarm = pecos.metrics.false_alarm_rate(obser, actual) - - assert_almost_equal(prob_detection['A'], 0/1.0, 5) - assert_almost_equal(prob_detection['B'], 2/3.0, 5) - assert_almost_equal(prob_detection['C'], 1/2.0, 5) - - assert_almost_equal(false_alarm['A'], 1-3/3.0, 5) - assert_almost_equal(false_alarm['B'], 1-0/1.0, 5) - assert_almost_equal(false_alarm['C'], 1-1/2.0, 5) - -def test_integral(): - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,4,3], [7,6,np.nan], [4,8,4], [1,8.5,6]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - integral = pecos.metrics.time_integral(df) - print(integral) - assert_equal(integral['A'], 57600) - assert_equal(integral['B'], 83700) - assert_equal(integral['C'], 41400) - - # test with irregular timesteps - index = index[[0,1,3,4]] - data = data[[0,1,3,4]] - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - integral = pecos.metrics.time_integral(df) - - assert_equal(integral['A'], 46800) - assert_equal(integral['B'], 83700) - assert_equal(integral['C'], 54000) - -def test_derivative(): - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,4,3], [7,6,np.nan], [4,8,4], [1,8.5,6]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - derivative = pecos.metrics.time_derivative(df) - - expected = np.array([[3.0/3600, 2.0/3600, 0], - [3.0/3600, 2.0/3600, np.nan], - [0, 2.0/3600, 1.0/7200], - [-3.0/3600, 1.25/3600, np.nan], - [-3.0/3600, 0.5/3600, 2.0/3600]]) - expected = pd.DataFrame(data=expected, index=index, columns=['A', 'B', 'C']) - - assert_frame_equal(expected, derivative) - # test with irregular timesteps - index = index[[0,1,2,4]] - data = data[[0,1,2,4]] - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) +class TestMetrics(unittest.TestCase): - derivative = pecos.metrics.time_derivative(df) + def test_pd_far(self): + index = pd.date_range('1/1/2016', periods=4, freq='H') + + actual = np.array([[True, False, False], + [False, False, True], + [True, False, False], + [True, True, True]]) + actual = pd.DataFrame(data=actual, index=index, columns=['A', 'B', 'C']) + + obser = np.array([[True, False, True], + [True, False, True], + [True, True, False], + [True, False, False]]) + obser = pd.DataFrame(data=obser, index=index, columns=['A', 'B', 'C']) + + prob_detection = pecos.metrics.probability_of_detection(obser, actual) + false_alarm = pecos.metrics.false_alarm_rate(obser, actual) + + self.assertAlmostEqual(prob_detection['A'], 0/1.0, 5) + self.assertAlmostEqual(prob_detection['B'], 2/3.0, 5) + self.assertAlmostEqual(prob_detection['C'], 1/2.0, 5) + + self.assertAlmostEqual(false_alarm['A'], 1-3/3.0, 5) + self.assertAlmostEqual(false_alarm['B'], 1-0/1.0, 5) + self.assertAlmostEqual(false_alarm['C'], 1-1/2.0, 5) - expected = np.array([[3.0/3600, 2.0/3600, 0], - [3.0/3600, 2.0/3600, np.nan], - [1.0/3600, 1.75/3600, np.nan], - [-3.0/3600, 1.25/3600, np.nan]]) - expected = pd.DataFrame(data=expected, index=index, columns=['A', 'B', 'C']) + def test_integral(self): + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,4,3], [7,6,np.nan], [4,8,4], [1,8.5,6]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + + integral = pecos.metrics.time_integral(df) + print(integral) + self.assertEqual(integral['A'], 57600) + self.assertEqual(integral['B'], 83700) + self.assertEqual(integral['C'], 41400) + + # test with irregular timesteps + index = index[[0,1,3,4]] + data = data[[0,1,3,4]] + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + + integral = pecos.metrics.time_integral(df) + + self.assertEqual(integral['A'], 46800) + self.assertEqual(integral['B'], 83700) + self.assertEqual(integral['C'], 54000) - assert_frame_equal(expected, derivative) - -def test_qci_no_test_results(): - periods = 5 - np.random.seed(100) - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data=np.sin(np.random.rand(3,1)*np.arange(0,periods,1)) - df = pd.DataFrame(data=data.transpose(), index=index, columns=['A', 'B', 'C']) - trans = dict(zip(df.columns, [[col] for col in df.columns])) - - pm = pecos.monitoring.PerformanceMonitoring() - pm.add_dataframe(df) - pm.add_translation_dictionary(trans) - - mask = pm.mask - QCI = pecos.metrics.qci(mask) + def test_derivative(self): + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,4,3], [7,6,np.nan], [4,8,4], [1,8.5,6]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - assert_equal(mask.any().any(), True) - assert_equal(QCI['A'], 1) - assert_equal(QCI['B'], 1) - assert_equal(QCI['C'], 1) + derivative = pecos.metrics.time_derivative(df) -def test_qci_with_test_results(): - periods = 5 - np.random.seed(100) - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data=np.sin(np.random.rand(3,1)*np.arange(0,periods,1)) - df = pd.DataFrame(data=data.transpose(), index=index, columns=['A', 'B', 'C']) - trans = dict(zip(df.columns, [[col] for col in df.columns])) + expected = np.array([[3.0/3600, 2.0/3600, 0], + [3.0/3600, 2.0/3600, np.nan], + [0, 2.0/3600, 1.0/7200], + [-3.0/3600, 1.25/3600, np.nan], + [-3.0/3600, 0.5/3600, 2.0/3600]]) + expected = pd.DataFrame(data=expected, index=index, columns=['A', 'B', 'C']) - pm = pecos.monitoring.PerformanceMonitoring() - pm.add_dataframe(df) - pm.add_translation_dictionary(trans) + assert_frame_equal(expected, derivative) - test_result = pd.DataFrame({ - 'Variable Name': 'A', - 'Start Time': '2016-01-01 01:00:00', - 'End Time': '2016-01-01 04:00:00', - 'Timesteps': 4, - 'Error Flag': 'Error Flag'}, index=[1]) - pm.test_results = pd.concat([pm.test_results, test_result]) + # test with irregular timesteps + index = index[[0,1,2,4]] + data = data[[0,1,2,4]] + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - test_result = pd.DataFrame({ - 'Variable Name': 'B', - 'Start Time': '2016-01-01 01:00:00', - 'End Time': '2016-01-01 01:00:00', - 'Timesteps': 1, - 'Error Flag': 'Error Flag'}, index=[2]) - pm.test_results = pd.concat([pm.test_results, test_result]) - mask = pm.mask - QCI = pecos.metrics.qci(mask) + derivative = pecos.metrics.time_derivative(df) - expected_mask = pd.DataFrame(data=[[False, False, True],[False, True, True],[False, True, True],[False, True, True],[False, True, True]], - index=pm.df.index, - columns=pm.df.columns) + expected = np.array([[3.0/3600, 2.0/3600, 0], + [3.0/3600, 2.0/3600, np.nan], + [1.0/3600, 1.75/3600, np.nan], + [-3.0/3600, 1.25/3600, np.nan]]) + expected = pd.DataFrame(data=expected, index=index, columns=['A', 'B', 'C']) - assert_equal((mask == expected_mask).any().any(), True) - assert_equal(QCI.mean(), (15-5)/15.0) + assert_frame_equal(expected, derivative) - tfilter = pd.Series(data = [True, False, True, True, True], index=pm.df.index) - QCI_with_tfilter = pecos.metrics.qci(mask, tfilter = tfilter) - - assert_equal(QCI_with_tfilter.mean(), (12-3)/12.0) - -def test_rmse(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - x1 = pd.DataFrame(data=np.array([4, 4, 4.5, 2.7, 6]), index=index, columns=['Power']) - x2 = pd.DataFrame(data=np.array([5,10,4.5,3,4]), index=index, columns=['Power']) - - RMSE = pecos.metrics.rmse(x1, x2) + def test_qci_no_test_results(self): + periods = 5 + np.random.seed(100) + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data=np.sin(np.random.rand(3,1)*np.arange(0,periods,1)) + df = pd.DataFrame(data=data.transpose(), index=index, columns=['A', 'B', 'C']) + trans = dict(zip(df.columns, [[col] for col in df.columns])) + + pm = pecos.monitoring.PerformanceMonitoring() + pm.add_dataframe(df) + pm.add_translation_dictionary(trans) + + mask = pm.mask + QCI = pecos.metrics.qci(mask) + + self.assertEqual(mask.any().any(), True) + self.assertEqual(QCI['A'], 1) + self.assertEqual(QCI['B'], 1) + self.assertEqual(QCI['C'], 1) + + def test_qci_with_test_results(self): + periods = 5 + np.random.seed(100) + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data=np.sin(np.random.rand(3,1)*np.arange(0,periods,1)) + df = pd.DataFrame(data=data.transpose(), index=index, columns=['A', 'B', 'C']) + trans = dict(zip(df.columns, [[col] for col in df.columns])) + + pm = pecos.monitoring.PerformanceMonitoring() + pm.add_dataframe(df) + pm.add_translation_dictionary(trans) + + test_result = pd.DataFrame({ + 'Variable Name': 'A', + 'Start Time': '2016-01-01 01:00:00', + 'End Time': '2016-01-01 04:00:00', + 'Timesteps': 4, + 'Error Flag': 'Error Flag'}, index=[1]) + pm.test_results = pd.concat([pm.test_results, test_result]) + + test_result = pd.DataFrame({ + 'Variable Name': 'B', + 'Start Time': '2016-01-01 01:00:00', + 'End Time': '2016-01-01 01:00:00', + 'Timesteps': 1, + 'Error Flag': 'Error Flag'}, index=[2]) + pm.test_results = pd.concat([pm.test_results, test_result]) + mask = pm.mask + QCI = pecos.metrics.qci(mask) + + expected_mask = pd.DataFrame(data=[[False, False, True],[False, True, True],[False, True, True],[False, True, True],[False, True, True]], + index=pm.df.index, + columns=pm.df.columns) + + self.assertEqual((mask == expected_mask).any().any(), True) + self.assertEqual(QCI.mean(), (15-5)/15.0) + + tfilter = pd.Series(data = [True, False, True, True, True], index=pm.df.index) + QCI_with_tfilter = pecos.metrics.qci(mask, tfilter = tfilter) + + self.assertEqual(QCI_with_tfilter.mean(), (12-3)/12.0) + + def test_rmse(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + x1 = pd.DataFrame(data=np.array([4, 4, 4.5, 2.7, 6]), index=index, columns=['Power']) + x2 = pd.DataFrame(data=np.array([5,10,4.5,3,4]), index=index, columns=['Power']) + + RMSE = pecos.metrics.rmse(x1, x2) + + self.assertAlmostEqual(RMSE['Power'], 2.8667, 4) - assert_almost_equal(RMSE['Power'], 2.8667, 4) if __name__ == '__main__': - test_derivative() \ No newline at end of file + unittest.main() diff --git a/pecos/tests/test_monitoring.py b/pecos/tests/test_monitoring.py index df8acd6..7ce0c92 100644 --- a/pecos/tests/test_monitoring.py +++ b/pecos/tests/test_monitoring.py @@ -1,12 +1,10 @@ import unittest -from nose.tools import * +from pandas.testing import assert_frame_equal, assert_series_equal from os.path import abspath, dirname, join -import pecos import pandas as pd -from pandas import Timestamp, RangeIndex -from pandas.testing import assert_frame_equal, assert_series_equal import numpy as np -from numpy import array + +import pecos #pd.set_option('expand_frame_repr', False) @@ -14,6 +12,7 @@ datadir = join(testdir,'data') simpleexampledir = join(testdir,'..', '..', 'examples','simple') + def simple_example_run_analysis(df): # Create an PerformanceMonitoring instance @@ -68,6 +67,7 @@ def simple_example_run_analysis(df): return QCI + class Test_simple_example(unittest.TestCase): @classmethod @@ -88,11 +88,11 @@ def setUp(self): time_filter = (clocktime > 3*3600) & (clocktime < 21*3600) self.time_filter = pd.Series(time_filter, index=self.pm.df.index) self.pm.add_time_filter(self.time_filter) - + @classmethod def tearDown(self): pass - + def test_check_timestamp(self): #Missing timestamp at 5:00 #Duplicate timestamp 17:00 @@ -261,7 +261,7 @@ def test_composite_signal(self): [('Wave Error C',pd.Timestamp('2015-01-01 13:00:00'),pd.Timestamp('2015-01-01 14:45:00'),8.0,'Data > upper bound, 0.25')], columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag']) - assert_frame_equal(temp, expected, check_dtype=False) + assert_frame_equal(temp, expected, check_dtype=False, check_index_type=False) def test_full_example(self): data_file = join(simpleexampledir,'simple.csv') @@ -269,7 +269,7 @@ def test_full_example(self): QCI = simple_example_run_analysis(df) - assert_almost_equal(QCI.mean(),0.852113,6) + self.assertAlmostEqual(QCI.mean(),0.852113,6) actual = pd.read_csv('test_results.csv', index_col=0) # Convert back to datetime just so that they are in the same format @@ -314,12 +314,13 @@ def test_full_example_with_timezone(self): QCI = simple_example_run_analysis(df) - assert_almost_equal(QCI.mean(),0.852113,6) + self.assertAlmostEqual(QCI.mean(),0.852113,6) actual = pd.read_csv('test_results.csv', index_col=0) expected = pd.read_csv(join(datadir,'Simple_test_results_with_timezone.csv'), index_col=0) assert_frame_equal(actual, expected, check_dtype=False) + class Test_check_timestamp(unittest.TestCase): @classmethod @@ -341,36 +342,37 @@ def tearDown(self): def test_check_exact_times_true(self): self.pm.check_timestamp(3600, exact_times=True) expected = pd.DataFrame( - array([['', Timestamp('2016-10-17 02:05:00'), - Timestamp('2016-10-17 03:05:00'), 2, + np.array([['', pd.Timestamp('2016-10-17 02:05:00'), + pd.Timestamp('2016-10-17 03:05:00'), 2, 'Missing timestamp']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=1, step=1) + index=pd.RangeIndex(start=0, stop=1, step=1) ) assert_frame_equal(expected, self.pm.test_results) def test_check_exact_times_false(self): self.pm.check_timestamp(3600, exact_times=False) expected = pd.DataFrame( - array([['', Timestamp('2016-10-17 02:00:00'), - Timestamp('2016-10-17 02:00:00'), 1, 'Missing timestamp']], dtype=object), + np.array([['', pd.Timestamp('2016-10-17 02:00:00'), + pd.Timestamp('2016-10-17 02:00:00'), 1, 'Missing timestamp']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=1, step=1) + index=pd.RangeIndex(start=0, stop=1, step=1) ) assert_frame_equal(expected, self.pm.test_results) def test_check_exact_times_true_with_start_time(self): - self.pm.check_timestamp(3600, expected_start_time=Timestamp('2016-10-17 01:00:00'), exact_times=True) + self.pm.check_timestamp(3600, expected_start_time=pd.Timestamp('2016-10-17 01:00:00'), + exact_times=True) expected = pd.DataFrame( - array([['', Timestamp('2016-10-17 01:00:00'), - Timestamp('2016-10-17 03:00:00'), 3, + np.array([['', pd.Timestamp('2016-10-17 01:00:00'), + pd.Timestamp('2016-10-17 03:00:00'), 3, 'Missing timestamp']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=1, step=1) + index=pd.RangeIndex(start=0, stop=1, step=1) ) assert_frame_equal(expected, self.pm.test_results) - + class Test_check_delta(unittest.TestCase): @classmethod @@ -393,10 +395,10 @@ def test_deadsensor(self): # dead sensor = < 1 in 5 hours self.pm.check_delta([1, None], window=5*3600) expected = pd.DataFrame( - array([['A', Timestamp('2017-01-01 01:00:00'), Timestamp('2017-01-01 08:00:00'), 8, 'Delta < lower bound, 1'], - ['A', Timestamp('2017-01-01 16:00:00'), Timestamp('2017-01-01 23:00:00'), 8, 'Delta < lower bound, 1']], dtype=object), + np.array([['A', pd.Timestamp('2017-01-01 01:00:00'), pd.Timestamp('2017-01-01 08:00:00'), 8, 'Delta < lower bound, 1'], + ['A', pd.Timestamp('2017-01-01 16:00:00'), pd.Timestamp('2017-01-01 23:00:00'), 8, 'Delta < lower bound, 1']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=2, step=1) + index=pd.RangeIndex(start=0, stop=2, step=1) ) #pecos.graphics.plot_test_results(self.pm.df, self.pm.test_results, filename_root='test_deadsensor') assert_frame_equal(expected, self.pm.test_results) @@ -404,17 +406,17 @@ def test_deadsensor(self): def test_increment_deadsensor(self): # As expected, check_increment does not produce the same results as check_delta self.pm.check_increment([1, None], 'A', increment=5) - assert_equal(10, self.pm.test_results['Timesteps'].sum()) + self.assertEqual(10, self.pm.test_results['Timesteps'].sum()) def test_abrupt_change(self): # abrupt change = > 7 in 3 hours self.pm.check_delta([None, 7], window=3*3600) expected = pd.DataFrame( - array([['A', Timestamp('2017-01-01 13:00:00'), Timestamp('2017-01-01 16:00:00'), 4, 'Delta > upper bound, 7'], - ['B', Timestamp('2017-01-01 10:00:00'), Timestamp('2017-01-01 12:00:00'), 3, 'Delta > upper bound, 7'], - ['B', Timestamp('2017-01-01 16:00:00'), Timestamp('2017-01-01 19:00:00'), 4, 'Delta > upper bound, 7']], dtype=object), + np.array([['A', pd.Timestamp('2017-01-01 13:00:00'), pd.Timestamp('2017-01-01 16:00:00'), 4, 'Delta > upper bound, 7'], + ['B', pd.Timestamp('2017-01-01 10:00:00'), pd.Timestamp('2017-01-01 12:00:00'), 3, 'Delta > upper bound, 7'], + ['B', pd.Timestamp('2017-01-01 16:00:00'), pd.Timestamp('2017-01-01 19:00:00'), 4, 'Delta > upper bound, 7']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=3, step=1) + index=pd.RangeIndex(start=0, stop=3, step=1) ) assert_frame_equal(expected, self.pm.test_results) @@ -422,10 +424,10 @@ def test_abrupt_positive_change(self): # abrupt positive change = > 7 in 3 hours self.pm.check_delta([None, 7], window=3*3600, direction='positive') expected = pd.DataFrame( - array([['A', Timestamp('2017-01-01 13:00:00'), Timestamp('2017-01-01 16:00:00'), 4, 'Delta (+) > upper bound, 7'], - ['B', Timestamp('2017-01-01 16:00:00'), Timestamp('2017-01-01 19:00:00'), 4, 'Delta (+) > upper bound, 7']], dtype=object), + np.array([['A', pd.Timestamp('2017-01-01 13:00:00'), pd.Timestamp('2017-01-01 16:00:00'), 4, 'Delta (+) > upper bound, 7'], + ['B', pd.Timestamp('2017-01-01 16:00:00'), pd.Timestamp('2017-01-01 19:00:00'), 4, 'Delta (+) > upper bound, 7']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=2, step=1) + index=pd.RangeIndex(start=0, stop=2, step=1) ) assert_frame_equal(expected, self.pm.test_results) @@ -433,9 +435,9 @@ def test_abrupt_negative_change(self): # abrupt negative change = < 7 in 3 hours self.pm.check_delta([None, 7], window=3*3600, direction='negative') expected = pd.DataFrame( - array([['B', Timestamp('2017-01-01 10:00:00'), Timestamp('2017-01-01 12:00:00'), 3, 'Delta (-) > upper bound, 7']], dtype=object), + np.array([['B', pd.Timestamp('2017-01-01 10:00:00'), pd.Timestamp('2017-01-01 12:00:00'), 3, 'Delta (-) > upper bound, 7']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=1, step=1) + index=pd.RangeIndex(start=0, stop=1, step=1) ) assert_frame_equal(expected, self.pm.test_results) @@ -514,7 +516,8 @@ def test_delta_scale(self, output=False): # test to make sure the results don't change expected = pd.read_csv(join(datadir,'delta_summary_100.csv'), index_col=0) assert_series_equal(summary['Number'], expected['Number'], check_dtype=False) - + + class Test_check_outlier(unittest.TestCase): @classmethod @@ -537,10 +540,10 @@ def test_outlier(self): # outlier if stdev > 1.9 self.pm.check_outlier([-1.9, 1.9], window=None, absolute_value=False) expected = pd.DataFrame( - array([['A', Timestamp('2017-01-01 19:00:00'), Timestamp('2017-01-01 19:00:00'), 1, 'Outlier < lower bound, -1.9'], - ['A', Timestamp('2017-01-01 06:00:00'), Timestamp('2017-01-01 06:00:00'), 1, 'Outlier > upper bound, 1.9']], dtype=object), + np.array([['A', pd.Timestamp('2017-01-01 19:00:00'), pd.Timestamp('2017-01-01 19:00:00'), 1, 'Outlier < lower bound, -1.9'], + ['A', pd.Timestamp('2017-01-01 06:00:00'), pd.Timestamp('2017-01-01 06:00:00'), 1, 'Outlier > upper bound, 1.9']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=2, step=1) + index=pd.RangeIndex(start=0, stop=2, step=1) ) assert_frame_equal(expected, self.pm.test_results) @@ -548,19 +551,19 @@ def test_outlier(self): results = pecos.monitoring.check_outlier(self.pm.data, [None, 1.9], window=None, absolute_value=True ) test_results = results['test_results'] expected = pd.DataFrame( - array([['A', Timestamp('2017-01-01 06:00:00'), Timestamp('2017-01-01 06:00:00'), 1, '|Outlier| > upper bound, 1.9'], - ['A', Timestamp('2017-01-01 19:00:00'), Timestamp('2017-01-01 19:00:00'), 1, '|Outlier| > upper bound, 1.9']], dtype=object), + np.array([['A', pd.Timestamp('2017-01-01 06:00:00'), pd.Timestamp('2017-01-01 06:00:00'), 1, '|Outlier| > upper bound, 1.9'], + ['A', pd.Timestamp('2017-01-01 19:00:00'), pd.Timestamp('2017-01-01 19:00:00'), 1, '|Outlier| > upper bound, 1.9']], dtype=object), columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], - index=RangeIndex(start=0, stop=2, step=1) + index=pd.RangeIndex(start=0, stop=2, step=1) ) assert_frame_equal(test_results, expected, check_dtype=False) - def test_outlier_streaming(self): # outlier if stdev > 1.9 pass + class Test_check_custom(unittest.TestCase): @classmethod @@ -588,7 +591,7 @@ def custom_func(data): metadata = self.pm.check_custom_static(custom_func, error_message='Static') N = self.pm.df.shape[0]*self.pm.df.shape[1] percent = 1-self.pm.test_results['Timesteps'].sum()/N - assert_almost_equal(percent, 0.95, 2) # 95% within 2 std + self.assertAlmostEqual(percent, 0.95, 2) # 95% within 2 std # using a specific key metadata_A = self.pm.check_custom_static(custom_func, key='A', error_message='Static') @@ -597,7 +600,7 @@ def custom_func(data): # Functional tests results = pecos.monitoring.check_custom_static(self.pm.data, custom_func, error_message='Static') percent = 1-results['test_results']['Timesteps'].sum()/N - assert_almost_equal(percent, 0.95, 2) # 95% within 2 std + self.assertAlmostEqual(percent, 0.95, 2) # 95% within 2 std def test_custom_streaming(self): @@ -609,7 +612,7 @@ def custom_func(data_pt, history): metadata = self.pm.check_custom_streaming(custom_func, 50, error_message='Streaming') N = self.pm.df.shape[0]*self.pm.df.shape[1] percent = 1-self.pm.test_results['Timesteps'].sum()/N - assert_almost_equal(percent, 0.95, 2) # 95% within 2 std + self.assertAlmostEqual(percent, 0.95, 2) # 95% within 2 std # using a specific key metadata_A = self.pm.check_custom_streaming(custom_func, 50, key='A', error_message='Streaming') @@ -618,14 +621,15 @@ def custom_func(data_pt, history): # Functional tests results = pecos.monitoring.check_custom_streaming(self.pm.data, custom_func, 50, error_message='Streaming') percent = 1-results['test_results']['Timesteps'].sum()/N - assert_almost_equal(percent, 0.95, 2) # 95% within 2 std - + self.assertAlmostEqual(percent, 0.95, 2) # 95% within 2 std + + class Test_append_test_results(unittest.TestCase): @classmethod def setUp(self): self.pm = pecos.monitoring.PerformanceMonitoring() - + @classmethod def tearDown(self): pass @@ -641,7 +645,7 @@ def test_append_test_results(self): self.pm._append_test_results(mask, 'None') expected = pd.DataFrame( - array([['A', 0, 3, 4, 'None'], + np.array([['A', 0, 3, 4, 'None'], ['A', 5, 5, 1, 'None'], ['B', 7, 9, 3, 'None'], ['C', 0, 5, 6, 'None'], @@ -649,7 +653,7 @@ def test_append_test_results(self): columns=['Variable Name', 'Start Time', 'End Time', 'Timesteps', 'Error Flag'], index=range(5)) assert_frame_equal(expected, self.pm.test_results) - + if __name__ == '__main__': unittest.main() diff --git a/pecos/tests/test_pv.py b/pecos/tests/test_pv.py index a33df4f..1ee9ed1 100644 --- a/pecos/tests/test_pv.py +++ b/pecos/tests/test_pv.py @@ -1,103 +1,108 @@ -from nose.tools import * +import unittest from pandas.testing import assert_frame_equal, assert_series_equal from os.path import abspath, dirname, join import pandas as pd import numpy as np + import pecos testdir = dirname(abspath(__file__)) -datadir = join(testdir,'data') +datadir = join(testdir, 'data') + + +class TestPV(unittest.TestCase): + + def test_insolation(self): + # same test as metrics.time_integral + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + + integral = pecos.pv.insolation(df) + + self.assertEqual(integral['A'], 100800) + self.assertEqual(integral['B'], 115200) + self.assertEqual(integral['C'], 129600) + + def test_energy(self): + # same test as metrics.time_integral + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) + df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) + + integral = pecos.pv.energy(df) + + self.assertEqual(integral['A'], 100800) + self.assertEqual(integral['B'], 115200) + self.assertEqual(integral['C'], 129600) + + def test_performance_ratio(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + E = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) + + PR = pecos.pv.performance_ratio(E, POA, 4, 1000) + expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) + assert_series_equal(PR, expected, check_dtype=False) -def test_insolation(): - # same test as metrics.time_integral - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - integral = pecos.pv.insolation(df) - - assert_equal(integral['A'], 100800) - assert_equal(integral['B'], 115200) - assert_equal(integral['C'], 129600) + def test_normalized_current(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + I = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) + + NI = pecos.pv.normalized_current(I, POA, 4, 1000) + expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) + assert_series_equal(NI, expected, check_dtype=False) -def test_energy(): - # same test as metrics.time_integral - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15]]) - df = pd.DataFrame(data=data, index=index, columns=['A', 'B', 'C']) - - integral = pecos.pv.energy(df) - - assert_equal(integral['A'], 100800) - assert_equal(integral['B'], 115200) - assert_equal(integral['C'], 129600) - -def test_performance_ratio(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - E = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) - - PR = pecos.pv.performance_ratio(E, POA, 4, 1000) - expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) - assert_series_equal(PR, expected, check_dtype=False) + def test_normalized_efficiency(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + P = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) + + NE = pecos.pv.normalized_efficiency(P, POA, 4, 1000) + expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) + assert_series_equal(NE, expected, check_dtype=False) -def test_normalized_current(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - I = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) - - NI = pecos.pv.normalized_current(I, POA, 4, 1000) - expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) - assert_series_equal(NI, expected, check_dtype=False) - -def test_normalized_efficiency(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - P = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - POA = pd.Series(data=np.array([1000,1250,1250,900,1500]), index=index) - - NE = pecos.pv.normalized_efficiency(P, POA, 4, 1000) - expected = pd.Series(data=np.array([1.0, 0.8, 0.9, 0.75, 1.0]), index=index) - assert_series_equal(NE, expected, check_dtype=False) + def test_performance_index(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + P = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + P_expected = pd.Series(data=np.array([5,10,4.5,3,4]), index=index) + + PI = pecos.pv.performance_index(P, P_expected) + expected = pd.Series(data=np.array([0.8,0.4,1,0.9,1.5]), index=index) + assert_series_equal(PI, expected, check_dtype=False) -def test_performance_index(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - P = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - P_expected = pd.Series(data=np.array([5,10,4.5,3,4]), index=index) - - PI = pecos.pv.performance_index(P, P_expected) - expected = pd.Series(data=np.array([0.8,0.4,1,0.9,1.5]), index=index) - assert_series_equal(PI, expected, check_dtype=False) + def test_energy_yield(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + E = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + + EY = pecos.pv.energy_yield(E, 10) + expected = pd.Series(data=np.array([0.4,0.4,0.45,0.27,0.6]), index=index) + assert_series_equal(EY, expected, check_dtype=False) -def test_energy_yield(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - E = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - - EY = pecos.pv.energy_yield(E, 10) - expected = pd.Series(data=np.array([0.4,0.4,0.45,0.27,0.6]), index=index) - assert_series_equal(EY, expected, check_dtype=False) + def test_clearness_index(self): + + periods = 5 + index = pd.date_range('1/1/2016', periods=periods, freq='H') + DNI = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) + ExI = pd.Series(data=np.array([5,10,4.5,3,4]), index=index) + + K = pecos.pv.clearness_index(DNI, ExI) + expected = pd.Series(data=np.array([0.8,0.4,1,0.9,1.5]), index=index) + assert_series_equal(K, expected, check_dtype=False) -def test_clearness_index(): - - periods = 5 - index = pd.date_range('1/1/2016', periods=periods, freq='H') - DNI = pd.Series(data=np.array([4, 4, 4.5, 2.7, 6]), index=index) - ExI = pd.Series(data=np.array([5,10,4.5,3,4]), index=index) - - K = pecos.pv.clearness_index(DNI, ExI) - expected = pd.Series(data=np.array([0.8,0.4,1,0.9,1.5]), index=index) - assert_series_equal(K, expected, check_dtype=False) if __name__ == '__main__': - test_performance_ratio() \ No newline at end of file + unittest.main() diff --git a/pecos/tests/test_utils.py b/pecos/tests/test_utils.py index 68ef01a..61feb23 100644 --- a/pecos/tests/test_utils.py +++ b/pecos/tests/test_utils.py @@ -1,12 +1,11 @@ import unittest -import sys -from nose import SkipTest -from nose.tools import * from pandas.testing import assert_frame_equal, assert_index_equal import pandas as pd import numpy as np + import pecos + class TestConvertIndex(unittest.TestCase): @classmethod @@ -33,44 +32,44 @@ def setUp(self): self.df_float = pd.DataFrame(data, index=index_float) self.df_clock = pd.DataFrame(data, index=index_clock) self.df_epoch = pd.DataFrame(data, index=index_epoch) - + @classmethod def tearDown(self): pass - + def test_index_to_datetime(self): - index = pecos.utils.index_to_datetime(self.df_float.index, unit='s', origin='1/1/2016') + index = pecos.utils.index_to_datetime(self.df_float.index, + unit='s', origin='1/1/2016') assert_index_equal(index, self.df_dt.index) - + def test_datetime_to_elapsedtime(self): index = pecos.utils.datetime_to_elapsedtime(self.df_dt.index, 14.0) assert_index_equal(index, self.df_float.index) - + # with timezone index_tz = self.df_dt.index.tz_localize('MST') index = pecos.utils.datetime_to_elapsedtime(index_tz, 14.0) assert_index_equal(index, self.df_float.index) - + def test_datetime_to_epochtime(self): - if sys.version_info.major < 3: - raise SkipTest # skip if python version < 3 index = pecos.utils.datetime_to_epochtime(self.df_dt.index) assert_index_equal(index, self.df_epoch.index) - + def test_datetime_to_clocktime(self): index = pecos.utils.datetime_to_clocktime(self.df_dt.index) assert_index_equal(index, self.df_clock.index) - + # with timezone index_tz = self.df_dt.index.tz_localize('MST') index = pecos.utils.datetime_to_clocktime(index_tz) assert_index_equal(index, self.df_clock.index) - + + class TestRoundIndex(unittest.TestCase): @classmethod def setUp(self): - index = pd.DatetimeIndex(['1/1/2016 00:00:14', + index = pd.DatetimeIndex(['1/1/2016 00:00:14', '1/1/2016 00:00:30', '1/1/2016 00:00:40', '1/1/2016 00:00:44', @@ -86,11 +85,11 @@ def setUp(self): @classmethod def tearDown(self): pass - + def test_round_index_nearest(self): index = pecos.utils.round_index(self.df.index, 15, 'nearest') nearest_index = pd.DatetimeIndex([ - '1/1/2016 00:00:15', + '1/1/2016 00:00:15', '1/1/2016 00:00:30', '1/1/2016 00:00:45', '1/1/2016 00:00:45', @@ -101,12 +100,12 @@ def test_round_index_nearest(self): '1/1/2016 00:01:45', '1/1/2016 00:02:00']) diff = index.difference(nearest_index) - assert_equals(len(diff), 0) - + self.assertEqual(len(diff), 0) + def test_round_index_floor(self): index = pecos.utils.round_index(self.df.index, 15, 'floor') floor_index = pd.DatetimeIndex([ - '1/1/2016 00:00:00', + '1/1/2016 00:00:00', '1/1/2016 00:00:30', '1/1/2016 00:00:30', '1/1/2016 00:00:30', @@ -117,12 +116,12 @@ def test_round_index_floor(self): '1/1/2016 00:01:45', '1/1/2016 00:02:00']) diff = index.difference(floor_index) - assert_equals(len(diff), 0) - + self.assertEqual(len(diff), 0) + def test_round_index_ceiling(self): index = pecos.utils.round_index(self.df.index, 15, 'ceiling') ceiling_index = pd.DatetimeIndex([ - '1/1/2016 00:00:15', + '1/1/2016 00:00:15', '1/1/2016 00:00:30', '1/1/2016 00:00:45', '1/1/2016 00:00:45', @@ -133,12 +132,12 @@ def test_round_index_ceiling(self): '1/1/2016 00:01:45', '1/1/2016 00:02:15']) diff = index.difference(ceiling_index) - assert_equals(len(diff), 0) - + self.assertEqual(len(diff), 0) + def test_round_index_invalid(self): index = pecos.utils.round_index(self.df.index, 15, 'invalid') diff = index.difference(self.df.index) - assert_equals(len(diff), 0) + self.assertEqual(len(diff), 0) class TestEvaluateString(unittest.TestCase): @@ -146,42 +145,44 @@ class TestEvaluateString(unittest.TestCase): @classmethod def setUp(self): index = pd.date_range('1/1/2020', periods=72, freq='H') - data = {'A': np.random.rand(72), + data = {'A': np.random.rand(72), 'B': np.random.rand(72)} self.df = pd.DataFrame(data, index=index) @classmethod def tearDown(self): pass - + def test_evaluate_string_index(self): string_to_eval = "({CLOCK_TIME} > 3*3600) & ({CLOCK_TIME} < 21*3600)" x = pecos.utils.evaluate_string(string_to_eval, self.df) - assert_equal(x.sum()[0], 72-4*3-3*3) - + self.assertEqual(x.sum()[0], 72-4*3-3*3) + def test_evaluate_string_specs(self): string_to_eval = "{A}*5" x = pecos.utils.evaluate_string(string_to_eval, specs={'A': 10}) - assert_equal(x, 50) - + self.assertEqual(x, 50) + def test_evaluate_string_data(self): string_to_eval = "{A}*5" x = pecos.utils.evaluate_string(string_to_eval, self.df, col_name='A') assert_frame_equal(x, self.df[['A']]*5) - + def test_evaluate_string_multiple_keywords(self): string_to_eval = "{A}*5 + {C}" - x = pecos.utils.evaluate_string(string_to_eval, self.df, specs={'C': 10}, col_name='A') + x = pecos.utils.evaluate_string(string_to_eval, self.df, + specs={'C': 10}, col_name='A') assert_frame_equal(x, self.df[['A']]*5+10) - + def test_evaluate_string_value(self): x = pecos.utils.evaluate_string(5) - assert_equal(x, 5) - + self.assertEqual(x, 5) + + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/requirements.txt b/requirements.txt index b3ecadb..ebf4d74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,15 @@ jinja2 matplotlib # Optional -nose pvlib plotly ephem sqlalchemy + +# Documentation +sphinx +sphinx_rtd_theme + +# Testing +pytest +coverage \ No newline at end of file