Skip to content

Commit

Permalink
Day night masking sphinx documentation (#139)
Browse files Browse the repository at this point in the history
* update the day-night masking example.

* update the day-night masking routine.

* added the SERF east data for running the day-night mask examples.

* added the day-night masking routine.

* Added section for comparing day-night mask to PVlib sunrise-sunset times.

* added separate printouts for sunrise and sunset time comparisons.

* added vertical lines for sunrise + sunset in plots

* update the routine to remove hardcoded file name.

* added update to the whatsnew file.

* removed a newline to see if we could get git actions to work.

* Made updates to documentation based on @kanderso-nrel's recommendations.

* Update docs/examples/day-night-masking.py

Co-authored-by: Cliff Hansen <[email protected]>

* Removed default kwargs for pvlib SPA sunrise-sunset function.

* Updating the commenting.

* fixed pep8 line length

Co-authored-by: Perry <[email protected]>
Co-authored-by: Cliff Hansen <[email protected]>
  • Loading branch information
3 people authored Apr 18, 2022
1 parent 3de6585 commit 9579731
Show file tree
Hide file tree
Showing 3 changed files with 2,738 additions and 1 deletion.
125 changes: 125 additions & 0 deletions docs/examples/day-night-masking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
Day-Night Masking
=================
Masking day-night periods using the PVAnalytics daytime module.
"""

# %%
# Identifying and masking day-night periods in an AC power time series or
# irradiance time series can aid in future data analysis, such as detecting
# if a time series has daylight savings time or time shifts. Here, we use
# :py:func:`pvanalytics.features.daytime.power_or_irradiance` to mask day/night
# periods, as well as to estimate sunrise and sunset times in the data set.
# This function is particularly useful for cases where the time zone of a data
# stream is unknown or incorrect, as its outputs can be used to determine time
# zone.

import pvanalytics
from pvanalytics.features.daytime import power_or_irradiance
import matplotlib.pyplot as plt
import pandas as pd
import pathlib
import pvlib
import numpy as np

# %%
# First, read in the 1-minute sampled AC power time series data, taken
# from the SERF East installation on the NREL campus.
# This sample is provided from the NREL PVDAQ database, and contains
# a column representing an AC power data stream.

pvanalytics_dir = pathlib.Path(pvanalytics.__file__).parent
ac_power_file = pvanalytics_dir / 'data' / 'serf_east_1min_ac_power.csv'
data = pd.read_csv(ac_power_file, index_col=0, parse_dates=True)
data = data.sort_index()
# This is the known frequency of the time series. You may need to infer
# the frequency or set the frequency with your AC power time series.
freq = "1T"
# These are the latitude-longitude coordinates associated with the
# SERF East system.
latitude = 39.742
longitude = -105.173
# Plot the time series.
data['ac_power__752'].plot()
plt.xlabel("Date")
plt.ylabel("AC Power (kW)")
plt.tight_layout()
plt.show()

# %%
# It is critical to set all negative values in the AC power time series to 0
# for :py:func:`pvanalytics.features.daytime.power_or_irradiance` to work
# properly. Negative erroneous data may affect daytime mask assignments.
data.loc[data['ac_power__752'] < 0, 'ac_power__752'] = 0

# %%
# Now, use :py:func:`pvanalytics.features.daytime.power_or_irradiance`
# to mask day periods in the time series.
predicted_day_night_mask = power_or_irradiance(series=data['ac_power__752'],
freq=freq)

# %%
# Function :py:func:`pvlib.solarposition.sun_rise_set_transit_spa` is
# used to get ground-truth sunrise and sunset times for each day at the site
# location, and a SPA-daytime mask is calculated based on these times. Data
# associated with SPA daytime periods is labeled as True, and data associated
# with SPA nighttime periods is labeled as False.
# SPA sunrise and sunset times are used here as a point of comparison to the
# :py:func:`pvanalytics.features.daytime.power_or_irradiance` outputs.
# SPA-based sunrise and sunset values are not
# needed to run :py:func:`pvanalytics.features.daytime.power_or_irradiance`.

sunrise_sunset_df = pvlib.solarposition.sun_rise_set_transit_spa(data.index,
latitude,
longitude)
data['sunrise_time'] = sunrise_sunset_df['sunrise']
data['sunset_time'] = sunrise_sunset_df['sunset']

data['daytime_mask'] = True
data.loc[(data.index < data.sunrise_time) |
(data.index > data.sunset_time), "daytime_mask"] = False


# %%
# Plot the AC power data stream with the mask output from
# :py:func:`pvanalytics.features.daytime.power_or_irradiance`,
# as well as the SPA-calculated sunrise and sunset

data['ac_power__752'].plot()
data.loc[predicted_day_night_mask, 'ac_power__752'].plot(ls='', marker='o')
data.loc[~predicted_day_night_mask, 'ac_power__752'].plot(ls='', marker='o')
sunrise_sunset_times = sunrise_sunset_df[['sunrise',
'sunset']].drop_duplicates()
for sunrise, sunset in sunrise_sunset_times.itertuples(index=False):
plt.axvline(x=sunrise, c="blue")
plt.axvline(x=sunset, c="red")
plt.legend(labels=["AC Power", "Daytime", "Nighttime",
"SPA Sunrise", "SPA Sunset"])
plt.xlabel("Date")
plt.ylabel("AC Power (kW)")
plt.tight_layout()
plt.show()

# %%
# Compare the predicted mask to the ground-truth SPA mask, to get the model
# accuracy. Also, compare sunrise and sunset times for the predicted mask
# compared to the ground truth sunrise and sunset times.
acc = 100 * np.sum(np.equal(data.daytime_mask,
predicted_day_night_mask))/len(data.daytime_mask)
print("Overall model prediction accuracy: " + str(round(acc, 2)) + "%")

# Generate predicted + SPA sunrise times for each day
print("Sunrise Comparison:")
print(pd.DataFrame({'predicted_sunrise': predicted_day_night_mask
.index[predicted_day_night_mask]
.to_series().resample("d").first(),
'pvlib_spa_sunrise': sunrise_sunset_df["sunrise"]
.resample("d").first()}))
# Generate predicted + SPA sunset times for each day
print("Sunset Comparison:")
print(pd.DataFrame({'predicted_sunset': predicted_day_night_mask
.index[predicted_day_night_mask]
.to_series().resample("d").last(),
'pvlib_spa_sunset': sunrise_sunset_df["sunrise"]
.resample("d").last()}))
6 changes: 5 additions & 1 deletion docs/whatsnew/0.1.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ Documentation
* Added examples for the quality.outliers module, including
:py:func:`pvanalytics.quality.outliers.tukey`,
:py:func:`pvanalytics.quality.outliers.zscore`,
:py:func:`pvanalytics.quality.outliers.hampel`,
:py:func:`pvanalytics.quality.outliers.hampel`
(:issue:`133`, :pull:`138`)
* Added examples for the pvanalytics.features.daytime module,
including :py:func:`pvanalytics.features.daytime.power_or_irradiance`
(:issue:`133`, :pull:`139`)


Contributors
~~~~~~~~~~~~
Expand Down
Loading

0 comments on commit 9579731

Please sign in to comment.