Repository: graspologic-org/graspologic

Issue:
Refactor LCC check and n_features_ attribute setting to BaseEmbed
ASE/LSE fit have some redundant code that could be in base

see #462 


Discussion:
This might be a dumb question, but wouldn't the fit method in ASE/LSE overridden the fit method in base, and how would we implement an abstract method in base?
it is possible to have a fit in base and call it using `super().method()`. idea would be to use that to take care of some of the redundant parts
I'd be interested in this issue
DoD:

- Put code that exists in ASE and LSE in Base

- Ensure correct functionality by running a simulation comparing the original ASE/LSE to the revised ASE/LSE and ensure that similar pairplots are generated
great start kareef. over the week or two, need to specify what code there is that is redundant in this way.

Resolving patch:
diff --git a/graspologic/embed/ase.py b/graspologic/embed/ase.py
--- a/graspologic/embed/ase.py
+++ b/graspologic/embed/ase.py
@@ -154,21 +154,11 @@ def fit(self, graph, y=None):
         self : object
             Returns an instance of self.
         """
-        A = import_graph(graph)
-
-        if self.check_lcc:
-            if not is_fully_connected(A):
-                msg = (
-                    "Input graph is not fully connected. Results may not"
-                    + "be optimal. You can compute the largest connected component by"
-                    + "using ``graspologic.utils.largest_connected_component``."
-                )
-                warnings.warn(msg, UserWarning)
+        A = self._fit(graph)
 
         if self.diag_aug:
             A = augment_diagonal(A)
 
-        self.n_features_in_ = A.shape[0]
         self._reduce_dim(A)
 
         # for out-of-sample
diff --git a/graspologic/embed/base.py b/graspologic/embed/base.py
--- a/graspologic/embed/base.py
+++ b/graspologic/embed/base.py
@@ -4,9 +4,15 @@
 from abc import abstractmethod
 
 import numpy as np
+import warnings
 from sklearn.base import BaseEstimator
 
-from ..utils import augment_diagonal, import_graph, is_almost_symmetric
+from ..utils import (
+    augment_diagonal,
+    import_graph,
+    is_almost_symmetric,
+    is_fully_connected,
+)
 from .svd import selectSVD
 
 
@@ -112,19 +118,15 @@ def _pairwise(self):
     def fit(self, graph, y=None):
         """
         A method for embedding.
-
         Parameters
         ----------
         graph: np.ndarray or networkx.Graph
-
         y : Ignored
-
         Returns
         -------
         lpm : LatentPosition object
             Contains X (the estimated latent positions), Y (same as X if input is
             undirected graph, or right estimated positions if directed graph), and d.
-
         See Also
         --------
         import_graph, LatentPosition
@@ -135,6 +137,40 @@ def fit(self, graph, y=None):
 
         return self
 
+    def _fit(self, graph, y=None):
+        """
+        A method for embedding.
+
+        Parameters
+        ----------
+        graph: np.ndarray or networkx.Graph
+
+        y : Ignored
+
+        Returns
+        -------
+        A : array-like, shape (n_vertices, n_vertices)
+            A graph
+
+        See Also
+        --------
+        import_graph, LatentPosition
+        """
+
+        A = import_graph(graph)
+
+        if self.check_lcc:
+            if not is_fully_connected(A):
+                msg = (
+                    "Input graph is not fully connected. Results may not"
+                    + "be optimal. You can compute the largest connected component by"
+                    + "using ``graspologic.utils.largest_connected_component``."
+                )
+                warnings.warn(msg, UserWarning)
+
+        self.n_features_in_ = A.shape[0]
+        return A
+
     def _fit_transform(self, graph):
         "Fits the model and returns the estimated latent positions."
         self.fit(graph)
diff --git a/graspologic/embed/lse.py b/graspologic/embed/lse.py
--- a/graspologic/embed/lse.py
+++ b/graspologic/embed/lse.py
@@ -147,18 +147,7 @@ def fit(self, graph, y=None):
         self : object
             Returns an instance of self.
         """
-        A = import_graph(graph)
-
-        if self.check_lcc:
-            if not is_fully_connected(A):
-                msg = (
-                    "Input graph is not fully connected. Results may not"
-                    + "be optimal. You can compute the largest connected component by"
-                    + "using ``graspologic.utils.largest_connected_component``."
-                )
-                warnings.warn(msg, UserWarning)
-
-        self.n_features_in_ = A.shape[0]
+        A = self._fit(graph)
         L_norm = to_laplacian(A, form=self.form, regularizer=self.regularizer)
         self._reduce_dim(L_norm)
         return self


Test patch: