:param pertibate_columns: list of str fo columns to pertibate see DOE
        :param filter_func: func that takes a SeabornRow and return
                            True if this row should be exist
        :param max_size: int of the max number of rows to try
                            but some may be filtered out
        :return:  None

def pertibate(self, pertibate_columns=None, filter_func=None,
                  max_size=1000):
        """
        :param pertibate_columns: list of str fo columns to pertibate see DOE
        :param filter_func: func that takes a SeabornRow and return
                            True if this row should be exist
        :param max_size: int of the max number of rows to try
                            but some may be filtered out
        :return:  None
        """
        pertibate_columns = pertibate_columns or self.columns
        for c in pertibate_columns:
            assert c in self.columns, 'Column %s was not part of this self' % c

        # noinspection PyTypeChecker
        column_size = [c in pertibate_columns and len(self._parameters[c]) or 1
                       for c in self.columns]

        max_size = min(max_size, reduce(lambda x, y: x * y, column_size))

        for indexes in self._index_iterator(column_size, max_size):
            row = SeabornRow(self._column_index,
                             [self._pertibate_value(indexes.pop(0), c) for
                              c in self.columns])

            kwargs = row.obj_to_dict()
            if filter_func is None or filter_func(_row_index=len(self.table),
                                                  **kwargs):
                self.table.append(row)

        for c in self.columns:  # if the parameter is a dynamic function
            if hasattr(self._parameters.get(c, ''), '__call__'):
                # noinspection PyTypeChecker
                self.set_column(c, self._parameters[c])