docs: def fit(self, X, y=None):
          """
          Fits the JointPlot, creating a correlative visualization between the columns
          specified during initialization and the data and target passed into fit:
  
              - If self.columns is None then X and y must both be specified as 1D arrays
                or X must be a 2D array with only 2 columns.
              - If self.columns is a single int or str, that column is selected to be
                visualized against the target y.
              - If self.columns is two ints or strs, those columns are visualized against
                each other. If y is specified then it is used to color the points.
  
          This is the main entry point into the joint plot visualization.
  
          Parameters
          ----------
          X : array-like
              An array-like object of either 1 or 2 dimensions depending on self.columns.
              Usually this is a 2D table with shape (n, m)
  
          y : array-like, default: None
              An vector or 1D array that has the same length as X. May be used to either
              directly plot data or to color data points.
          """
          # Convert python objects to numpy arrays
          if isinstance(X, (list, tuple)):
              X = np.array(X)
  
          if y is not None and isinstance(y, (list, tuple)):
              y = np.array(y)
  
          # Case where no columns are specified
          if self.columns is None:
              if (y is None and (X.ndim != 2 or X.shape[1] != 2)) or (y is not None and (X.ndim != 1 or y.ndim != 1)):
                  raise YellowbrickValueError((
                      "when self.columns is None specify either X and y as 1D arrays "
                      "or X as a matrix with 2 columns"
                  ))
  
              if y is None:
                  # Draw the first column as x and the second column as y
                  self.draw(X[:,0], X[:,1], xlabel="0", ylabel="1")
                  return self
  
              # Draw x against y
              self.draw(X, y, xlabel="x", ylabel="y")
              return self
  
          # Case where a single string or int index is specified
          if isinstance(self.columns, (int,str)):
              if y is None:
                  raise YellowbrickValueError(
                      "when self.columns is a single index, y must be specified"
                  )
  
              # fetch the index from X -- raising index error if not possible
              x = self._index_into(self.columns, X)
              self.draw(x, y, xlabel=str(self.columns), ylabel="target")
              return self
  
          # Case where there is a double index for both columns
          columns = tuple(self.columns)
          if len(columns) != 2:
              raise YellowbrickValueError((
                      "'{}' contains too many indices or is invalid for joint plot"
                  ).format(columns))
  
          # TODO: color the points based on the target if it is given
          x = self._index_into(columns[0], X)
          y = self._index_into(columns[1], X)
          self.draw(x, y, xlabel=str(columns[0]), ylabel=str(columns[1]))
          return self