Repository: pgmpy/pgmpy

Issue:
[BUG] BayesianEstimator incorrectly exclude independent node in DiscreteBayesianNetwork
**Describe the bug**
<!--
A clear and concise description of what the bug is.
-->

In the BayesianEstimator constructor, any node in the DiscreteBayesianNetwork that isn’t connected to an edge is inadvertently removed.


```
class BayesianEstimator(ParameterEstimator):
    """
    Class used to compute parameters for a model using Bayesian Parameter Estimation.
    See `MaximumLikelihoodEstimator` for constructor parameters.
    """

    def __init__(
        self,
        model: Union[DAG, DiscreteBayesianNetwork],
        data: pd.DataFrame,
        **kwargs,
    ):
        if not isinstance(model, (DAG, DiscreteBayesianNetwork)):
            raise NotImplementedError(
                "Bayesian Parameter Estimation is only implemented for DAG or DiscreteBayesianNetwork"
            )

        else:
            if len(model.latents) != 0:
                raise ValueError(
                    f"Bayesian Parameter Estimation works only "
                    f"on models with all observed variables. Found latent variables: {model.latents}"
                )

            elif isinstance(model, DAG):
                model = DiscreteBayesianNetwork(model.edges()) # Add only nodes included in edges
                model.add_nodes_from(model.nodes()) # Input model does not have nodes not included in edges

        super(BayesianEstimator, self).__init__(model, data, **kwargs)
```

All nodes which have no edge are excluded during `DiscreteBayesianNetwork(model.edges())`.

**Expected behavior**
<!--
A clear and concise description of what you expected to happen.
-->

Reconstructed model should include nodes not included in edges of original model.

**Additional context**
<!--
Add any other context about the problem here.
-->

**Versions** 1.0.0


Discussion hints:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 94.91%. Comparing base ([`2bba5dc`](https://app.codecov.io/gh/pgmpy/pgmpy/commit/2bba5dc71b8d0e0019bb28733605439352d13a79?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)) to head ([`4e2c2ff`](https://app.codecov.io/gh/pgmpy/pgmpy/commit/4e2c2fffd475dec53ddef1e4ff1fd6b0893271e3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)).
:warning: Report is 58 commits behind head on dev.

<details><summary>Additional details and impacted files</summary>



[![Impacted file tree graph](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271/graphs/tree.svg?width=650&height=150&src=pr&token=UaJMCdHaEF&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)

```diff
@@            Coverage Diff             @@
##              dev    #2271      +/-   ##
==========================================
+ Coverage   94.59%   94.91%   +0.32%     
==========================================
  Files         149      152       +3     
  Lines       20155    21609    +1454     
==========================================
+ Hits        19065    20511    +1446     
- Misses       1090     1098       +8     
```

| [Files with missing lines](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) | Coverage Δ | |
|---|---|---|
| [pgmpy/estimators/BayesianEstimator.py](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&filepath=pgmpy%2Festimators%2FBayesianEstimator.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvZXN0aW1hdG9ycy9CYXllc2lhbkVzdGltYXRvci5weQ==) | `93.22% <100.00%> (+0.49%)` | :arrow_up: |
| [pgmpy/tests/test\_base/test\_DAG.py](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&filepath=pgmpy%2Ftests%2Ftest_base%2Ftest_DAG.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvdGVzdHMvdGVzdF9iYXNlL3Rlc3RfREFHLnB5) | `100.00% <100.00%> (ø)` | |

... and [16 files with indirect coverage changes](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)
</details>
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 94.92%. Comparing base ([`2bba5dc`](https://app.codecov.io/gh/pgmpy/pgmpy/commit/2bba5dc71b8d0e0019bb28733605439352d13a79?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)) to head ([`ac79753`](https://app.codecov.io/gh/pgmpy/pgmpy/commit/ac797531d6713ac8de1ad202941b40a17d78ede7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)).
:warning: Report is 63 commits behind head on dev.

<details><summary>Additional details and impacted files</summary>



[![Impacted file tree graph](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271/graphs/tree.svg?width=650&height=150&src=pr&token=UaJMCdHaEF&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)

```diff
@@            Coverage Diff             @@
##              dev    #2271      +/-   ##
==========================================
+ Coverage   94.59%   94.92%   +0.33%     
==========================================
  Files         149      154       +5     
  Lines       20155    21735    +1580     
==========================================
+ Hits        19065    20632    +1567     
- Misses       1090     1103      +13     
```

| [Files with missing lines](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy) | Coverage Δ | |
|---|---|---|
| [pgmpy/estimators/BayesianEstimator.py](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&filepath=pgmpy%2Festimators%2FBayesianEstimator.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvZXN0aW1hdG9ycy9CYXllc2lhbkVzdGltYXRvci5weQ==) | `93.22% <100.00%> (+0.49%)` | :arrow_up: |
| [pgmpy/tests/test\_base/test\_DAG.py](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271?src=pr&el=tree&filepath=pgmpy%2Ftests%2Ftest_base%2Ftest_DAG.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy#diff-cGdtcHkvdGVzdHMvdGVzdF9iYXNlL3Rlc3RfREFHLnB5) | `100.00% <100.00%> (ø)` | |

... and [19 files with indirect coverage changes](https://app.codecov.io/gh/pgmpy/pgmpy/pull/2271/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pgmpy)
</details>

Solution patch:
diff --git a/.flake8 b/.flake8
new file mode 100644
index 00000000..a7e6f8e9
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+max-line-length = 120
+ignore = E203,E231,E241,E222,E221,W503,E712
+exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
diff --git a/pgmpy/estimators/BayesianEstimator.py b/pgmpy/estimators/BayesianEstimator.py
index 5b2def78..78558893 100644
--- a/pgmpy/estimators/BayesianEstimator.py
+++ b/pgmpy/estimators/BayesianEstimator.py
@@ -40,8 +40,10 @@ class BayesianEstimator(ParameterEstimator):
                 )
 
             elif isinstance(model, DAG):
-                model = DiscreteBayesianNetwork(model.edges())
-                model.add_nodes_from(model.nodes())
+                edges = model.edges()
+                nodes = model.nodes()
+                model = DiscreteBayesianNetwork(edges)
+                model.add_nodes_from(nodes)
 
         super(BayesianEstimator, self).__init__(model, data, **kwargs)
 
diff --git a/pyproject.toml b/pyproject.toml
index 9c65c266..08592bfd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -97,26 +97,6 @@ pgmpy = [
 [tool.setuptools]
 include-package-data = true
 
-[tool.flake8]
-max-line-length = 120
-ignore = [
-  "E203",
-  "E231",
-  "E241",
-  "E222",
-  "E221",
-  "W503",
-  "E712",
-]
-exclude = [
-  ".git",
-  "__pycache__",
-  "docs/source/conf.py",
-  "old",
-  "build",
-  "dist",
-]
-
 [tool.pytest.ini_options]
 addopts = "-p no:doctest"
 norecursedirs = [".git", "ignore", "build", "__pycache__"]


Test patch:
diff --git a/pgmpy/tests/test_base/test_DAG.py b/pgmpy/tests/test_base/test_DAG.py
index fdf43e5c..f679175f 100644
--- a/pgmpy/tests/test_base/test_DAG.py
+++ b/pgmpy/tests/test_base/test_DAG.py
@@ -460,6 +460,34 @@ class TestDAGCreation(unittest.TestCase):
                 TabularCPD("B", 2, [[2.0 / 3], [1.0 / 3]]),
             )
 
+    def test_dag_with_independent_node_fit(self):
+        edge_list = [("A", "C"), ("B", "C")]
+        dag = DAG(edge_list)
+        dag.add_node("D")
+        dbn = DiscreteBayesianNetwork(edge_list)
+        dbn.add_node("D")
+        for model in [dag, dbn]:
+            data = pd.DataFrame(
+                data={"A": [0, 0, 1], "B": [0, 1, 0], "C": [1, 1, 0], "D": [1, 1, 1]}
+            )
+            pseudo_counts = {
+                "A": [[9], [3]],
+                "B": [[9], [3]],
+                "C": [[9, 9, 9, 9], [3, 3, 3, 3]],
+                "D": [[9]],
+            }
+
+            fitted_model_bayesian = model.fit(
+                data,
+                estimator=BayesianEstimator,
+                prior_type="dirichlet",
+                pseudo_counts=pseudo_counts,
+            )
+            self.assertTrue(fitted_model_bayesian.check_model())
+            self.assertEqual(
+                sorted(fitted_model_bayesian.nodes()), ["A", "B", "C", "D"]
+            )
+
     def tearDown(self):
         del self.graph