captest 0.12.1__py2.py3-none-any.whl → 0.13.0__py2.py3-none-any.whl

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.
captest/capdata.py CHANGED
@@ -40,7 +40,7 @@ from bokeh.io import show
40
40
  from bokeh.plotting import figure
41
41
  from bokeh.palettes import Category10
42
42
  from bokeh.layouts import gridplot
43
- from bokeh.models import Legend, HoverTool, ColumnDataSource
43
+ from bokeh.models import Legend, HoverTool, ColumnDataSource, NumeralTickFormatter
44
44
 
45
45
  import param
46
46
 
@@ -87,6 +87,7 @@ else:
87
87
  'pvlib package.')
88
88
 
89
89
  from captest import util
90
+ from captest import plotting
90
91
 
91
92
  plot_colors_brewer = {'real_pwr': ['#2b8cbe', '#7bccc4', '#bae4bc', '#f0f9e8'],
92
93
  'irr_poa': ['#e31a1c', '#fd8d3c', '#fecc5c', '#ffffb2'],
@@ -423,7 +424,34 @@ def check_all_perc_diff_comb(series, perc_diff):
423
424
  return all([perc_difference(x, y) < perc_diff for x, y in c])
424
425
 
425
426
 
426
- def sensor_filter(df, perc_diff):
427
+ def abs_diff_from_average(series, threshold):
428
+ """Check each value in series <= average of other values.
429
+
430
+ Drops NaNs from series before calculating difference from average for each value.
431
+
432
+ Returns True if there is only one value in the series.
433
+
434
+ Parameters
435
+ ----------
436
+ series : pd.Series
437
+ Pandas series of values to check.
438
+ threshold : numeric
439
+ Threshold value for absolute difference from average.
440
+
441
+ Returns
442
+ -------
443
+ bool
444
+ """
445
+ series = series.dropna()
446
+ if len(series) == 1:
447
+ return True
448
+ abs_diffs = []
449
+ for i, val in enumerate(series):
450
+ abs_diffs.append(abs(val - series.drop(series.index[i]).mean()) <= threshold)
451
+ return all(abs_diffs)
452
+
453
+
454
+ def sensor_filter(df, threshold, row_filter=check_all_perc_diff_comb):
427
455
  """
428
456
  Check dataframe for rows with inconsistent values.
429
457
 
@@ -436,8 +464,7 @@ def sensor_filter(df, perc_diff):
436
464
  Percent difference as decimal.
437
465
  """
438
466
  if df.shape[1] >= 2:
439
- bool_ser = df.apply(check_all_perc_diff_comb, perc_diff=perc_diff,
440
- axis=1)
467
+ bool_ser = df.apply(row_filter, args=(threshold, ), axis=1)
441
468
  return df[bool_ser].index
442
469
  elif df.shape[1] == 1:
443
470
  return df.index
@@ -1123,7 +1150,7 @@ def determine_pass_or_fail(cap_ratio, tolerance, nameplate):
1123
1150
  Limits for passing and failing test.
1124
1151
  """
1125
1152
  sign = tolerance.split(sep=' ')[0]
1126
- error = int(tolerance.split(sep=' ')[1]) / 100
1153
+ error = float(tolerance.split(sep=' ')[1]) / 100
1127
1154
 
1128
1155
  nameplate_plus_error = nameplate * (1 + error)
1129
1156
  nameplate_minus_error = nameplate * (1 - error)
@@ -1363,17 +1390,60 @@ def overlay_scatters(measured, expected, expected_label='PVsyst'):
1363
1390
 
1364
1391
 
1365
1392
  def index_capdata(capdata, label, filtered=True):
1393
+ """
1394
+ Like Dataframe.loc but for CapData objects.
1395
+
1396
+ Pass a single label or list of labels to select the columns from the `data` or
1397
+ `data_filtered` DataFrames. The label can be a column name, a column group key, or
1398
+ a regression column key.
1399
+
1400
+ The special label `regcols` will return the columns identified in `regression_cols`.
1401
+
1402
+ Parameters
1403
+ ----------
1404
+ capdata : CapData
1405
+ The CapData object to select from.
1406
+ label : str or list
1407
+ The label or list of labels to select from the `data` or `data_filtered`
1408
+ DataFrames. The label can be a column name, a column group key, or a
1409
+ regression column key. The special label `regcols` will return the columns
1410
+ identified in `regression_cols`.
1411
+ filtered : bool, default True
1412
+ By default the method will return columns from the `data_filtered` DataFrame.
1413
+ Set to False to return columns from the `data` DataFrame.
1414
+
1415
+ Returns
1416
+ --------
1417
+ DataFrame
1418
+ """
1366
1419
  if filtered:
1367
1420
  data = capdata.data_filtered
1368
1421
  else:
1369
1422
  data = capdata.data
1423
+ if label == 'regcols':
1424
+ label = list(capdata.regression_cols.values())
1370
1425
  if isinstance(label, str):
1371
1426
  if label in capdata.column_groups.keys():
1372
- return data[capdata.column_groups[label]]
1427
+ selected_data = data[capdata.column_groups[label]]
1373
1428
  elif label in capdata.regression_cols.keys():
1374
- return data[capdata.column_groups[capdata.regression_cols[label]]]
1429
+ col_or_grp = capdata.regression_cols[label]
1430
+ if col_or_grp in capdata.column_groups.keys():
1431
+ selected_data = data[capdata.column_groups[col_or_grp]]
1432
+ elif col_or_grp in data.columns:
1433
+ selected_data = data[col_or_grp]
1434
+ else:
1435
+ warnings.warn(
1436
+ 'Group or column "{}" mapped to the "{}" key of regression_cols '
1437
+ 'not found in column_groups keys or columns of CapData.data'.format(
1438
+ col_or_grp, label
1439
+ )
1440
+ )
1375
1441
  elif label in data.columns:
1376
- return data.loc[:, label]
1442
+ selected_data = data.loc[:, label]
1443
+ if isinstance(selected_data, pd.Series):
1444
+ return selected_data.to_frame()
1445
+ else:
1446
+ return selected_data
1377
1447
  elif isinstance(label, list):
1378
1448
  cols_to_return = []
1379
1449
  for l in label:
@@ -1420,15 +1490,15 @@ class FilteredLocIndexer(object):
1420
1490
 
1421
1491
  class CapData(object):
1422
1492
  """
1423
- Class to store capacity test data and translation of column names.
1493
+ Class to store capacity test data and column grouping.
1424
1494
 
1425
1495
  CapData objects store a pandas dataframe of measured or simulated data
1426
- and a dictionary used grouping columns by type of measurement.
1496
+ and a dictionary grouping columns by type of measurement.
1427
1497
 
1428
1498
  The `column_groups` dictionary allows maintaining the original column names
1429
1499
  while also grouping measurements of the same type from different
1430
1500
  sensors. Many of the methods for plotting and filtering data rely on the
1431
- column groupings to streamline user interaction.
1501
+ column groupings.
1432
1502
 
1433
1503
  Parameters
1434
1504
  ----------
@@ -1446,18 +1516,11 @@ class CapData(object):
1446
1516
  `group_columns` creates an abbreviated name and a list of columns that
1447
1517
  contain measurements of that type. The abbreviated names are the keys
1448
1518
  and the corresponding values are the lists of columns.
1449
- trans_keys : list
1450
- Simply a list of the `column_groups` keys.
1451
1519
  regression_cols : dictionary
1452
1520
  Dictionary identifying which columns in `data` or groups of columns as
1453
1521
  identified by the keys of `column_groups` are the independent variables
1454
1522
  of the ASTM Capacity test regression equation. Set using
1455
1523
  `set_regression_cols` or by directly assigning a dictionary.
1456
- trans_abrev : dictionary
1457
- Enumerated translation dict keys mapped to original column names.
1458
- Enumerated translation dict keys are used in plot hover tooltip.
1459
- col_colors : dictionary
1460
- Original column names mapped to a color for use in plot function.
1461
1524
  summary_ix : list of tuples
1462
1525
  Holds the row index data modified by the update_summary decorator
1463
1526
  function.
@@ -1482,10 +1545,7 @@ class CapData(object):
1482
1545
  self.data = pd.DataFrame()
1483
1546
  self.data_filtered = None
1484
1547
  self.column_groups = {}
1485
- self.trans_keys = []
1486
1548
  self.regression_cols = {}
1487
- self.trans_abrev = {}
1488
- self.col_colors = {}
1489
1549
  self.summary_ix = []
1490
1550
  self.summary = []
1491
1551
  self.removed = []
@@ -1493,8 +1553,9 @@ class CapData(object):
1493
1553
  self.filter_counts = {}
1494
1554
  self.rc = None
1495
1555
  self.regression_results = None
1496
- self.regression_formula = ('power ~ poa + I(poa * poa)'
1497
- '+ I(poa * t_amb) + I(poa * w_vel) - 1')
1556
+ self.regression_formula = (
1557
+ 'power ~ poa + I(poa * poa) + I(poa * t_amb) + I(poa * w_vel) - 1'
1558
+ )
1498
1559
  self.tolerance = None
1499
1560
  self.pre_agg_cols = None
1500
1561
  self.pre_agg_trans = None
@@ -1535,11 +1596,7 @@ class CapData(object):
1535
1596
  cd_c.data = self.data.copy()
1536
1597
  cd_c.data_filtered = self.data_filtered.copy()
1537
1598
  cd_c.column_groups = copy.copy(self.column_groups)
1538
- cd_c.trans_keys = copy.copy(self.trans_keys)
1539
1599
  cd_c.regression_cols = copy.copy(self.regression_cols)
1540
- cd_c.trans_abrev = copy.copy(self.trans_abrev)
1541
- cd_c.col_colors = copy.copy(self.col_colors)
1542
- cd_c.col_colors = copy.copy(self.col_colors)
1543
1600
  cd_c.summary_ix = copy.copy(self.summary_ix)
1544
1601
  cd_c.summary = copy.copy(self.summary)
1545
1602
  cd_c.rc = copy.copy(self.rc)
@@ -1552,37 +1609,9 @@ class CapData(object):
1552
1609
 
1553
1610
  def empty(self):
1554
1611
  """Return a boolean indicating if the CapData object contains data."""
1555
- tests_indicating_empty = [self.data.empty, len(self.trans_keys) == 0,
1556
- len(self.column_groups) == 0]
1612
+ tests_indicating_empty = [self.data.empty, len(self.column_groups) == 0]
1557
1613
  return all(tests_indicating_empty)
1558
1614
 
1559
- def set_plot_attributes(self):
1560
- """Set column colors used in plot method."""
1561
- # dframe = self.data
1562
-
1563
- group_id_regex = {
1564
- 'real_pwr': re.compile(r'real_pwr|pwr|meter_power|active_pwr|active_power', re.IGNORECASE),
1565
- 'irr_poa': re.compile(r'poa|irr_poa|poa_irr', re.IGNORECASE),
1566
- 'irr_ghi': re.compile(r'ghi|irr_ghi|ghi_irr', re.IGNORECASE),
1567
- 'temp_amb': re.compile(r'amb|temp.*amb', re.IGNORECASE),
1568
- 'temp_mod': re.compile(r'bom|temp.*bom|module.*temp.*|temp.*mod.*', re.IGNORECASE),
1569
- 'wind': re.compile(r'wind|w_vel|wspd|wind__', re.IGNORECASE),
1570
- }
1571
-
1572
- for group_id, cols_in_group in self.column_groups.items():
1573
- col_key = None
1574
- for plot_colors_group_key, regex in group_id_regex.items():
1575
- if regex.match(group_id):
1576
- col_key = plot_colors_group_key
1577
- break
1578
- for i, col in enumerate(cols_in_group):
1579
- try:
1580
- j = i % 4
1581
- self.col_colors[col] = plot_colors_brewer[col_key][j]
1582
- except KeyError:
1583
- j = i % 256
1584
- self.col_colors[col] = cc.glasbey_dark[j]
1585
-
1586
1615
  def drop_cols(self, columns):
1587
1616
  """
1588
1617
  Drop columns from CapData `data` and `column_groups`.
@@ -1625,7 +1654,10 @@ class CapData(object):
1625
1654
  """
1626
1655
  if reg_vars is None:
1627
1656
  reg_vars = list(self.regression_cols.keys())
1628
- df = self.rview(reg_vars, filtered_data=filtered_data).copy()
1657
+ if filtered_data:
1658
+ df = self.floc[reg_vars].copy()
1659
+ else:
1660
+ df = self.loc[reg_vars].copy()
1629
1661
  rename = {df.columns[0]: reg_vars}
1630
1662
 
1631
1663
  if isinstance(reg_vars, list):
@@ -1643,79 +1675,6 @@ class CapData(object):
1643
1675
  df.rename(columns=rename, inplace=True)
1644
1676
  return df
1645
1677
 
1646
- def view(self, tkey, filtered_data=False):
1647
- """
1648
- Convience function returns columns using `column_groups` names.
1649
-
1650
- Parameters
1651
- ----------
1652
- tkey: int or str or list of int or strs
1653
- String or list of strings from self.trans_keys or int postion or
1654
- list of int postitions of value in self.trans_keys.
1655
- """
1656
- if isinstance(tkey, int):
1657
- keys = self.column_groups[self.trans_keys[tkey]]
1658
- elif isinstance(tkey, list) and len(tkey) > 1:
1659
- keys = []
1660
- for key in tkey:
1661
- if isinstance(key, str):
1662
- keys.extend(self.column_groups[key])
1663
- elif isinstance(key, int):
1664
- keys.extend(self.column_groups[self.trans_keys[key]])
1665
- elif tkey in self.trans_keys:
1666
- keys = self.column_groups[tkey]
1667
-
1668
- if filtered_data:
1669
- return self.data_filtered[keys]
1670
- else:
1671
- return self.data[keys]
1672
-
1673
- def rview(self, ind_var, filtered_data=False):
1674
- """
1675
- Convience fucntion to return regression independent variable.
1676
-
1677
- Parameters
1678
- ----------
1679
- ind_var: string or list of strings
1680
- may be 'power', 'poa', 't_amb', 'w_vel', a list of some subset of
1681
- the previous four strings or 'all'
1682
- """
1683
- if ind_var == 'all':
1684
- keys = list(self.regression_cols.values())
1685
- elif isinstance(ind_var, list) and len(ind_var) > 1:
1686
- keys = [self.regression_cols[key] for key in ind_var]
1687
- elif ind_var in met_keys:
1688
- ind_var = [ind_var]
1689
- keys = [self.regression_cols[key] for key in ind_var]
1690
-
1691
- lst = []
1692
- for key in keys:
1693
- if key in self.data.columns:
1694
- lst.extend([key])
1695
- else:
1696
- lst.extend(self.column_groups[key])
1697
- if filtered_data:
1698
- return self.data_filtered[lst]
1699
- else:
1700
- return self.data[lst]
1701
-
1702
- def __comb_trans_keys(self, grp):
1703
- comb_keys = []
1704
-
1705
- for key in self.trans_keys:
1706
- if key.find(grp) != -1:
1707
- comb_keys.append(key)
1708
-
1709
- cols = []
1710
- for key in comb_keys:
1711
- cols.extend(self.column_groups[key])
1712
-
1713
- grp_comb = grp + '_comb'
1714
- if grp_comb not in self.trans_keys:
1715
- self.column_groups[grp_comb] = cols
1716
- self.trans_keys.extend([grp_comb])
1717
- print('Added new group: ' + grp_comb)
1718
-
1719
1678
  def review_column_groups(self):
1720
1679
  """Print `column_groups` with nice formatting."""
1721
1680
  if len(self.column_groups) == 0:
@@ -1746,9 +1705,9 @@ class CapData(object):
1746
1705
  Plots filtered data when true and all data when false.
1747
1706
  """
1748
1707
  if filtered:
1749
- df = self.rview(['power', 'poa'], filtered_data=True)
1708
+ df = self.floc[['power', 'poa']]
1750
1709
  else:
1751
- df = self.rview(['power', 'poa'], filtered_data=False)
1710
+ df = self.loc[['power', 'poa']]
1752
1711
 
1753
1712
  if df.shape[1] != 2:
1754
1713
  return warnings.warn('Aggregate sensors before using this '
@@ -1785,175 +1744,109 @@ class CapData(object):
1785
1744
  vdims = ['power', 'index']
1786
1745
  if all_reg_columns:
1787
1746
  vdims.extend(list(df.columns.difference(vdims)))
1747
+ hover = HoverTool(
1748
+ tooltips=[
1749
+ ('datetime', '@index{%Y-%m-%d %H:%M}'),
1750
+ ('poa', '@poa{0,0.0}'),
1751
+ ('power', '@power{0,0.0}'),
1752
+ ],
1753
+ formatters={
1754
+ '@index': 'datetime',
1755
+ }
1756
+ )
1788
1757
  poa_vs_kw = hv.Scatter(df, 'poa', vdims).opts(
1789
1758
  size=5,
1790
- tools=['hover', 'lasso_select', 'box_select'],
1759
+ tools=[hover, 'lasso_select', 'box_select'],
1791
1760
  legend_position='right',
1792
1761
  height=400,
1793
1762
  width=400,
1763
+ selection_fill_color='red',
1764
+ selection_line_color='red',
1765
+ yformatter=NumeralTickFormatter(format='0,0'),
1794
1766
  )
1795
1767
  # layout_scatter = (poa_vs_kw).opts(opt_dict)
1796
1768
  if timeseries:
1797
- poa_vs_time = hv.Curve(df, 'index', ['power', 'poa']).opts(
1798
- tools=['hover', 'lasso_select', 'box_select'],
1769
+ power_vs_time = hv.Scatter(df, 'index', ['power', 'poa']).opts(
1770
+ tools=[hover, 'lasso_select', 'box_select'],
1799
1771
  height=400,
1800
1772
  width=800,
1773
+ selection_fill_color='red',
1774
+ selection_line_color='red',
1801
1775
  )
1802
- layout_timeseries = (poa_vs_kw + poa_vs_time)
1803
- DataLink(poa_vs_kw, poa_vs_time)
1776
+ power_col, poa_col = self.loc[['power', 'poa']].columns
1777
+ power_vs_time_underlay = hv.Curve(
1778
+ self.data.rename_axis('index', axis='index'),
1779
+ 'index',
1780
+ [power_col, poa_col],
1781
+ ).opts(
1782
+ tools=['lasso_select', 'box_select'],
1783
+ height=400,
1784
+ width=800,
1785
+ line_color='gray',
1786
+ line_width=1,
1787
+ line_alpha=0.4,
1788
+ yformatter=NumeralTickFormatter(format='0,0'),
1789
+ )
1790
+ layout_timeseries = (poa_vs_kw + power_vs_time * power_vs_time_underlay)
1791
+ DataLink(poa_vs_kw, power_vs_time)
1804
1792
  return(layout_timeseries.cols(1))
1805
1793
  else:
1806
1794
  return(poa_vs_kw)
1807
1795
 
1808
- def plot(self, marker='line', ncols=1, width=1500, height=250,
1809
- legends=False, merge_grps=['irr', 'temp'], subset=None,
1810
- filtered=False, use_abrev_name=False, **kwargs):
1811
- """
1812
- Create a plot for each group of sensors in self.column_groups.
1813
-
1814
- Function returns a Bokeh grid of figures. A figure is generated for
1815
- each type of measurement identified by the keys in `column_groups` and
1816
- a line is plotted on the figure for each column of measurements of
1817
- that type.
1818
-
1819
- For example, if there are multiple plane of array irradiance sensors,
1820
- the data from each one will be plotted on a single figure.
1821
-
1822
- Figures are not generated for categories that would plot more than 10
1823
- lines.
1796
+ def plot(
1797
+ self,
1798
+ combine=plotting.COMBINE,
1799
+ default_groups=plotting.DEFAULT_GROUPS,
1800
+ width=1500,
1801
+ height=250,
1802
+ **kwargs,
1803
+ ):
1804
+ """
1805
+ Create a dashboard to explore timeseries plots of the data.
1806
+
1807
+ The dashboard contains three tabs: Groups, Layout, and Overlay. The first tab,
1808
+ Groups, presents a column of plots with a separate plot overlaying the measurements
1809
+ for each group of the `column_groups`. The groups plotted are defined by the
1810
+ `default_groups` argument.
1811
+
1812
+ The second tab, Layout, allows manually selecting groups to plot. The button
1813
+ on this tab can be used to replace the column of plots on the Groups tab with
1814
+ the current figure on the Layout tab. Rerun this method after clicking the button
1815
+ to see the new plots in the Groups tab.
1816
+
1817
+ The third tab, Overlay, allows picking a group or any combination of individual
1818
+ tags to overlay on a single plot. The list of groups and tags can be filtered
1819
+ using regular expressions. Adding a text id in the box and clicking Update will
1820
+ add the current overlay to the list of groups on the Layout tab.
1824
1821
 
1825
1822
  Parameters
1826
1823
  ----------
1827
- marker : str, default 'line'
1828
- Accepts 'line', 'circle', 'line-circle'. These are bokeh marker
1829
- options.
1830
- ncols : int, default 2
1831
- Number of columns in the bokeh gridplot.
1832
- width : int, default 400
1833
- Width of individual plots in gridplot.
1834
- height: int, default 350
1835
- Height of individual plots in gridplot.
1836
- legends : bool, default False
1837
- Turn on or off legends for individual plots.
1838
- merge_grps : list, default ['irr', 'temp']
1839
- List of strings to search for in the `column_groups` keys.
1840
- A new entry is added to `column_groups` with keys following the
1841
- format 'search str_comb' and the value is a list of column names
1842
- that contain the search string. The default will combine all
1843
- irradiance measurements into a group and temperature measurements
1844
- into a group.
1845
-
1846
- Pass an empty list to not merge any plots.
1847
-
1848
- Use 'irr-poa' and 'irr-ghi' to plot clear sky modeled with measured
1849
- data.
1850
- subset : list, default None
1851
- List of the keys of `column_groups` to control the order of to plot
1852
- only a subset of the plots or control the order of plots.
1853
- filtered : bool, default False
1854
- Set to true to plot the filtered data.
1855
- kwargs
1856
- Pass additional options to bokeh gridplot. Merge_tools=False will
1857
- shows the hover tool icon, so it can be turned off.
1824
+ combine : dict, optional
1825
+ Dictionary of group names and regex strings to use to identify groups from
1826
+ column groups and individual tags (columns) to combine into new groups. See the
1827
+ `parse_combine` function for more details.
1828
+ default_groups : list of str, optional
1829
+ List of regex strings to use to identify default groups to plot. See the
1830
+ `plotting.find_default_groups` function for more details.
1831
+ group_width : int, optional
1832
+ The width of the plots on the Groups tab.
1833
+ group_height : int, optional
1834
+ The height of the plots on the Groups tab.
1835
+ **kwargs : optional
1836
+ Additional keyword arguments are passed to the options of the scatter plot.
1858
1837
 
1859
1838
  Returns
1860
1839
  -------
1861
- show(grid)
1862
- Command to show grid of figures. Intended for use in jupyter
1863
- notebook.
1864
- """
1865
- for str_val in merge_grps:
1866
- self.__comb_trans_keys(str_val)
1867
-
1868
- if filtered:
1869
- dframe = self.data_filtered
1870
- else:
1871
- dframe = self.data
1872
- dframe.index.name = 'Timestamp'
1873
-
1874
- names_to_abrev = {val: key for key, val in self.trans_abrev.items()}
1875
-
1876
- plots = []
1877
- x_axis = None
1878
-
1879
- source = ColumnDataSource(dframe)
1880
-
1881
- hover = HoverTool()
1882
- hover.tooltips = [
1883
- ("Name", "$name"),
1884
- ("Datetime", "@Timestamp{%F %H:%M}"),
1885
- ("Value", "$y{0,0.00}"),
1886
- ]
1887
- hover.formatters = {"@Timestamp": "datetime"}
1888
-
1889
- tools = 'pan, xwheel_pan, xwheel_zoom, box_zoom, save, reset'
1890
-
1891
- if isinstance(subset, list):
1892
- plot_keys = subset
1893
- else:
1894
- plot_keys = self.trans_keys
1895
-
1896
- for j, key in enumerate(plot_keys):
1897
- df = dframe[self.column_groups[key]]
1898
- cols = df.columns.tolist()
1899
-
1900
- if x_axis is None:
1901
- p = figure(title=key, width=width, height=height,
1902
- x_axis_type='datetime', tools=tools)
1903
- p.tools.append(hover)
1904
- x_axis = p.x_range
1905
- if j > 0:
1906
- p = figure(title=key, width=width, height=height,
1907
- x_axis_type='datetime', x_range=x_axis, tools=tools)
1908
- p.tools.append(hover)
1909
- legend_items = []
1910
- for i, col in enumerate(cols):
1911
- if use_abrev_name:
1912
- name = names_to_abrev[col]
1913
- else:
1914
- name = col
1915
-
1916
- if col.find('csky') == -1:
1917
- line_dash = 'solid'
1918
- else:
1919
- line_dash = (5, 2)
1920
-
1921
- if marker == 'line':
1922
- try:
1923
- series = p.line('Timestamp', col, source=source,
1924
- line_color=self.col_colors[col],
1925
- line_dash=line_dash,
1926
- name=name)
1927
- except KeyError:
1928
- series = p.line('Timestamp', col, source=source,
1929
- line_dash=line_dash,
1930
- name=name)
1931
- elif marker == 'circle':
1932
- series = p.circle('Timestamp', col,
1933
- source=source,
1934
- line_color=self.col_colors[col],
1935
- size=2, fill_color="white",
1936
- name=name)
1937
- if marker == 'line-circle':
1938
- series = p.line('Timestamp', col, source=source,
1939
- line_color=self.col_colors[col],
1940
- name=name)
1941
- series = p.circle('Timestamp', col,
1942
- source=source,
1943
- line_color=self.col_colors[col],
1944
- size=2, fill_color="white",
1945
- name=name)
1946
- legend_items.append((col, [series, ]))
1947
-
1948
- legend = Legend(items=legend_items, location=(40, -5))
1949
- legend.label_text_font_size = '8pt'
1950
- if legends:
1951
- p.add_layout(legend, 'below')
1952
-
1953
- plots.append(p)
1954
-
1955
- grid = gridplot(plots, ncols=ncols, **kwargs)
1956
- return show(grid)
1840
+ Panel tabbed layout
1841
+ """
1842
+ return plotting.plot(
1843
+ self,
1844
+ combine=combine,
1845
+ default_groups=default_groups,
1846
+ group_width=width,
1847
+ group_height=height,
1848
+ **kwargs,
1849
+ )
1957
1850
 
1958
1851
  def scatter_filters(self):
1959
1852
  """
@@ -1966,7 +1859,7 @@ class CapData(object):
1966
1859
  scatters = []
1967
1860
 
1968
1861
  data = self.get_reg_cols(reg_vars=['power', 'poa'], filtered_data=False)
1969
- data['index'] = self.data.loc[:, 'index']
1862
+ data['index'] = self.data.index
1970
1863
  plt_no_filtering = hv.Scatter(data, 'poa', ['power', 'index']).relabel('all')
1971
1864
  scatters.append(plt_no_filtering)
1972
1865
 
@@ -1986,6 +1879,16 @@ class CapData(object):
1986
1879
  scatters.append(plt)
1987
1880
 
1988
1881
  scatter_overlay = hv.Overlay(scatters)
1882
+ hover = HoverTool(
1883
+ tooltips=[
1884
+ ('datetime', '@index{%Y-%m-%d %H:%M}'),
1885
+ ('poa', '@poa{0,0.0}'),
1886
+ ('power', '@power{0,0.0}'),
1887
+ ],
1888
+ formatters={
1889
+ '@index': 'datetime',
1890
+ }
1891
+ )
1989
1892
  scatter_overlay.opts(
1990
1893
  hv.opts.Scatter(
1991
1894
  size=5,
@@ -1994,7 +1897,8 @@ class CapData(object):
1994
1897
  muted_fill_alpha=0,
1995
1898
  fill_alpha=0.4,
1996
1899
  line_width=0,
1997
- tools=['hover'],
1900
+ tools=[hover],
1901
+ yformatter=NumeralTickFormatter(format='0,0'),
1998
1902
  ),
1999
1903
  hv.opts.Overlay(
2000
1904
  legend_position='right',
@@ -2014,8 +1918,8 @@ class CapData(object):
2014
1918
  plots = []
2015
1919
 
2016
1920
  data = self.get_reg_cols(reg_vars='power', filtered_data=False)
2017
- data.reset_index(inplace=True)
2018
- plt_no_filtering = hv.Curve(data, ['Timestamp'], ['power'], label='all')
1921
+ data['Timestamp'] = data.index
1922
+ plt_no_filtering = hv.Curve(data, ['Timestamp'], ['power'], label='all')
2019
1923
  plt_no_filtering.opts(
2020
1924
  line_color='black',
2021
1925
  line_width=1,
@@ -2024,10 +1928,10 @@ class CapData(object):
2024
1928
  )
2025
1929
  plots.append(plt_no_filtering)
2026
1930
 
2027
- d1 = self.rview('power').loc[self.removed[0]['index'], :]
1931
+ d1 = data.loc[self.removed[0]['index'], ['power', 'Timestamp']]
2028
1932
  plt_first_filter = hv.Scatter(
2029
- (d1.index, d1.iloc[:, 0]),
2030
- label=self.removed[0]['name'])
1933
+ d1, ['Timestamp'], ['power'], label=self.removed[0]['name']
1934
+ )
2031
1935
  plots.append(plt_first_filter)
2032
1936
 
2033
1937
  for i, filtering_step in enumerate(self.kept):
@@ -2035,18 +1939,30 @@ class CapData(object):
2035
1939
  break
2036
1940
  else:
2037
1941
  flt_legend = self.kept[i + 1]['name']
2038
- d_flt = self.rview('power').loc[filtering_step['index'], :]
2039
- plt = hv.Scatter((d_flt.index, d_flt.iloc[:, 0]), label=flt_legend)
1942
+ d_flt = data.loc[filtering_step['index'], :]
1943
+ plt = hv.Scatter(
1944
+ d_flt, ['Timestamp'], ['power'], label=flt_legend
1945
+ )
2040
1946
  plots.append(plt)
2041
1947
 
2042
1948
  scatter_overlay = hv.Overlay(plots)
1949
+ hover = HoverTool(
1950
+ tooltips=[
1951
+ ('datetime', '@Timestamp{%Y-%m-%d %H:%M}'),
1952
+ ('power', '@power{0,0.0}'),
1953
+ ],
1954
+ formatters={
1955
+ '@Timestamp': 'datetime',
1956
+ }
1957
+ )
2043
1958
  scatter_overlay.opts(
2044
1959
  hv.opts.Scatter(
2045
1960
  size=5,
2046
1961
  muted_fill_alpha=0,
2047
1962
  fill_alpha=1,
2048
1963
  line_width=0,
2049
- tools=['hover'],
1964
+ tools=[hover],
1965
+ yformatter=NumeralTickFormatter(format='0,0'),
2050
1966
  ),
2051
1967
  hv.opts.Overlay(
2052
1968
  legend_position='bottom',
@@ -2154,8 +2070,9 @@ class CapData(object):
2154
2070
  self.regression_cols['w_vel']: 'mean'}
2155
2071
 
2156
2072
  dfs_to_concat = []
2073
+ agg_names = {}
2157
2074
  for group_id, agg_func in agg_map.items():
2158
- columns_to_aggregate = self.view(group_id, filtered_data=False)
2075
+ columns_to_aggregate = self.loc[group_id]
2159
2076
  if columns_to_aggregate.shape[1] == 1:
2160
2077
  continue
2161
2078
  agg_result = columns_to_aggregate.agg(agg_func, axis=1).to_frame()
@@ -2165,23 +2082,23 @@ class CapData(object):
2165
2082
  col_name = group_id + '_' + agg_func.__name__ + '_agg'
2166
2083
  agg_result.rename(columns={agg_result.columns[0]: col_name}, inplace=True)
2167
2084
  dfs_to_concat.append(agg_result)
2085
+ agg_names[group_id] = col_name
2168
2086
 
2169
2087
  dfs_to_concat.append(self.data)
2170
2088
  # write over data and data_filtered attributes
2171
2089
  self.data = pd.concat(dfs_to_concat, axis=1)
2172
2090
  self.data_filtered = self.data.copy()
2173
2091
 
2174
- # update regression_cols attribute
2092
+ # update regression_cols attribute
2175
2093
  for reg_var, trans_group in self.regression_cols.items():
2176
- if self.rview(reg_var).shape[1] == 1:
2094
+ if self.loc[reg_var].shape[1] == 1:
2177
2095
  continue
2178
- if trans_group in agg_map.keys():
2179
- try:
2180
- agg_col = trans_group + '_' + agg_map[trans_group] + '_agg' # noqa: E501
2181
- except TypeError:
2182
- agg_col = trans_group + '_' + col_name + '_agg'
2183
- print(agg_col)
2184
- self.regression_cols[reg_var] = agg_col
2096
+ if trans_group in agg_names.keys():
2097
+ print(
2098
+ "Regression variable '{}' has been remapped: '{}' to '{}'"
2099
+ .format(reg_var, trans_group, agg_names[trans_group])
2100
+ )
2101
+ self.regression_cols[reg_var] = agg_names[trans_group]
2185
2102
 
2186
2103
  def data_columns_to_excel(self, sort_by_reversed_names=True):
2187
2104
  """
@@ -2490,7 +2407,7 @@ class CapData(object):
2490
2407
  Add option to return plot showing envelope with points not removed
2491
2408
  alpha decreased.
2492
2409
  """
2493
- XandY = self.rview(['poa', 'power'], filtered_data=True)
2410
+ XandY = self.floc[['poa', 'power']]
2494
2411
  if XandY.shape[1] > 2:
2495
2412
  return warnings.warn('Too many columns. Try running '
2496
2413
  'aggregate_sensors before using '
@@ -2533,7 +2450,7 @@ class CapData(object):
2533
2450
  Spec pf column
2534
2451
  Increase options to specify which columns are used in the filter.
2535
2452
  """
2536
- for key in self.trans_keys:
2453
+ for key in self.column_groups.keys():
2537
2454
  if key.find('pf') == 0:
2538
2455
  selection = key
2539
2456
 
@@ -2583,7 +2500,7 @@ class CapData(object):
2583
2500
  power_data = self.get_reg_cols('power')
2584
2501
  elif isinstance(columns, str):
2585
2502
  if columns in self.column_groups.keys():
2586
- power_data = self.view(columns, filtered_data=True)
2503
+ power_data = self.floc[columns]
2587
2504
  multiple_columns = True
2588
2505
  else:
2589
2506
  power_data = pd.DataFrame(self.data_filtered[columns])
@@ -2651,7 +2568,8 @@ class CapData(object):
2651
2568
  self.data_filtered = func(self.data_filtered, *args, **kwargs)
2652
2569
 
2653
2570
  @update_summary
2654
- def filter_sensors(self, perc_diff=None, inplace=True):
2571
+ def filter_sensors(
2572
+ self, perc_diff=None, inplace=True, row_filter=check_all_perc_diff_comb):
2655
2573
  """
2656
2574
  Drop suspicious measurments by comparing values from different sensors.
2657
2575
 
@@ -2687,16 +2605,18 @@ class CapData(object):
2687
2605
  poa_trans_key = regression_cols['poa']
2688
2606
  perc_diff = {poa_trans_key: 0.05}
2689
2607
 
2690
- for key, perc_diff_for_key in perc_diff.items():
2608
+ for key, threshold in perc_diff.items():
2691
2609
  if 'index' in locals():
2692
2610
  # if index has been assigned then take intersection
2693
2611
  sensors_df = df[trans[key]]
2694
- next_index = sensor_filter(sensors_df, perc_diff_for_key)
2612
+ next_index = sensor_filter(
2613
+ sensors_df, threshold, row_filter=row_filter)
2695
2614
  index = index.intersection(next_index) # noqa: F821
2696
2615
  else:
2697
2616
  # if index has not been assigned then assign it
2698
2617
  sensors_df = df[trans[key]]
2699
- index = sensor_filter(sensors_df, perc_diff_for_key)
2618
+ index = sensor_filter(
2619
+ sensors_df, threshold, row_filter=row_filter)
2700
2620
 
2701
2621
  df_out = self.data_filtered.loc[index, :]
2702
2622
 
@@ -2743,7 +2663,7 @@ class CapData(object):
2743
2663
  'load_data clear_sky option.')
2744
2664
  if ghi_col is None:
2745
2665
  ghi_keys = []
2746
- for key in self.trans_keys:
2666
+ for key in self.column_groups.keys():
2747
2667
  defs = key.split('-')
2748
2668
  if len(defs) == 1:
2749
2669
  continue
@@ -2758,7 +2678,7 @@ class CapData(object):
2758
2678
  else:
2759
2679
  meas_ghi = ghi_keys[0]
2760
2680
 
2761
- meas_ghi = self.view(meas_ghi, filtered_data=True)
2681
+ meas_ghi = self.floc[meas_ghi]
2762
2682
  if meas_ghi.shape[1] > 1:
2763
2683
  warnings.warn('Averaging measured GHI data. Pass column name '
2764
2684
  'to ghi_col to use a specific column.')
@@ -2957,8 +2877,7 @@ class CapData(object):
2957
2877
  pandas DataFrame
2958
2878
  If pred=True, then returns a pandas dataframe of results.
2959
2879
  """
2960
- df = self.rview(['poa', 't_amb', 'w_vel'],
2961
- filtered_data=True)
2880
+ df = self.floc[['poa', 't_amb', 'w_vel']]
2962
2881
  df = df.rename(columns={df.columns[0]: 'poa',
2963
2882
  df.columns[1]: 't_amb',
2964
2883
  df.columns[2]: 'w_vel'})
@@ -3046,8 +2965,7 @@ class CapData(object):
3046
2965
  See pandas Grouper doucmentation for details. Default is left
3047
2966
  labeled and left closed.
3048
2967
  """
3049
- df = self.rview(['poa', 't_amb', 'w_vel', 'power'],
3050
- filtered_data=True)
2968
+ df = self.floc[['poa', 't_amb', 'w_vel', 'power']]
3051
2969
  df = df.rename(columns={df.columns[0]: 'poa',
3052
2970
  df.columns[1]: 't_amb',
3053
2971
  df.columns[2]: 'w_vel',
@@ -3153,7 +3071,7 @@ class CapData(object):
3153
3071
  """
3154
3072
  spatial_uncerts = {}
3155
3073
  for group in column_groups:
3156
- df = self.view(group, filtered_data=True)
3074
+ df = self.floc[group]
3157
3075
  # prevent aggregation from updating column groups?
3158
3076
  # would not need the below line then
3159
3077
  df = df[[col for col in df.columns if 'agg' not in col]]