diff --git a/pydatview/common.py b/pydatview/common.py index 1002c5e..7d15bc6 100644 --- a/pydatview/common.py +++ b/pydatview/common.py @@ -511,8 +511,15 @@ def exception2string(excp, iMax=40, prefix=' | ', prevStack=True): # --- # --------------------------------------------------------------------------------{ def isString(x): - b = x.dtype == object and isinstance(x.values[0], str) - return b + if len(x) == 0: + return False + if isinstance(x, list): + return isinstance(x[0], str) + if hasattr(x, 'values'): # handles pandas Series/DataFrame and xarray DataArray + val = x.values.flat[0] + else: # handles numpy array + val = x.flat[0] + return isinstance(val, str) def isDate(x): return np.issubdtype(x.dtype, np.datetime64) diff --git a/pydatview/plotdata.py b/pydatview/plotdata.py index 820dd82..372625d 100644 --- a/pydatview/plotdata.py +++ b/pydatview/plotdata.py @@ -1,5 +1,6 @@ import os import numpy as np +import pandas from pydatview.common import no_unit, unit, inverse_unit, splitunit, has_chinese_char from pydatview.common import isString, isDate, getDt from pydatview.common import unique, pretty_num, pretty_time, pretty_date @@ -10,6 +11,7 @@ except AttributeError: trapz = np.trapz +MAX_UNIQUE_STRING_TO_PLOT = 1000 # Potentially put this in user file # --------------------------------------------------------------------------------} # --- PlotDataList functions @@ -70,6 +72,7 @@ def __init__(PD, x=None, y=None, sx='', sy=''): PD.xyMeasInput1 = (None, None) PD.xyMeasInput2 = (None, None) PD.xyMeas = [(None,None)]*2 # 2 measures for now + PD.MAX_UNIQUE_STRING_TO_PLOT = MAX_UNIQUE_STRING_TO_PLOT if x is not None and y is not None: PD.fromXY(x,y,sx,sy) @@ -118,12 +121,15 @@ def _post_init(PD, pipeline=None): # --- Store stats - n=len(PD.y) - if n>1000: - if (PD.xIsString): - raise Exception('Error: x values contain more than 1000 string. This is not suitable for plotting.\n\nPlease select another column for table: {}\nProblematic column: {}\n'.format(PD.st,PD.sx)) - if (PD.yIsString): - raise Exception('Error: y values contain more than 1000 string. This is not suitable for plotting.\n\nPlease select another column for table: {}\nProblematic column: {}\n'.format(PD.st,PD.sy)) + n = len(PD.y) + if PD.xIsString: + nu = len(np.unique(PD.x)) + if nu > PD.MAX_UNIQUE_STRING_TO_PLOT: + raise Exception(f'Error: x values contain more than {PD.MAX_UNIQUE_STRING_TO_PLOT} unique strings. This is not suitable for plotting.\n\nPlease select another column for table: {PD.st}\nProblematic column: {PD.sx}\n') + if PD.yIsString: + nu = len(np.unique(PD.y)) + if nu > PD.MAX_UNIQUE_STRING_TO_PLOT: + raise Exception(f'Error: y values contain more than {PD.MAX_UNIQUE_STRING_TO_PLOT} unique strings. This is not suitable for plotting.\n\nPlease select another column for table: {PD.st}\nProblematic column: {PD.sy}\n') PD.needChineseFont = has_chinese_char(PD.sy) or has_chinese_char(PD.sx) # Stats of the raw data (computed once and for all, since it can be expensive for large dataset diff --git a/tests/test_plotdata.py b/tests/test_plotdata.py index 6575145..28f47f9 100644 --- a/tests/test_plotdata.py +++ b/tests/test_plotdata.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt -from pydatview.plotdata import PlotData +from pydatview.plotdata import PlotData, MAX_UNIQUE_STRING_TO_PLOT class TestPlotData(unittest.TestCase): @@ -115,7 +115,21 @@ def test_fatigue(self): np.testing.assert_almost_equal(v, 9.4714702, 3) + def test_plotManyStrings(self): + # Test for an array of size less than MAX_UNIQUE_STRING_TO_PLOT + x = np.linspace(-2, 2, MAX_UNIQUE_STRING_TO_PLOT) + y = np.asarray([f"s_{i}" for i in range(1, len(x) + 1)]) + PD = PlotData(x,y) + self.assertEqual(PD.xIsString, False) + self.assertEqual(PD.yIsString, True) + # Test for a larger array + x = np.linspace(-2, 2, MAX_UNIQUE_STRING_TO_PLOT+1) + y = np.asarray([f"s_{i}" for i in range(1, len(x) + 1)]) + with self.assertRaises(Exception): + PD = PlotData(x,y) + if __name__ == '__main__': - unittest.main() + TestPlotData().test_plotManyStrings() +# unittest.main()