code: def expected_number_of_transactions_in_first_n_periods(self, n):
          r
          params = self._unload_params("alpha", "beta", "gamma", "delta")
          alpha, beta, gamma, delta = params
  
          x_counts = self.data.groupby("frequency")["weights"].sum()
          x = np.asarray(x_counts.index)
  
          p1 = binom(n, x) * exp(
              betaln(alpha + x, beta + n - x) - betaln(alpha, beta) + betaln(gamma, delta + n) - betaln(gamma, delta)
          )
  
          I = np.arange(x.min(), n)
  
          @np.vectorize
          def p2(j, x):
              i = I[int(j) :]
              return np.sum(
                  binom(i, x)
                  * exp(
                      betaln(alpha + x, beta + i - x)
                      - betaln(alpha, beta)
                      + betaln(gamma + 1, delta + i)
                      - betaln(gamma, delta)
                  )
              )
  
          p1 += np.fromfunction(p2, (x.shape[0],), x=x)
  
          idx = pd.Index(x, name="frequency")
          return DataFrame(p1 * x_counts.sum(), index=idx, columns=["model"])
docs: r"""
          Return expected number of transactions in first n n_periods.
  
          Expected number of transactions occurring across first n transaction
          opportunities.
          Used by Fader and Hardie to assess in-sample fit.
  
          .. math:: Pr(X(n) = x| \alpha, \beta, \gamma, \delta)
  
          See (7) in Fader & Hardie 2010.
  
          Parameters
          ----------
          n: float
              number of transaction opportunities
  
          Returns
          -------
          DataFrame:
              Predicted values, indexed by x