Repository: pvlib/pvanalytics

Issue:
Handle missing data gracefully in clipping.levels()
`features.clipping.levels()` raises some very difficult to decipher value errors form numpy when there are NAs in the data. We should either raise a more meaningful and useful exception or simply deal with NAs by dropping them, applying the clipping filter, reindexing, and filling missing values with `False`.

Discussion:
wfvining: Coveralls is reporting a coverage change; however, no change is shown in the coverage reports. I'm going to chalk that up to a mistake on the coveralls/CI side.

Patch:
diff --git a/pvanalytics/features/clipping.py b/pvanalytics/features/clipping.py
index 45f4bd7..91af4ea 100644
--- a/pvanalytics/features/clipping.py
+++ b/pvanalytics/features/clipping.py
@@ -79,16 +79,18 @@ def levels(ac_power, window=4, fraction_in_window=0.75,
     for more information.
 
     """
+    power = ac_power.copy()
+    power.dropna(inplace=True)
     num_bins = np.ceil(1.0 / rtol).astype(int)
-    flags = pd.Series(index=ac_power.index, data=False)
-    power_plateaus, bins = _detect_levels(ac_power, count=levels,
+    flags = pd.Series(index=power.index, data=False)
+    power_plateaus, bins = _detect_levels(power, count=levels,
                                           num_bins=num_bins)
     for lower, upper in power_plateaus:
-        temp = pd.Series(index=ac_power.index, data=0.0)
-        temp.loc[(ac_power >= lower) & (ac_power <= upper)] = 1.0
+        temp = pd.Series(index=power.index, data=0.0)
+        temp.loc[(power >= lower) & (power <= upper)] = 1.0
         flags = flags | _label_clipping(temp, window=window,
                                         frac=fraction_in_window)
-    return flags
+    return flags.reindex_like(ac_power).fillna(False)
 
 
 def _daytime_powercurve(ac_power, power_quantile):


Test patch:
diff --git a/pvanalytics/tests/features/test_clipping.py b/pvanalytics/tests/features/test_clipping.py
index 347ff40..d2da7cc 100644
--- a/pvanalytics/tests/features/test_clipping.py
+++ b/pvanalytics/tests/features/test_clipping.py
@@ -71,6 +71,16 @@ def test_levels_two_periods(quadratic, quadratic_clipped):
     assert not clipped[50:].any()
 
 
+def test_levels_missing_data(quadratic, quadratic_clipped):
+    quadratic[10:20] = np.nan
+    quadratic_clipped[10:20] = np.nan
+    assert_series_equal(
+        pd.Series(False, quadratic.index),
+        clipping.levels(quadratic, window=10)
+    )
+    assert not clipping.levels(quadratic_clipped, window=10)[10:20].any()
+
+
 def test_threshold_no_clipping(quadratic):
     """In a data set with a single quadratic there is no clipping."""
     quadratic.index = pd.date_range(