docs: def by_col(cls, df, e, b, t=None, geom_col='geometry', inplace=False, **kwargs):
          """
          Compute smoothing by columns in a dataframe. The bounding box and point
          information is computed from the geometry column.
  
          Parameters
          -----------
          df      :  pandas.DataFrame
                     a dataframe containing the data to be smoothed
          e       :  string or list of strings
                     the name or names of columns containing event variables to be
                     smoothed
          b       :  string or list of strings
                     the name or names of columns containing the population
                     variables to be smoothed
          t       :  Headbanging_Triples instance or list of Headbanging_Triples
                     list of headbanging triples instances. If not provided, this
                     is computed from the geometry column of the dataframe.
          geom_col:  string
                     the name of the column in the dataframe containing the
                     geometry information.
          inplace :  bool
                     a flag denoting whether to output a copy of `df` with the
                     relevant smoothed columns appended, or to append the columns
                     directly to `df` itself.
          **kwargs:  optional keyword arguments
                     optional keyword options that are passed directly to the
                     smoother.
          Returns
          ---------
          a new dataframe containing the smoothed Headbanging Median Rates for the
          event/population pairs. If done inplace, there is no return value and
          `df` is modified in place.
          """
          import pandas as pd
          if not inplace:
              new = df.copy()
              cls.by_col(new, e, b, t=t, geom_col=geom_col, inplace=True, **kwargs)
              return new
          import pandas as pd
          # prep for application over multiple event/population pairs
          if isinstance(e, str):
              e = [e]
          if isinstance(b, str):
              b = [b]
          if len(e) > len(b):
              b = b * len(e)
  
          data = get_points_array(df[geom_col])
  
          #Headbanging_Triples doesn't take **kwargs, so filter its arguments
          # (self, data, w, k=5, t=3, angle=135.0, edgecor=False):
  
          w = kwargs.pop('w', None)
          if w is None:
              found = False
              for k in df._metadata:
                  w = df.__dict__.get(w, None)
                  if isinstance(w, W):
                      found = True
              if not found:
                  raise Exception('Weights not provided and no weights attached to frame!'
                                      ' Please provide a weight or attach a weight to the'
                                      ' dataframe')
  
          k = kwargs.pop('k', 5)
          t = kwargs.pop('t', 3)
          angle = kwargs.pop('angle', 135.0)
          edgecor = kwargs.pop('edgecor', False)
  
          hbt = Headbanging_Triples(data, w, k=k, t=t, angle=angle,
                                    edgecor=edgecor)
  
          res = []
          for ename, bname in zip(e, b):
              r = cls(df[ename], df[bname], hbt, **kwargs).r
              name = '_'.join(('-'.join((ename, bname)), cls.__name__.lower()))
              df[name] = r