コード例 #1
0
    def test_remove_column(self):
        mc = MarkedColumns()
        mc.add_y(4)
        mc.add_x(3)
        ec = ErrorColumn(column=2, related_y_column=6)
        mc.add_y_err(ec)

        self.assertEqual(1, len(mc.as_x))
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(4)
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))
        self.assertEqual(1, len(mc.as_x))

        mc.remove(3)
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(2)
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))
    def test_remove_column(self):
        mc = MarkedColumns()
        mc.add_y(4)
        mc.add_x(3)
        ec = ErrorColumn(column=2, related_y_column=6, label_index=0)
        mc.add_y_err(ec)

        self.assertEqual(1, len(mc.as_x))
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(4)
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))
        self.assertEqual(1, len(mc.as_x))

        mc.remove(3)
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(2)
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))
コード例 #3
0
    def test_build_labels_y_with_only_some_having_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y_err(ErrorColumn(2,1))

        expected = [(0, '[Y0]'), (1, '[Y1]'), (2, '[Y1_YErr]')]
        self.assertEqual(expected, mc.build_labels())
コード例 #4
0
    def test_fail_to_add_yerr_for_another_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(1, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        self.assertRaises(ValueError, lambda: mc.add_y_err(ErrorColumn(0, 1)))
    def test_fail_to_add_yerr_for_another_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(1, 0, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        self.assertRaises(ValueError, lambda: mc.add_y_err(ErrorColumn(0, 1, 2)))
コード例 #6
0
    def test_build_labels_y_and_yerr_change_middle(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)

        # change one of the columns to YErr
        mc.add_y_err(ErrorColumn(1, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        # change the last Y column to YErr
        mc.add_y_err(ErrorColumn(2, 0))
        expected = [(0, '[Y0]'), (2, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())
    def test_build_labels_y_and_yerr_change_middle(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)

        # change one of the columns to YErr
        mc.add_y_err(ErrorColumn(1, 0, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        # change the last Y column to YErr
        mc.add_y_err(ErrorColumn(2, 0, 0))
        expected = [(0, '[Y0]'), (2, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())
    def test_build_labels_y_and_yerr_change_first(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)

        # change one of the columns to YErr
        mc.add_y_err(ErrorColumn(0, 1, 1))
        # note: the first column is being set -> this decreases the label index of all columns to its right by 1
        expected = [(1, '[Y0]'), (2, '[Y1]'), (0, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        # change the last Y column to YErr
        mc.add_y_err(ErrorColumn(2, 1, 0))
        expected = [(1, '[Y0]'), (2, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())
コード例 #9
0
    def test_build_labels_y_and_yerr_change_first(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)

        # change one of the columns to YErr
        mc.add_y_err(ErrorColumn(0, 1))
        # note: the first column is being set -> this decreases the label index of all columns to its right by 1
        expected = [(1, '[Y0]'), (2, '[Y1]'), (0, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        # change the last Y column to YErr
        mc.add_y_err(ErrorColumn(2, 1))
        expected = [(1, '[Y0]'), (2, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())
コード例 #10
0
    def test_find_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(4, 1, 1))
        expected = {1: 4}
        self.assertEqual(expected, mc.find_yerr([1]))
        # Replace the Y column, which has an associated YErr. This should remove the YErr as well
        mc.add_y_err(ErrorColumn(1, 3, 1))
        expected = {3: 1}
        self.assertEqual(expected, mc.find_yerr([0, 1, 2, 3]))
        mc.add_y_err(ErrorColumn(4, 2, 1))
        expected = {2: 4, 3: 1}
        self.assertEqual(expected, mc.find_yerr([0, 1, 2, 3]))
コード例 #11
0
    def test_find_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(4, 1))
        expected = {1: 4}
        self.assertEqual(expected, mc.find_yerr([1]))
        # Replace the Y column, which has an associated YErr. This should remove the YErr as well
        mc.add_y_err(ErrorColumn(1, 3))
        expected = {3: 1}
        self.assertEqual(expected, mc.find_yerr([0, 1, 2, 3]))
        mc.add_y_err(ErrorColumn(4, 2))
        expected = {2: 4, 3: 1}
        self.assertEqual(expected, mc.find_yerr([0, 1, 2, 3]))
コード例 #12
0
    def test_build_labels_x_y(self):
        # TODO test this edge case: mark all columns Y, remove one that is not the last one!
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        # note that the max Y label number will decrease as more Y columns are being changed to X
        expected = [(0, '[Y0]'), (1, '[Y1]'), (2, '[Y2]'), (3, '[Y3]')]
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]')]
        mc.add_x(1)
        self.assertEqual(expected, mc.build_labels())
        expected = [(1, '[X0]'), (3, '[X1]'), (0, '[Y0]'), (2, '[Y1]')]
        mc.add_x(3)
        self.assertEqual(expected, mc.build_labels())
コード例 #13
0
    def test_build_labels_x_y_and_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(1, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]')]
        mc.add_x(1)
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (2, '[Y0]'), (3, '[Y1]'), (0, '[Y1_YErr]')]
        mc.add_y_err(ErrorColumn(0, 3))
        self.assertEqual(expected, mc.build_labels())
コード例 #14
0
    def test_build_labels_x_y_and_yerr(self):
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        mc.add_y_err(ErrorColumn(1, 0, 0))
        expected = [(0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]'), (1, '[Y0_YErr]')]
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]')]
        mc.add_x(1)
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (2, '[Y0]'), (3, '[Y1]'), (0, '[Y1_YErr]')]
        mc.add_y_err(ErrorColumn(0, 3, 2))
        self.assertEqual(expected, mc.build_labels())
コード例 #15
0
    def test_build_labels_x_y(self):
        # TODO test this edge case: mark all columns Y, remove one that is not the last one!
        mc = MarkedColumns()
        mc.add_y(0)
        mc.add_y(1)
        mc.add_y(2)
        mc.add_y(3)

        # note that the max Y label number will decrease as more Y columns are being changed to X
        expected = [(0, '[Y0]'), (1, '[Y1]'), (2, '[Y2]'), (3, '[Y3]')]
        self.assertEqual(expected, mc.build_labels())

        expected = [(1, '[X0]'), (0, '[Y0]'), (2, '[Y1]'), (3, '[Y2]')]
        mc.add_x(1)
        self.assertEqual(expected, mc.build_labels())
        expected = [(1, '[X0]'), (3, '[X1]'), (0, '[Y0]'), (2, '[Y1]')]
        mc.add_x(3)
        self.assertEqual(expected, mc.build_labels())
コード例 #16
0
    def test_changing_y_to_none_removes_associated_yerr_columns(self):
        """
        Test to check if a first column is marked as Y, a second column YErr is associated with it, but then
        the first one is changed to X - the YErr mark should be removed
        """
        mc = MarkedColumns()
        mc.add_y(4)
        ec = ErrorColumn(column=2, related_y_column=4, label_index=0)
        mc.add_y_err(ec)

        # check that we have both a Y col and an associated YErr
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(4)
        # changing the column to NONE should have removed it from X, Y and YErr
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))
コード例 #17
0
    def test_changing_y_to_none_removes_associated_yerr_columns(self):
        """
        Test to check if a first column is marked as Y, a second column YErr is associated with it, but then
        the first one is changed to X - the YErr mark should be removed
        """
        mc = MarkedColumns()
        mc.add_y(4)
        ec = ErrorColumn(column=2, related_y_column=4, label_index=0)
        mc.add_y_err(ec)

        # check that we have both a Y col and an associated YErr
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.remove(4)
        # changing the column to NONE should have removed it from X, Y and YErr
        self.assertEqual(0, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))
コード例 #18
0
    def test_changing_y_to_x_removes_associated_yerr_columns(self):
        """
        Test to check if a first column is marked as Y, a second column YErr is associated with it, but then
        the first one is changed to X - the YErr mark should be removed
        """
        mc = MarkedColumns()
        mc.add_y(4)
        ec = ErrorColumn(column=2, related_y_column=4)
        mc.add_y_err(ec)

        # check that we have both a Y col and an associated YErr
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(1, len(mc.as_y_err))

        mc.add_x(4)
        # changing the column to X should have removed it from Y and Yerr
        self.assertEqual(1, len(mc.as_x))
        self.assertEqual(0, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))

        # check setting the column back to Y does not automatically reinstate the error column
        mc.add_y(4)
        self.assertEqual(1, len(mc.as_y))
        self.assertEqual(0, len(mc.as_y_err))
コード例 #19
0
ファイル: model.py プロジェクト: mantidproject/mantid
class TableWorkspaceDisplayModel:
    SPECTRUM_PLOT_LEGEND_STRING = '{}-{}'
    BIN_PLOT_LEGEND_STRING = '{}-bin-{}'

    ALLOWED_WORKSPACE_TYPES = [PeaksWorkspace, TableWorkspace]

    @classmethod
    def supports(cls, ws):
        """
        Checks that the provided workspace is supported by this display.
        :param ws: Workspace to be checked for support
        :raises ValueError: if the workspace is not supported
        """
        if not any(isinstance(ws, allowed_type) for allowed_type in cls.ALLOWED_WORKSPACE_TYPES):
            raise ValueError("The workspace type is not supported: {0}".format(ws))

    def __init__(self, ws):
        """
        Initialise the model with the workspace
        :param ws: Workspace to be used for providing data
        :raises ValueError: if the workspace is not supported
        """
        self.supports(ws)

        self.ws = ws
        self.ws_num_rows = self.ws.rowCount()
        self.ws_num_cols = self.ws.columnCount()
        self.marked_columns = MarkedColumns()
        self._original_column_headers = self.get_column_headers()

        # loads the types of the columns
        for col in range(self.ws_num_cols):
            plot_type = self.ws.getPlotType(col)
            if plot_type == TableWorkspaceColumnTypeMapping.X:
                self.marked_columns.add_x(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.Y:
                self.marked_columns.add_y(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.YERR:
                # mark YErrs only if there are any columns that have been marked as Y
                # if there are none then do not mark anything as YErr
                if len(self.marked_columns.as_y) > len(self.marked_columns.as_y_err):
                    # Assume all the YErrs are associated with the first available (no other YErr has it) Y column.
                    # There isn't a way to know the correct Y column, as that information is not stored
                    # in the table workspace - the original table workspace does not associate Y errors
                    # columns with specific Y columns
                    err_for_column = self.marked_columns.as_y[len(self.marked_columns.as_y_err)]
                    label = str(len(self.marked_columns.as_y_err))
                    self.marked_columns.add_y_err(ErrorColumn(col, err_for_column, label))

    def _get_v3d_from_str(self, string):
        if '[' in string and ']' in string:
            string = string[1:-1]
        if ',' in string:
            return V3D(*[float(x) for x in string.split(',')])
        else:
            raise ValueError("'{}' is not a valid V3D string.".format(string))

    def original_column_headers(self):
        return self._original_column_headers[:]

    def build_current_labels(self):
        return self.marked_columns.build_labels()

    def get_name(self):
        return self.ws.name()

    def get_column_headers(self):
        return self.ws.getColumnNames()

    def get_column(self, index):
        return self.ws.column(index)

    def get_number_of_rows(self):
        return self.ws_num_rows

    def get_number_of_columns(self):
        return self.ws_num_cols

    def get_column_header(self, index):
        return self.get_column_headers()[index]

    def is_peaks_workspace(self):
        return isinstance(self.ws, PeaksWorkspace)

    def set_cell_data(self, row, col, data, is_v3d):
        # if the cell contains V3D data, construct a V3D object
        # from the string to that it can be properly set
        if is_v3d:
            data = self._get_v3d_from_str(data)
        # The False stops the replace workspace ADS event from being triggered
        # The replace event causes the TWD model to be replaced, which in turn
        # deletes the previous table item objects, however this happens
        # at the same time as we are trying to locally update the data in the
        # item object itself, which causes a Qt exception that the object has
        # already been deleted and a crash
        self.ws.setCell(row, col, data, notify_replace=False)

    def workspace_equals(self, workspace_name):
        return self.ws.name() == workspace_name

    def delete_rows(self, selected_rows):
        DeleteTableRows(self.ws, selected_rows)

    def get_statistics(self, selected_columns):
        stats = StatisticsOfTableWorkspace(self.ws, selected_columns)
        return stats

    def sort(self, column_index, sort_ascending):
        column_name = self.ws.getColumnNames()[column_index]
        if self.is_peaks_workspace():
            SortPeaksWorkspace(InputWorkspace=self.ws, OutputWorkspace=self.ws, ColumnNameToSortBy=column_name,
                               SortAscending=sort_ascending)
        else:
            SortTableWorkspace(InputWorkspace=self.ws, OutputWorkspace=self.ws, Columns=column_name,
                               Ascending=sort_ascending)
コード例 #20
0
class TableWorkspaceDisplayModel:
    SPECTRUM_PLOT_LEGEND_STRING = '{}-{}'
    BIN_PLOT_LEGEND_STRING = '{}-bin-{}'
    EDITABLE_COLUMN_NAMES = ['h', 'k', 'l']

    ALLOWED_WORKSPACE_TYPES = [ITableWorkspace]

    @classmethod
    def supports(cls, ws: ITableWorkspace):
        """
        Checks that the provided workspace is supported by this display.
        :param ws: Workspace to be checked for support
        :raises ValueError: if the workspace is not supported
        """
        if not any(
                isinstance(ws, allowed_type)
                for allowed_type in cls.ALLOWED_WORKSPACE_TYPES):
            raise ValueError(
                "The workspace type is not supported: {0}".format(ws))

    def __init__(self, ws: ITableWorkspace):
        """
        Initialise the model with the workspace
        :param ws: Workspace to be used for providing data
        :raises ValueError: if the workspace is not supported
        """
        self.supports(ws)

        self.ws: ITableWorkspace = ws
        self.ws_num_rows = self.ws.rowCount()
        self.ws_num_cols = self.ws.columnCount()
        self.marked_columns = MarkedColumns()
        self._original_column_headers = self.get_column_headers()

        # loads the types of the columns
        for col in range(self.ws_num_cols):
            plot_type = self.ws.getPlotType(col)
            if plot_type == TableWorkspaceColumnTypeMapping.X:
                self.marked_columns.add_x(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.Y:
                self.marked_columns.add_y(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.YERR:
                err_for_column = self.ws.getLinkedYCol(col)
                if err_for_column >= 0:
                    self.marked_columns.add_y_err(
                        ErrorColumn(col, err_for_column))

    def _get_v3d_from_str(self, string):
        if '[' in string and ']' in string:
            string = string[1:-1]
        if ',' in string:
            return V3D(*[float(x) for x in string.split(',')])
        else:
            raise ValueError("'{}' is not a valid V3D string.".format(string))

    def original_column_headers(self):
        return self._original_column_headers[:]

    def build_current_labels(self):
        return self.marked_columns.build_labels()

    def get_name(self):
        return self.ws.name()

    def get_column_headers(self):
        return self.ws.getColumnNames()

    def get_column(self, index):
        return self.ws.column(index)

    def get_cell(self, row, column):
        return self.ws.cell(row, column)

    def get_number_of_rows(self):
        return self.ws_num_rows

    def get_number_of_columns(self):
        return self.ws_num_cols

    def get_column_header(self, index):
        return self.get_column_headers()[index]

    def is_editable_column(self, icol):
        if self.is_peaks_workspace():
            return self.ws.getColumnNames()[icol] in self.EDITABLE_COLUMN_NAMES
        else:
            return not self.ws.isColumnReadOnly(icol)

    def is_peaks_workspace(self):
        return isinstance(self.ws, IPeaksWorkspace)

    def set_cell_data(self, row, col, data, is_v3d):
        if self.is_peaks_workspace():
            p = self.ws.getPeak(row)
            if self.ws.getColumnNames()[col] == "h":
                p.setH(data)
            elif self.ws.getColumnNames()[col] == "k":
                p.setK(data)
            elif self.ws.getColumnNames()[col] == "l":
                p.setL(data)
        else:
            # if the cell contains V3D data, construct a V3D object
            # from the string to that it can be properly set
            if is_v3d:
                data = self._get_v3d_from_str(data)
            # The False stops the replace workspace ADS event from being triggered
            # The replace event causes the TWD model to be replaced, which in turn
            # deletes the previous table item objects, however this happens
            # at the same time as we are trying to locally update the data in the
            # item object itself, which causes a Qt exception that the object has
            # already been deleted and a crash
            self.ws.setCell(row, col, data, notify_replace=False)

    def workspace_equals(self, workspace_name):
        return self.ws.name() == workspace_name

    def delete_rows(self, selected_rows):
        from mantid.simpleapi import DeleteTableRows
        DeleteTableRows(self.ws, selected_rows)

    def get_statistics(self, selected_columns):
        from mantid.simpleapi import StatisticsOfTableWorkspace
        stats = StatisticsOfTableWorkspace(self.ws, selected_columns)
        return stats

    def sort(self, column_index, sort_ascending):
        from mantid.simpleapi import SortPeaksWorkspace, SortTableWorkspace
        column_name = self.ws.getColumnNames()[column_index]
        if self.is_peaks_workspace():
            SortPeaksWorkspace(InputWorkspace=self.ws,
                               OutputWorkspace=self.ws,
                               ColumnNameToSortBy=column_name,
                               SortAscending=sort_ascending)
        else:
            SortTableWorkspace(InputWorkspace=self.ws,
                               OutputWorkspace=self.ws,
                               Columns=column_name,
                               Ascending=sort_ascending)

    def set_column_type(self, col, type, linked_col_index=-1):
        self.ws.setPlotType(col, type, linked_col_index)
コード例 #21
0
class TableWorkspaceDisplayModel:
    SPECTRUM_PLOT_LEGEND_STRING = '{}-{}'
    BIN_PLOT_LEGEND_STRING = '{}-bin-{}'

    ALLOWED_WORKSPACE_TYPES = [PeaksWorkspace, TableWorkspace]

    @classmethod
    def supports(cls, ws):
        """
        Checks that the provided workspace is supported by this display.
        :param ws: Workspace to be checked for support
        :raises ValueError: if the workspace is not supported
        """
        if not any(
                isinstance(ws, allowed_type)
                for allowed_type in cls.ALLOWED_WORKSPACE_TYPES):
            raise ValueError(
                "The workspace type is not supported: {0}".format(ws))

    def __init__(self, ws):
        """
        Initialise the model with the workspace
        :param ws: Workspace to be used for providing data
        :raises ValueError: if the workspace is not supported
        """
        self.supports(ws)

        self.ws = ws
        self.ws_num_rows = self.ws.rowCount()
        self.ws_num_cols = self.ws.columnCount()
        self.marked_columns = MarkedColumns()
        self._original_column_headers = self.get_column_headers()

        # loads the types of the columns
        for col in range(self.ws_num_cols):
            plot_type = self.ws.getPlotType(col)
            if plot_type == TableWorkspaceColumnTypeMapping.X:
                self.marked_columns.add_x(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.Y:
                self.marked_columns.add_y(col)
            elif plot_type == TableWorkspaceColumnTypeMapping.YERR:
                # mark YErrs only if there are any columns that have been marked as Y
                # if there are none then do not mark anything as YErr
                if len(self.marked_columns.as_y) > len(
                        self.marked_columns.as_y_err):
                    # Assume all the YErrs are associated with the first available (no other YErr has it) Y column.
                    # There isn't a way to know the correct Y column, as that information is not stored
                    # in the table workspace - the original table workspace does not associate Y errors
                    # columns with specific Y columns
                    err_for_column = self.marked_columns.as_y[len(
                        self.marked_columns.as_y_err)]
                    label = str(len(self.marked_columns.as_y_err))
                    self.marked_columns.add_y_err(
                        ErrorColumn(col, err_for_column, label))

    def _get_v3d_from_str(self, string):
        if '[' in string and ']' in string:
            string = string[1:-1]
        if ',' in string:
            return V3D(*[float(x) for x in string.split(',')])
        else:
            raise ValueError("'{}' is not a valid V3D string.".format(string))

    def original_column_headers(self):
        return self._original_column_headers[:]

    def build_current_labels(self):
        return self.marked_columns.build_labels()

    def get_name(self):
        return self.ws.name()

    def get_column_headers(self):
        return self.ws.getColumnNames()

    def get_column(self, index):
        return self.ws.column(index)

    def get_number_of_rows(self):
        return self.ws_num_rows

    def get_number_of_columns(self):
        return self.ws_num_cols

    def get_column_header(self, index):
        return self.get_column_headers()[index]

    def is_peaks_workspace(self):
        return isinstance(self.ws, PeaksWorkspace)

    def set_cell_data(self, row, col, data, is_v3d):
        # if the cell contains V3D data, construct a V3D object
        # from the string to that it can be properly set
        if is_v3d:
            data = self._get_v3d_from_str(data)
        # The False stops the replace workspace ADS event from being triggered
        # The replace event causes the TWD model to be replaced, which in turn
        # deletes the previous table item objects, however this happens
        # at the same time as we are trying to locally update the data in the
        # item object itself, which causes a Qt exception that the object has
        # already been deleted and a crash
        self.ws.setCell(row, col, data, notify_replace=False)

    def workspace_equals(self, workspace_name):
        return self.ws.name() == workspace_name

    def delete_rows(self, selected_rows):
        DeleteTableRows(self.ws, selected_rows)

    def get_statistics(self, selected_columns):
        stats = StatisticsOfTableWorkspace(self.ws, selected_columns)
        return stats

    def sort(self, column_index, sort_ascending):
        column_name = self.ws.getColumnNames()[column_index]
        if self.is_peaks_workspace():
            SortPeaksWorkspace(InputWorkspace=self.ws,
                               OutputWorkspace=self.ws,
                               ColumnNameToSortBy=column_name,
                               SortAscending=sort_ascending)
        else:
            SortTableWorkspace(InputWorkspace=self.ws,
                               OutputWorkspace=self.ws,
                               Columns=column_name,
                               Ascending=sort_ascending)