<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def _prepare_dataframe(adata, var_names, groupby=None, use_raw=None, log=False, num_categories=7, layer=None, gene_symbols=None):
 """ Given the anndata object, prepares a data frame in which the row index are the categories defined by group by and the columns correspond to var_names. Parameters adata : :class:`~anndata.AnnData` Annotated data matrix. var_names : `str` or list of `str` `var_names` should be a valid subset of `adata.var_names`. groupby : `str` or `None`, optional (default: `None`) The key of the observation grouping to consider. It is expected that groupby is a categorical. If groupby is not a categorical observation, it would be subdivided into `num_categories`. log : `bool`, optional (default: `False`) Use the log of the values use_raw : `bool`, optional (default: `None`) Use `raw` attribute of `adata` if present. num_categories : `int`, optional (default: `7`) Only used if groupby observation is not categorical. This value determines the number of groups into which the groupby observation should be subdivided. gene_symbols : string, optional (default: `None`) Key for field in .var that stores gene symbols. Returns ------- Tuple of `pandas.DataFrame` and list of categories. """

    from scipy.sparse import issparse
    sanitize_anndata(adata)
    if use_raw is None and adata.raw is not None: use_raw = True
    if isinstance(var_names, str):
        var_names = [var_names]

    if groupby is not None:
        if groupby not in adata.obs_keys():
            raise ValueError('groupby has to be a valid observation. Given value: {}, '
                             'valid observations: {}'.format(groupby, adata.obs_keys()))

    if gene_symbols is not None and gene_symbols in adata.var.columns:
        # translate gene_symbols to var_names
        # slow method but gives a meaningful error en case no gene symbol is found:
        translated_var_names = []
        for symbol in var_names:
            if symbol not in adata.var[gene_symbols].values:
                logg.error("Gene symbol {!r} not found in given gene_symbols column: {!r}".format(symbol, gene_symbols))
                return
            translated_var_names.append(adata.var[adata.var[gene_symbols] == symbol].index[0])
        symbols = var_names
        var_names = translated_var_names
    if layer is not None:
        if layer not in adata.layers.keys():
            raise KeyError('Selected layer: {} is not in the layers list. The list of '
                           'valid layers is: {}'.format(layer, adata.layers.keys()))
        matrix = adata[:, var_names].layers[layer]
    elif use_raw:
        matrix = adata.raw[:, var_names].X
    else:
        matrix = adata[:, var_names].X

    if issparse(matrix):
        matrix = matrix.toarray()
    if log:
        matrix = np.log1p(matrix)

    obs_tidy = pd.DataFrame(matrix, columns=var_names)
    if groupby is None:
        groupby = ''
        categorical = pd.Series(np.repeat('', len(obs_tidy))).astype('category')
    else:
        if not is_categorical_dtype(adata.obs[groupby]):
            # if the groupby column is not categorical, turn it into one
            # by subdividing into  `num_categories` categories
            categorical = pd.cut(adata.obs[groupby], num_categories)
        else:
            categorical = adata.obs[groupby]

    obs_tidy.set_index(categorical, groupby, inplace=True)
    if gene_symbols is not None:
        # translate the column names to the symbol names
        obs_tidy.rename(columns=dict([(var_names[x], symbols[x]) for x in range(len(var_names))]), inplace=True)
    categories = obs_tidy.index.categories

    return categories, obs_tidy