diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 35701bd3a2..fac828fa0a 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1647,26 +1647,41 @@ def retrieve_sam(name=None, path=None): return _parse_raw_sam_df(csvdata) +def _normalize_sam_product_names(names): + ''' + Replace special characters within the product names to make them more + suitable for use as Dataframe column names. + ''' + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019 + + import warnings + + BAD_CHARS = ' -.()[]:+/",' + GOOD_CHARS = '____________' + + mapping = str.maketrans(BAD_CHARS, GOOD_CHARS) + names = pd.Series(data=names) + norm_names = names.str.translate(mapping) + + n_duplicates = names.duplicated().sum() + if n_duplicates > 0: + warnings.warn('Original names contain %d duplicate(s).' % n_duplicates) + + n_duplicates = norm_names.duplicated().sum() + if n_duplicates > 0: + warnings.warn('Normalized names contain %d duplicate(s).' % n_duplicates) + + return norm_names.values + + def _parse_raw_sam_df(csvdata): + df = pd.read_csv(csvdata, index_col=0, skiprows=[1, 2]) - colnames = df.columns.values.tolist() - parsedcolnames = [] - for cn in colnames: - parsedcolnames.append(cn.replace(' ', '_')) - - df.columns = parsedcolnames - - parsedindex = [] - for index in df.index: - parsedindex.append(index.replace(' ', '_').replace('-', '_') - .replace('.', '_').replace('(', '_') - .replace(')', '_').replace('[', '_') - .replace(']', '_').replace(':', '_') - .replace('+', '_').replace('/', '_') - .replace('"', '_').replace(',', '_')) - - df.index = parsedindex + + df.columns = df.columns.str.replace(' ', '_') + df.index = _normalize_sam_product_names(df.index) df = df.transpose() + if 'ADRCoefficients' in df.index: ad_ce = 'ADRCoefficients' # for each inverter, parses a string of coefficients like diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 17f70e9d12..68d5a498e4 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -147,6 +147,28 @@ def test_PVSystem_physicaliam(mocker): pvsystem.physicaliam.assert_called_once_with(thetas, **module_parameters) assert iam < 1. +def test__normalize_sam_product_names(): + + BAD_NAMES = [' -.()[]:+/",', 'Module[1]'] + NORM_NAMES = ['____________', 'Module_1_'] + + norm_names = pvsystem._normalize_sam_product_names(BAD_NAMES) + assert list(norm_names) == NORM_NAMES + + BAD_NAMES = ['Module[1]', 'Module(1)'] + NORM_NAMES = ['Module_1_', 'Module_1_'] + + with pytest.warns(UserWarning): + norm_names = pvsystem._normalize_sam_product_names(BAD_NAMES) + assert list(norm_names) == NORM_NAMES + + BAD_NAMES = ['Module[1]', 'Module[1]'] + NORM_NAMES = ['Module_1_', 'Module_1_'] + + with pytest.warns(UserWarning): + norm_names = pvsystem._normalize_sam_product_names(BAD_NAMES) + assert list(norm_names) == NORM_NAMES + # if this completes successfully we'll be able to do more tests below. @pytest.fixture(scope="session")