docs: def generate_visualizations(methods, data, true_labels, base_dir = 'visualizations',
          figsize=(18,10), **scatter_options):
      """
      Generates visualization scatters for all the methods.
  
      Args:
          methods: follows same format as run_experiments. List of tuples.
          data: genes x cells
          true_labels: array of integers
          base_dir: base directory to save all the plots
          figsize: tuple of ints representing size of figure
          scatter_options: options for plt.scatter
      """
      plt.figure(figsize=figsize)
      for method in methods:
          preproc= method[0]
          if isinstance(preproc, Preprocess):
              preprocessed, ll = preproc.run(data)
              output_names = preproc.output_names
          else:
              # if the input is a list, only use the first preproc result
              p1 = data
              output_names = ['']
              for p in preproc:
                  p1, ll = p.run(p1)
                  p1 = p1[0]
                  output_names[0] = output_names[0] + p.output_names[0]
              preprocessed = [p1]
          for r, name in zip(preprocessed, output_names):
              # TODO: cluster labels
              print(name)
              # if it's 2d, just display it... else, do tsne to reduce to 2d
              if r.shape[0]==2:
                  r_dim_red = r
              else:
                  # sometimes the data is too big to do tsne... (for sklearn)
                  if sparse.issparse(r) and r.shape[0] > 100:
                      name = 'tsvd_' + name
                      tsvd = TruncatedSVD(50)
                      r_dim_red = tsvd.fit_transform(r.T)
                      try:
                          tsne = TSNE(2)
                          r_dim_red = tsne.fit_transform(r_dim_red).T
                          name = 'tsne_' + name
                      except:
                          tsvd2 = TruncatedSVD(2)
                          r_dim_red = tsvd2.fit_transform(r_dim_red).T
                  else:
                      name = 'tsne_' + name
                      tsne = TSNE(2)
                      r_dim_red = tsne.fit_transform(r.T).T
              if isinstance(method[1], list):
                  for clustering_method in method[1]:
                      try:
                          cluster_labels = clustering_method.run(r)
                      except:
                          print('clustering failed')
                          continue
                      output_path = base_dir + '/{0}_{1}_labels.png'.format(name, clustering_method.name)
                      visualize_dim_red(r_dim_red, cluster_labels, output_path, **scatter_options)
              else:
                  clustering_method = method[1]
                  try:
                      cluster_labels = clustering_method.run(r)
                  except:
                      print('clustering failed')
                      continue
                  output_path = base_dir + '/{0}_{1}_labels.png'.format(name, clustering_method.name)
                  visualize_dim_red(r_dim_red, cluster_labels, output_path, **scatter_options)
              output_path = base_dir + '/{0}_true_labels.png'.format(name)
              visualize_dim_red(r_dim_red, true_labels, output_path, **scatter_options)