code: 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)