rdtools 2.2.0b0__tar.gz → 2.2.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {rdtools-2.2.0b0/rdtools.egg-info → rdtools-2.2.0b2}/PKG-INFO +1 -1
  2. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/_version.py +3 -3
  3. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/analysis_chains.py +99 -9
  4. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/clearsky_temperature.py +3 -1
  5. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/degradation.py +22 -23
  6. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/filtering.py +4 -4
  7. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/normalization.py +2 -3
  8. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/plotting.py +2 -2
  9. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/soiling.py +7 -6
  10. {rdtools-2.2.0b0 → rdtools-2.2.0b2/rdtools.egg-info}/PKG-INFO +1 -1
  11. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools.egg-info/requires.txt +6 -6
  12. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/setup.py +8 -6
  13. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/LICENSE +0 -0
  14. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/MANIFEST.in +0 -0
  15. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/README.md +0 -0
  16. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/__init__.py +0 -0
  17. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/_deprecation.py +0 -0
  18. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/aggregation.py +0 -0
  19. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/availability.py +0 -0
  20. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/bootstrap.py +0 -0
  21. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/data/temperature.hdf5 +0 -0
  22. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools/models/xgboost_clipping_model.json +0 -0
  23. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools.egg-info/SOURCES.txt +0 -0
  24. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools.egg-info/dependency_links.txt +0 -0
  25. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools.egg-info/not-zip-safe +0 -0
  26. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/rdtools.egg-info/top_level.txt +0 -0
  27. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/setup.cfg +0 -0
  28. {rdtools-2.2.0b0 → rdtools-2.2.0b2}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rdtools
3
- Version: 2.2.0b0
3
+ Version: 2.2.0b2
4
4
  Summary: Functions for reproducible timeseries analysis of photovoltaic systems.
5
5
  Home-page: https://github.com/NREL/rdtools
6
6
  Author: Rdtools Python Developers
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2022-09-14T07:12:44-0600",
11
+ "date": "2023-12-01T15:24:38-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "0de0d1445621842c6ecc28b9f5f83c3fe84b5da9",
15
- "version": "2.2.0-beta.0"
14
+ "full-revisionid": "250e412bda8199491d8dc45673752374913b9c65",
15
+ "version": "2.2.0-beta.2"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -75,6 +75,14 @@ class TrendAnalysis():
75
75
  filter_params defaults to empty dicts for each function in rdtools.filtering,
76
76
  in which case those functions use default parameter values, `ad_hoc_filter`
77
77
  defaults to None. See examples for more information.
78
+ filter_params_aggregated: dict
79
+ parameters to be passed to rdtools.filtering functions that specifically handle
80
+ aggregated data (dily filters, etc). Keys are the names of the rdtools.filtering functions.
81
+ Values are dicts of parameters to be passed to those functions. Also has a special key
82
+ `ad_hoc_filter`; this filter is a boolean mask joined with the rest of the filters.
83
+ filter_params_aggregated defaults to empty dicts for each function in rdtools.filtering,
84
+ in which case those functions use default parameter values, `ad_hoc_filter`
85
+ defaults to None. See examples for more information.
78
86
  results : dict
79
87
  Nested dict used to store the results of methods ending with `_analysis`
80
88
  '''
@@ -133,6 +141,9 @@ class TrendAnalysis():
133
141
  'csi_filter': {},
134
142
  'ad_hoc_filter': None # use this to include an explict filter
135
143
  }
144
+ self.filter_params_aggregated = {
145
+ 'ad_hoc_filter': None
146
+ }
136
147
  # remove tcell_filter from list if power_expected is passed in
137
148
  if power_expected is not None and temperature_cell is None:
138
149
  del self.filter_params['tcell_filter']
@@ -252,7 +263,8 @@ class TrendAnalysis():
252
263
  clearsky_poa = clearsky_poa['poa_global']
253
264
 
254
265
  if aggregate:
255
- interval_id = pd.Series(range(len(self.poa_global)), index=self.poa_global.index)
266
+ interval_id = pd.Series(
267
+ range(len(self.poa_global)), index=self.poa_global.index)
256
268
  interval_id = interval_id.reindex(times, method='backfill')
257
269
  clearsky_poa = clearsky_poa.groupby(interval_id).mean()
258
270
  clearsky_poa.index = self.poa_global.index
@@ -383,7 +395,8 @@ class TrendAnalysis():
383
395
  self.filter_params, which is a dict, the keys of which are names of
384
396
  functions in rdtools.filtering, and the values of which are dicts
385
397
  containing the associated parameters with which to run the filtering
386
- functions. See examples for details on how to modify filter parameters.
398
+ functions. This private method is specifically for the original indexed
399
+ data. See examples for details on how to modify filter parameters.
387
400
 
388
401
  Parameters
389
402
  ----------
@@ -405,7 +418,8 @@ class TrendAnalysis():
405
418
  # at once. However, we add a default value of True, with the same index as
406
419
  # energy_normalized, so that the output is still correct even when all
407
420
  # filters have been disabled.
408
- filter_components = {'default': pd.Series(True, index=energy_normalized.index)}
421
+ filter_components = {'default': pd.Series(
422
+ True, index=energy_normalized.index)}
409
423
 
410
424
  if case == 'sensor':
411
425
  poa = self.poa_global
@@ -455,14 +469,16 @@ class TrendAnalysis():
455
469
  ad_hoc_filter = self.filter_params['ad_hoc_filter']
456
470
 
457
471
  if ad_hoc_filter.isnull().any():
458
- warnings.warn('ad_hoc_filter contains NaN values; setting to False (excluding)')
472
+ warnings.warn(
473
+ 'ad_hoc_filter contains NaN values; setting to False (excluding)')
459
474
  ad_hoc_filter = ad_hoc_filter.fillna(False)
460
475
 
461
476
  if not filter_components.index.equals(ad_hoc_filter.index):
462
477
  warnings.warn('ad_hoc_filter index does not match index of other filters; missing '
463
478
  'values will be set to True (kept). Align the index with the index '
464
479
  'of the filter_components attribute to prevent this warning')
465
- ad_hoc_filter = ad_hoc_filter.reindex(filter_components.index).fillna(True)
480
+ ad_hoc_filter = ad_hoc_filter.reindex(
481
+ filter_components.index).fillna(True)
466
482
 
467
483
  filter_components['ad_hoc_filter'] = ad_hoc_filter
468
484
 
@@ -475,6 +491,63 @@ class TrendAnalysis():
475
491
  self.clearsky_filter = bool_filter
476
492
  self.clearsky_filter_components = filter_components
477
493
 
494
+ def _aggregated_filter(self, aggregated, case):
495
+ """
496
+ Mirrors the _filter private function, but with aggregated filters applied.
497
+ These aggregated filters are based on those in rdtools.filtering. Uses
498
+ self.filter_params_aggregated, which is a dict, the keys of which are names of
499
+ functions in rdtools.filtering, and the values of which are dicts
500
+ containing the associated parameters with which to run the filtering
501
+ functions. See examples for details on how to modify filter parameters.
502
+
503
+ Parameters
504
+ ----------
505
+ aggregated : pandas.Series
506
+ Time series of aggregated normalized AC energy
507
+ case : str
508
+ 'sensor' or 'clearsky' which filtering protocol to apply. Affects
509
+ whether result is stored in self.sensor_filter_aggregated or
510
+ self.clearsky_filter_aggregated)
511
+
512
+ Returns
513
+ -------
514
+ None
515
+ """
516
+ filter_components_aggregated = {'default':
517
+ pd.Series(True, index=aggregated.index)}
518
+ # Add daily aggregate filters as they come online here.
519
+ # Convert the dictionary into a dataframe (after running filters)
520
+ filter_components_aggregated = pd.DataFrame(
521
+ filter_components_aggregated).fillna(False)
522
+ # Run the ad-hoc filter from filter_params_aggregated, if available
523
+ if self.filter_params_aggregated.get('ad_hoc_filter', None) is not None:
524
+ ad_hoc_filter_aggregated = self.filter_params_aggregated['ad_hoc_filter']
525
+
526
+ if ad_hoc_filter_aggregated.isnull().any():
527
+ warnings.warn(
528
+ 'aggregated ad_hoc_filter contains NaN values; setting to False (excluding)')
529
+ ad_hoc_filter_aggregated = ad_hoc_filter_aggregated.fillna(False)
530
+
531
+ if not filter_components_aggregated.index.equals(ad_hoc_filter_aggregated.index):
532
+ warnings.warn('Aggregated ad_hoc_filter index does not match index of other '
533
+ 'filters; missing values will be set to True (kept). '
534
+ 'Align the index with the index of the '
535
+ 'filter_components_aggregated attribute to prevent this warning')
536
+ ad_hoc_filter_aggregated = ad_hoc_filter_aggregated.reindex(
537
+ filter_components_aggregated.index).fillna(True)
538
+
539
+ filter_components_aggregated['ad_hoc_filter'] = ad_hoc_filter_aggregated
540
+
541
+ bool_filter_aggregated = filter_components_aggregated.all(axis=1)
542
+ filter_components_aggregated = filter_components_aggregated.drop(
543
+ columns=['default'])
544
+ if case == 'sensor':
545
+ self.sensor_filter_aggregated = bool_filter_aggregated
546
+ self.sensor_filter_components_aggregated = filter_components_aggregated
547
+ elif case == 'clearsky':
548
+ self.clearsky_filter_aggregated = bool_filter_aggregated
549
+ self.clearsky_filter_components_aggregated = filter_components_aggregated
550
+
478
551
  def _filter_check(self, post_filter):
479
552
  '''
480
553
  post-filter check for requisite 730 days of data
@@ -621,8 +694,16 @@ class TrendAnalysis():
621
694
  self._filter(energy_normalized, 'sensor')
622
695
  aggregated, aggregated_insolation = self._aggregate(
623
696
  energy_normalized[self.sensor_filter], insolation[self.sensor_filter])
624
- self.sensor_aggregated_performance = aggregated
625
- self.sensor_aggregated_insolation = aggregated_insolation
697
+ # Run daily filters on aggregated data
698
+ self._aggregated_filter(aggregated, 'sensor')
699
+ # Apply filter to aggregated data and store
700
+ self.sensor_aggregated_performance = aggregated[self.sensor_filter_aggregated]
701
+ self.sensor_aggregated_insolation = aggregated_insolation[self.sensor_filter_aggregated]
702
+ # Reindex the data after the fact, so it's on the aggregated interval
703
+ self.sensor_aggregated_performance = self.sensor_aggregated_performance.asfreq(
704
+ self.aggregation_freq)
705
+ self.sensor_aggregated_insolation = self.sensor_aggregated_insolation.asfreq(
706
+ self.aggregation_freq)
626
707
 
627
708
  def _clearsky_preprocess(self):
628
709
  '''
@@ -651,8 +732,17 @@ class TrendAnalysis():
651
732
  self._filter(cs_normalized, 'clearsky')
652
733
  cs_aggregated, cs_aggregated_insolation = self._aggregate(
653
734
  cs_normalized[self.clearsky_filter], cs_insolation[self.clearsky_filter])
654
- self.clearsky_aggregated_performance = cs_aggregated
655
- self.clearsky_aggregated_insolation = cs_aggregated_insolation
735
+ # Run daily filters on aggregated data
736
+ self._aggregated_filter(cs_aggregated, 'clearsky')
737
+ # Apply daily filter to aggregated data and store
738
+ self.clearsky_aggregated_performance = cs_aggregated[self.clearsky_filter_aggregated]
739
+ self.clearsky_aggregated_insolation = \
740
+ cs_aggregated_insolation[self.clearsky_filter_aggregated]
741
+ # Reindex the data after the fact, so it's on the aggregated interval
742
+ self.clearsky_aggregated_performance = self.clearsky_aggregated_performance.asfreq(
743
+ self.aggregation_freq)
744
+ self.clearsky_aggregated_insolation = self.clearsky_aggregated_insolation.asfreq(
745
+ self.aggregation_freq)
656
746
 
657
747
  def sensor_analysis(self, analyses=['yoy_degradation'], yoy_kwargs={}, srr_kwargs={}):
658
748
  '''
@@ -57,7 +57,9 @@ def get_clearsky_tamb(times, latitude, longitude, window_size=40,
57
57
  freq_actual = times.freq
58
58
 
59
59
  dt_daily = pd.date_range(times.date[0] - buffer, times.date[-1] + buffer,
60
- freq='D', tz=times.tz)
60
+ freq='D')
61
+ dt_daily = dt_daily.tz_localize(times.tz, ambiguous='infer',
62
+ nonexistent='shift_forward')
61
63
 
62
64
  f = h5py.File(filepath, "r")
63
65
 
@@ -38,7 +38,7 @@ def degradation_ols(energy_normalized, confidence_level=68.2):
38
38
 
39
39
  # calculate a years column as x value for regression, ignoring leap years
40
40
  day_diffs = (df.index - df.index[0])
41
- df['days'] = day_diffs.astype('timedelta64[s]') / (60 * 60 * 24)
41
+ df['days'] = day_diffs / pd.Timedelta('1d')
42
42
  df['years'] = df.days / 365.0
43
43
 
44
44
  # add intercept-constant to the exogeneous variable
@@ -123,22 +123,14 @@ def degradation_classical_decomposition(energy_normalized,
123
123
 
124
124
  # calculate a years column as x value for regression, ignoring leap years
125
125
  day_diffs = (df.index - df.index[0])
126
- df['days'] = day_diffs.astype('timedelta64[s]') / (60 * 60 * 24)
126
+ df['days'] = day_diffs / pd.Timedelta('1d')
127
127
  df['years'] = df.days / 365.0
128
128
 
129
129
  # Compute yearly rolling mean to isolate trend component using
130
130
  # moving average
131
- it = df.iterrows()
132
- energy_ma = []
133
- for i, row in it:
134
- if row.years - 0.5 >= min(df.years) and \
135
- row.years + 0.5 <= max(df.years):
136
- roll = df[(df.years <= row.years + 0.5) &
137
- (df.years >= row.years - 0.5)]
138
- energy_ma.append(roll.energy_normalized.mean())
139
- else:
140
- energy_ma.append(np.nan)
141
-
131
+ energy_ma = df['energy_normalized'].rolling('365d', center=True).mean()
132
+ has_full_year = (df['years'] >= df['years'][0] + 0.5) & (df['years'] <= df['years'][-1] - 0.5)
133
+ energy_ma[~has_full_year] = np.nan
142
134
  df['energy_ma'] = energy_ma
143
135
 
144
136
  # add intercept-constant to the exogeneous variable
@@ -244,11 +236,19 @@ def degradation_year_on_year(energy_normalized, recenter=True,
244
236
  raise ValueError('energy_normalized must not be '
245
237
  'more frequent than daily')
246
238
 
247
- # Detect less than 2 years of data
248
- if energy_normalized.index[-1] - energy_normalized.index[0] < \
249
- pd.Timedelta('730d'):
250
- raise ValueError('must provide at least two years of '
251
- 'normalized energy')
239
+ # Detect less than 2 years of data. This is complicated by two things:
240
+ # - leap days muddle the precise meaning of "two years of data".
241
+ # - can't just check the number of days between the first and last
242
+ # index values, since non-daily (e.g. weekly) inputs span
243
+ # a longer period than their index values directly indicate.
244
+ # See the unit tests for several motivating cases.
245
+ if energy_normalized.index.inferred_freq is not None:
246
+ step = pd.tseries.frequencies.to_offset(energy_normalized.index.inferred_freq)
247
+ else:
248
+ step = energy_normalized.index.to_series().diff().median()
249
+
250
+ if energy_normalized.index[-1] < energy_normalized.index[0] + pd.DateOffset(years=2) - step:
251
+ raise ValueError('must provide at least two years of normalized energy')
252
252
 
253
253
  # If circular block bootstrapping...
254
254
  if uncertainty_method == 'circular_block':
@@ -282,11 +282,12 @@ def degradation_year_on_year(energy_normalized, recenter=True,
282
282
  tolerance=pd.Timedelta('8D')
283
283
  )
284
284
 
285
- df['time_diff_years'] = (df.dt - df.dt_right).astype('timedelta64[h]') / 8760.0
285
+ df['time_diff_years'] = (df.dt - df.dt_right) / pd.Timedelta('365d')
286
286
  df['yoy'] = 100.0 * (df.energy - df.energy_right) / (df.time_diff_years)
287
287
  df.index = df.dt
288
288
 
289
289
  yoy_result = df.yoy.dropna()
290
+
290
291
  df_right = df.set_index(df.dt_right).drop_duplicates('dt_right')
291
292
  df['usage_of_points'] = df.yoy.notnull().astype(int).add(
292
293
  df_right.yoy.notnull().astype(int), fill_value=0)
@@ -386,10 +387,8 @@ def _mk_test(x, alpha=0.05):
386
387
  n = len(x)
387
388
 
388
389
  # calculate S
389
- s = 0
390
- for k in range(n - 1):
391
- for j in range(k + 1, n):
392
- s += np.sign(x[j] - x[k])
390
+ x = np.array(x)
391
+ s = np.sum(np.triu(np.sign(-np.subtract.outer(x, x)), 1))
393
392
 
394
393
  # calculate the unique data
395
394
  unique_x = np.unique(x)
@@ -424,8 +424,9 @@ def logic_clip_filter(power_ac,
424
424
  # series sampling frequency is less than 95% consistent.
425
425
  _check_data_sampling_frequency(power_ac)
426
426
  # Get the sampling frequency of the time series
427
- time_series_sampling_frequency = power_ac.index.to_series().diff()\
428
- .astype('timedelta64[m]').mode()[0]
427
+ time_series_sampling_frequency = (
428
+ power_ac.index.to_series().diff() / pd.Timedelta('60s')
429
+ ).mode()[0]
429
430
  # Make copies of the original inputs for the cases that the data is
430
431
  # changes for clipping evaluation
431
432
  original_time_series_sampling_frequency = time_series_sampling_frequency
@@ -651,8 +652,7 @@ def xgboost_clip_filter(power_ac,
651
652
  # series sampling frequency is less than 95% consistent.
652
653
  _check_data_sampling_frequency(power_ac)
653
654
  # Get the most common sampling frequency
654
- sampling_frequency = int(power_ac.index.to_series().diff()
655
- .astype('timedelta64[m]').mode()[0])
655
+ sampling_frequency = int((power_ac.index.to_series().diff() / pd.Timedelta('60s')).mode()[0])
656
656
  freq_string = str(sampling_frequency) + "T"
657
657
  # Min-max normalize
658
658
  # Resample the series based on the most common sampling frequency
@@ -380,7 +380,7 @@ def irradiance_rescale(irrad, irrad_sim, max_iterations=100,
380
380
  '''
381
381
 
382
382
  if method == 'iterative':
383
- def _rmse(fact):
383
+ def _rmse(fact, filt):
384
384
  """
385
385
  Calculates RMSE with a given rescale fact(or) according to global
386
386
  filt(er)
@@ -392,10 +392,9 @@ def irradiance_rescale(irrad, irrad_sim, max_iterations=100,
392
392
 
393
393
  def _single_rescale(irrad, irrad_sim, guess):
394
394
  "Optimizes rescale factor once"
395
- global filt
396
395
  csi = irrad / (guess * irrad_sim) # clear sky index
397
396
  filt = (csi >= 0.8) & (csi <= 1.2) & (irrad > 200)
398
- min_result = minimize(_rmse, guess, method='Nelder-Mead')
397
+ min_result = minimize(_rmse, guess, (filt), method='Nelder-Mead')
399
398
 
400
399
  factor = min_result['x'][0]
401
400
  return factor
@@ -238,8 +238,8 @@ def soiling_interval_plot(soiling_info, normalized_yield, point_alpha=0.5,
238
238
  sratio = soiling_info['soiling_ratio_perfect_clean']
239
239
  fig, ax = plt.subplots()
240
240
  renormalized = normalized_yield / soiling_info['renormalizing_factor']
241
- ax.plot(renormalized.index, renormalized, 'o')
242
- ax.plot(sratio.index, sratio, 'o')
241
+ ax.plot(renormalized.index, renormalized, 'o', c=point_color, alpha=point_alpha)
242
+ ax.plot(sratio.index, sratio, 'o', c=profile_color, alpha=profile_alpha)
243
243
  ax.set_ylim(ymin, ymax)
244
244
  ax.set_ylabel('Renormalized energy')
245
245
 
@@ -1762,11 +1762,11 @@ class CODSAnalysis():
1762
1762
  self.soiling_loss = [0, 0, (1 - result_df.soiling_ratio).mean()]
1763
1763
  self.small_soiling_signal = True
1764
1764
  self.errors = (
1765
- 'Soiling signal is small relative to the noise.'
1766
- 'Iterative decomposition not possible.\n'
1767
- 'Degradation found by RdTools YoY')
1768
- print(self.errors)
1769
- return
1765
+ 'Soiling signal is small relative to the noise. '
1766
+ 'Iterative decomposition not possible. '
1767
+ 'Degradation found by RdTools YoY.')
1768
+ warnings.warn(self.errors)
1769
+ return self.result_df, self.degradation, self.soiling_loss
1770
1770
  self.small_soiling_signal = False
1771
1771
 
1772
1772
  # Aggregate all bootstrap samples
@@ -2507,7 +2507,8 @@ def _make_seasonal_samples(list_of_SCs, sample_nr=10, min_multiplier=0.5,
2507
2507
  ''' Generate seasonal samples by perturbing the amplitude and the phase of
2508
2508
  a seasonal components found with the fitted CODS model '''
2509
2509
  samples = pd.DataFrame(index=list_of_SCs[0].index,
2510
- columns=range(int(sample_nr*len(list_of_SCs))))
2510
+ columns=range(int(sample_nr*len(list_of_SCs))),
2511
+ dtype=float)
2511
2512
  # From each fitted signal, we will generate new seaonal components
2512
2513
  for i, signal in enumerate(list_of_SCs):
2513
2514
  # Remove beginning and end of signal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rdtools
3
- Version: 2.2.0b0
3
+ Version: 2.2.0b2
4
4
  Summary: Functions for reproducible timeseries analysis of photovoltaic systems.
5
5
  Home-page: https://github.com/NREL/rdtools
6
6
  Author: Rdtools Python Developers
@@ -1,12 +1,12 @@
1
1
  matplotlib>=3.0.0
2
- numpy>=1.16
3
- pandas!=0.24.0,!=1.0.0,!=1.0.1,>=0.23.2
2
+ numpy>=1.17.3
3
+ pandas<2.1,>=1.3.0
4
4
  statsmodels>=0.11.1
5
- scipy>=1.1.0
5
+ scipy>=1.2.0
6
6
  h5py>=2.8.0
7
7
  plotly>=4.0.0
8
8
  xgboost>=1.3.3
9
- pvlib<0.10.0,>=0.7.0
9
+ pvlib<0.11.0,>=0.7.0
10
10
  scikit-learn>=0.22.0
11
11
  arch>=4.11
12
12
  filterpy>=1.4.2
@@ -17,7 +17,7 @@ flake8
17
17
  ipython
18
18
  nbsphinx-link==1.3.0
19
19
  nbsphinx==0.8.8
20
- nbval
20
+ nbval==0.9.6
21
21
  pytest>=3.6.3
22
22
  pytest-mock
23
23
  sphinx-gallery==0.8.1
@@ -36,5 +36,5 @@ sphinx-gallery==0.8.1
36
36
  pytest>=3.6.3
37
37
  coverage
38
38
  flake8
39
- nbval
39
+ nbval==0.9.6
40
40
  pytest-mock
@@ -35,21 +35,23 @@ TESTS_REQUIRE = [
35
35
  'pytest >= 3.6.3',
36
36
  'coverage',
37
37
  'flake8',
38
- 'nbval',
38
+ 'nbval==0.9.6', # https://github.com/computationalmodelling/nbval/issues/194
39
39
  'pytest-mock',
40
40
  ]
41
41
 
42
42
  INSTALL_REQUIRES = [
43
43
  'matplotlib >= 3.0.0',
44
- 'numpy >= 1.16',
45
- # exclude pandas==1.0.0 & 1.0.1 for GH142, and 0.24.0 for GH114
46
- 'pandas >= 0.23.2,!=0.24.0,!=1.0.0,!=1.0.1',
44
+ 'numpy >= 1.17.3',
45
+ # pandas restricted to <2.1 until
46
+ # https://github.com/pandas-dev/pandas/issues/55794
47
+ # is resolved
48
+ 'pandas >= 1.3.0, <2.1',
47
49
  'statsmodels >= 0.11.1',
48
- 'scipy >= 1.1.0',
50
+ 'scipy >= 1.2.0',
49
51
  'h5py >= 2.8.0',
50
52
  'plotly>=4.0.0',
51
53
  'xgboost >= 1.3.3',
52
- 'pvlib >= 0.7.0, <0.10.0',
54
+ 'pvlib >= 0.7.0, <0.11.0',
53
55
  'scikit-learn >= 0.22.0',
54
56
  'arch >= 4.11',
55
57
  'filterpy >= 1.4.2'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes