def offline_plotly_scatter_bubble(df, x='x', y='y', size_col='size', text_col='text',
                                  category_col='category', possible_categories=None,
                                  filename=None,
                                  config={'displaylogo': False},
                                  xscale=None, yscale='log',
                                  layout={'hovermode': 'closest', 'showlegend': False, 'autosize': True},
                                  marker={'sizemode': 'area'},
                                  min_size=10,
                                  ):
    r
    config_default = dict(DEFAULT_PLOTLY_CONFIG)
    marker_default = {
        'size': size_col or min_size,
        'sizemode': 'area',
        'sizeref': int(df[size_col].min() * .8) if size_col else min_size}
    marker_default.update(marker)
    size_col = marker_default.pop('size')
    layout_default = {
        'xaxis': XAxis(title=x, type=xscale),
        'yaxis': YAxis(title=y, type=yscale),
    }
    layout_default.update(**layout)
    if config is not None:
        config_default.update(config)
    df.columns = clean_columns(df.columns)
    if possible_categories is None and category_col is not None:
        if category_col in df.columns:
            category_labels = df[category_col]
        else:
            category_labels = np.array(category_col)
        possible_categories = list(set(category_labels))
    possible_categories = [None] if possible_categories is None else possible_categories
    if category_col and category_col in df:
        masks = [np.array(df[category_col] == label) for label in possible_categories]
    else:
        masks = [np.array([True] * len(df))] * len(possible_categories)
    data = {'data': [
            Scatter(x=df[x][mask].values,
                    y=df[y][mask].values,
                    text=df[text_col][mask].values,
                    marker=Marker(size=df[size_col][mask] if size_col in df.columns else size_col,
                                  **marker_default),
                    mode='markers',
                    name=str(category_name)) for (category_name, mask) in zip(possible_categories, masks)
            ],
            'layout': Layout(**layout_default)
            }
    return offline_plotly_data(data, filename=filename, config=config_default)