Make a DataFrame with the given columns.

        Will always return a copy of the underlying table.

        Parameters
        ----------
        columns : sequence or string, optional
            Sequence of the column names desired in the DataFrame. A string
            can also be passed if only one column is desired.
            If None all columns are returned, including registered columns.

        Returns
        -------
        frame : pandas.DataFrame

def to_frame(self, columns=None):
        """
        Make a DataFrame with the given columns.

        Will always return a copy of the underlying table.

        Parameters
        ----------
        columns : sequence or string, optional
            Sequence of the column names desired in the DataFrame. A string
            can also be passed if only one column is desired.
            If None all columns are returned, including registered columns.

        Returns
        -------
        frame : pandas.DataFrame

        """
        extra_cols = _columns_for_table(self.name)

        if columns is not None:
            columns = [columns] if isinstance(columns, str) else columns
            columns = set(columns)
            set_extra_cols = set(extra_cols)
            local_cols = set(self.local.columns) & columns - set_extra_cols
            df = self.local[list(local_cols)].copy()
            extra_cols = {k: extra_cols[k] for k in (columns & set_extra_cols)}
        else:
            df = self.local.copy()

        with log_start_finish(
                'computing {!r} columns for table {!r}'.format(
                    len(extra_cols), self.name),
                logger):
            for name, col in extra_cols.items():
                with log_start_finish(
                        'computing column {!r} for table {!r}'.format(
                            name, self.name),
                        logger):
                    df[name] = col()

        return df