We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`ZeroDivisionError` when plotting parameter importance
Plotting parameter importance of a multi-objective study causes a `ZeroDivisionError: Weights sum to zero, can't be normalized.` The error happens with a with two-objective study, which otherwise runs nicely and produces a Pareto plot without any problem.

## Expected behavior

Generate a plot of parameter importance based on a multi-objective study, as described in the documentation.

## Environment

- Optuna version: 2.6.0
- Python version: 3.8
- OS: Ubuntu 18.04

## Error messages, stack traces, or logs

```
ERROR Traceback (most recent call last):
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/visualization/_param_importances.py", line 126, in plot_param_importances
    importances = optuna.importance.get_param_importances(
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/__init__.py", line 91, in get_param_importances
    return evaluator.evaluate(study, params=params, target=target)
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_evaluator.py", line 121, in evaluate
    evaluator.fit(
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py", line 74, in fit
    self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py", line 74, in <listcomp>
    self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py", line 23, in __init__
    statistics = self._precompute_statistics()
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py", line 185, in _precompute_statistics
    value = numpy.average(child_values, weights=child_weights)
  File "<__array_function__ internals>", line 5, in average
  File "/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/numpy/lib/function_base.py", line 409, in average
    raise ZeroDivisionError(
ZeroDivisionError: Weights sum to zero, can't be normalized

---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-10-380c9d9887b3> in <module>
/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/... in create_param_importance_plot(self)
    168 
    169     def create_param_importance_plot(self):
--> 170         param_importance_fig = optuna.visualization.plot_param_importances(
    171             self.study, target_name=Q_METRIC_NAME, target=lambda t: t.values[0]
    172         )

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/visualization/_param_importances.py in plot_param_importances(study, evaluator, params, target, target_name)
    124         return go.Figure(data=[], layout=layout)
    125 
--> 126     importances = optuna.importance.get_param_importances(
    127         study, evaluator=evaluator, params=params, target=target
    128     )

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/__init__.py in get_param_importances(study, evaluator, params, target)
     89         raise TypeError("Evaluator must be a subclass of BaseImportanceEvaluator.")
     90 
---> 91     return evaluator.evaluate(study, params=params, target=target)

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_evaluator.py in evaluate(self, study, params, target)
    119 
    120         evaluator = self._evaluator
--> 121         evaluator.fit(
    122             X=trans_params,
    123             y=trans_values,

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py in fit(self, X, y, search_spaces, column_to_encoded_columns)
     72         self._forest.fit(X, y)
     73 
---> 74         self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]
     75         self._column_to_encoded_columns = column_to_encoded_columns
     76         self._variances = {}

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py in <listcomp>(.0)
     72         self._forest.fit(X, y)
     73 
---> 74         self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]
     75         self._column_to_encoded_columns = column_to_encoded_columns
     76         self._variances = {}

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py in __init__(self, tree, search_spaces)
     21         self._search_spaces = search_spaces
     22 
---> 23         statistics = self._precompute_statistics()
     24         split_midpoints, split_sizes = self._precompute_split_midpoints_and_sizes()
     25         subtree_active_features = self._precompute_subtree_active_features()

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py in _precompute_statistics(self)
    183                     child_values.append(statistics[child_node_index, 0])
    184                     child_weights.append(statistics[child_node_index, 1])
--> 185                 value = numpy.average(child_values, weights=child_weights)
    186                 weight = numpy.sum(child_weights)
    187                 statistics[node_index] = [value, weight]

<__array_function__ internals> in average(*args, **kwargs)

/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/numpy/lib/function_base.py in average(a, axis, weights, returned)
    407         scl = wgt.sum(axis=axis, dtype=result_dtype)
    408         if np.any(scl == 0.0):
--> 409             raise ZeroDivisionError(
    410                 "Weights sum to zero, can't be normalized")
    411 

ZeroDivisionError: Weights sum to zero, can't be normalized
```

## Steps to reproduce

1. Configure and run a default study with two objectives (maximize, maximize), the main function returning the result of a rounded float for one objective and an integer division for the second objective.
2. Generate a parameter importance plot with:
```python
fig = optuna.visualization.plot_param_importances(study, target_name=METRIC_NAME, target=lambda t: t.values[0]) 
```

## Additional context (optional)

Apart from the param importances not being generated, the study runs smoothly and I can generate a Pareto front plot without any problem.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `optuna/importance/_fanova/_evaluator.py`
Content:
```
1 from collections import OrderedDict
2 from typing import Callable
3 from typing import Dict
4 from typing import List
5 from typing import Optional
6 
7 import numpy
8 
9 from optuna._transform import _SearchSpaceTransform
10 from optuna.importance._base import _get_distributions
11 from optuna.importance._base import BaseImportanceEvaluator
12 from optuna.importance._fanova._fanova import _Fanova
13 from optuna.study import Study
14 from optuna.trial import FrozenTrial
15 from optuna.trial import TrialState
16 
17 
18 class FanovaImportanceEvaluator(BaseImportanceEvaluator):
19     """fANOVA importance evaluator.
20 
21     Implements the fANOVA hyperparameter importance evaluation algorithm in
22     `An Efficient Approach for Assessing Hyperparameter Importance
23     <http://proceedings.mlr.press/v32/hutter14.html>`_.
24 
25     Given a study, fANOVA fits a random forest regression model that predicts the objective value
26     given a parameter configuration. The more accurate this model is, the more reliable the
27     importances assessed by this class are.
28 
29     .. note::
30 
31         Requires the `sklearn <https://github.com/scikit-learn/scikit-learn>`_ Python package.
32 
33     .. note::
34 
35         Pairwise and higher order importances are not supported through this class. They can be
36         computed using :class:`~optuna.importance._fanova._fanova._Fanova` directly but is not
37         recommended as interfaces may change without prior notice.
38 
39     .. note::
40 
41         The performance of fANOVA depends on the prediction performance of the underlying
42         random forest model. In order to obtain high prediction performance, it is necessary to
43         cover a wide range of the hyperparameter search space. It is recommended to use an
44         exploration-oriented sampler such as :class:`~optuna.samplers.RandomSampler`.
45 
46     .. note::
47 
48         For how to cite the original work, please refer to
49         https://automl.github.io/fanova/cite.html.
50 
51     Args:
52         n_trees:
53             The number of trees in the forest.
54         max_depth:
55             The maximum depth of the trees in the forest.
56         seed:
57             Controls the randomness of the forest. For deterministic behavior, specify a value
58             other than :obj:`None`.
59 
60     """
61 
62     def __init__(
63         self, *, n_trees: int = 64, max_depth: int = 64, seed: Optional[int] = None
64     ) -> None:
65         self._evaluator = _Fanova(
66             n_trees=n_trees,
67             max_depth=max_depth,
68             min_samples_split=2,
69             min_samples_leaf=1,
70             seed=seed,
71         )
72 
73     def evaluate(
74         self,
75         study: Study,
76         params: Optional[List[str]] = None,
77         *,
78         target: Optional[Callable[[FrozenTrial], float]] = None,
79     ) -> Dict[str, float]:
80         if target is None and study._is_multi_objective():
81             raise ValueError(
82                 "If the `study` is being used for multi-objective optimization, "
83                 "please specify the `target`. For example, use "
84                 "`target=lambda t: t.values[0]` for the first objective value."
85             )
86 
87         distributions = _get_distributions(study, params)
88         if len(distributions) == 0:
89             return OrderedDict()
90 
91         trials = []
92         for trial in study.trials:
93             if trial.state != TrialState.COMPLETE:
94                 continue
95             if any(name not in trial.params for name in distributions.keys()):
96                 continue
97             trials.append(trial)
98 
99         trans = _SearchSpaceTransform(distributions, transform_log=False, transform_step=False)
100 
101         n_trials = len(trials)
102         trans_params = numpy.empty((n_trials, trans.bounds.shape[0]), dtype=numpy.float64)
103         trans_values = numpy.empty(n_trials, dtype=numpy.float64)
104 
105         for trial_idx, trial in enumerate(trials):
106             trans_params[trial_idx] = trans.transform(trial.params)
107             trans_values[trial_idx] = trial.value if target is None else target(trial)
108 
109         trans_bounds = trans.bounds
110         column_to_encoded_columns = trans.column_to_encoded_columns
111 
112         if trans_params.size == 0:  # `params` were given but as an empty list.
113             return OrderedDict()
114 
115         # Many (deep) copies of the search spaces are required during the tree traversal and using
116         # Optuna distributions will create a bottleneck.
117         # Therefore, search spaces (parameter distributions) are represented by a single
118         # `numpy.ndarray`, coupled with a list of flags that indicate whether they are categorical
119         # or not.
120 
121         evaluator = self._evaluator
122         evaluator.fit(
123             X=trans_params,
124             y=trans_values,
125             search_spaces=trans_bounds,
126             column_to_encoded_columns=column_to_encoded_columns,
127         )
128 
129         importances = {}
130         for i, name in enumerate(distributions.keys()):
131             importance, _ = evaluator.get_importance((i,))
132             importances[name] = importance
133 
134         total_importance = sum(importances.values())
135         for name in importances:
136             importances[name] /= total_importance
137 
138         sorted_importances = OrderedDict(
139             reversed(
140                 sorted(importances.items(), key=lambda name_and_importance: name_and_importance[1])
141             )
142         )
143         return sorted_importances
144
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/optuna/importance/_fanova/_evaluator.py b/optuna/importance/_fanova/_evaluator.py
--- a/optuna/importance/_fanova/_evaluator.py
+++ b/optuna/importance/_fanova/_evaluator.py
@@ -88,6 +88,12 @@
         if len(distributions) == 0:
             return OrderedDict()
 
+        # fANOVA does not support parameter distributions with a single value.
+        # However, there is no reason to calculate parameter importance in such case anyway,
+        # since it will always be 0 as the parameter is constant in the objective function.
+        zero_importances = {name: 0.0 for name, dist in distributions.items() if dist.single()}
+        distributions = {name: dist for name, dist in distributions.items() if not dist.single()}
+
         trials = []
         for trial in study.trials:
             if trial.state != TrialState.COMPLETE:
@@ -131,6 +137,7 @@
             importance, _ = evaluator.get_importance((i,))
             importances[name] = importance
 
+        importances = {**importances, **zero_importances}
         total_importance = sum(importances.values())
         for name in importances:
             importances[name] /= total_importance

{"golden_diff": "diff --git a/optuna/importance/_fanova/_evaluator.py b/optuna/importance/_fanova/_evaluator.py\n--- a/optuna/importance/_fanova/_evaluator.py\n+++ b/optuna/importance/_fanova/_evaluator.py\n@@ -88,6 +88,12 @@\n         if len(distributions) == 0:\n             return OrderedDict()\n \n+        # fANOVA does not support parameter distributions with a single value.\n+        # However, there is no reason to calculate parameter importance in such case anyway,\n+        # since it will always be 0 as the parameter is constant in the objective function.\n+        zero_importances = {name: 0.0 for name, dist in distributions.items() if dist.single()}\n+        distributions = {name: dist for name, dist in distributions.items() if not dist.single()}\n+\n         trials = []\n         for trial in study.trials:\n             if trial.state != TrialState.COMPLETE:\n@@ -131,6 +137,7 @@\n             importance, _ = evaluator.get_importance((i,))\n             importances[name] = importance\n \n+        importances = {**importances, **zero_importances}\n         total_importance = sum(importances.values())\n         for name in importances:\n             importances[name] /= total_importance\n", "issue": "`ZeroDivisionError` when plotting parameter importance\nPlotting parameter importance of a multi-objective study causes a `ZeroDivisionError: Weights sum to zero, can't be normalized.` The error happens with a with two-objective study, which otherwise runs nicely and produces a Pareto plot without any problem.\r\n\r\n## Expected behavior\r\n\r\nGenerate a plot of parameter importance based on a multi-objective study, as described in the documentation.\r\n\r\n## Environment\r\n\r\n- Optuna version: 2.6.0\r\n- Python version: 3.8\r\n- OS: Ubuntu 18.04\r\n\r\n## Error messages, stack traces, or logs\r\n\r\n```\r\nERROR Traceback (most recent call last):\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/visualization/_param_importances.py\", line 126, in plot_param_importances\r\n    importances = optuna.importance.get_param_importances(\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/__init__.py\", line 91, in get_param_importances\r\n    return evaluator.evaluate(study, params=params, target=target)\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_evaluator.py\", line 121, in evaluate\r\n    evaluator.fit(\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py\", line 74, in fit\r\n    self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py\", line 74, in <listcomp>\r\n    self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py\", line 23, in __init__\r\n    statistics = self._precompute_statistics()\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py\", line 185, in _precompute_statistics\r\n    value = numpy.average(child_values, weights=child_weights)\r\n  File \"<__array_function__ internals>\", line 5, in average\r\n  File \"/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/numpy/lib/function_base.py\", line 409, in average\r\n    raise ZeroDivisionError(\r\nZeroDivisionError: Weights sum to zero, can't be normalized\r\n\r\n---------------------------------------------------------------------------\r\nZeroDivisionError                         Traceback (most recent call last)\r\n<ipython-input-10-380c9d9887b3> in <module>\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/... in create_param_importance_plot(self)\r\n    168 \r\n    169     def create_param_importance_plot(self):\r\n--> 170         param_importance_fig = optuna.visualization.plot_param_importances(\r\n    171             self.study, target_name=Q_METRIC_NAME, target=lambda t: t.values[0]\r\n    172         )\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/visualization/_param_importances.py in plot_param_importances(study, evaluator, params, target, target_name)\r\n    124         return go.Figure(data=[], layout=layout)\r\n    125 \r\n--> 126     importances = optuna.importance.get_param_importances(\r\n    127         study, evaluator=evaluator, params=params, target=target\r\n    128     )\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/__init__.py in get_param_importances(study, evaluator, params, target)\r\n     89         raise TypeError(\"Evaluator must be a subclass of BaseImportanceEvaluator.\")\r\n     90 \r\n---> 91     return evaluator.evaluate(study, params=params, target=target)\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_evaluator.py in evaluate(self, study, params, target)\r\n    119 \r\n    120         evaluator = self._evaluator\r\n--> 121         evaluator.fit(\r\n    122             X=trans_params,\r\n    123             y=trans_values,\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py in fit(self, X, y, search_spaces, column_to_encoded_columns)\r\n     72         self._forest.fit(X, y)\r\n     73 \r\n---> 74         self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]\r\n     75         self._column_to_encoded_columns = column_to_encoded_columns\r\n     76         self._variances = {}\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_fanova.py in <listcomp>(.0)\r\n     72         self._forest.fit(X, y)\r\n     73 \r\n---> 74         self._trees = [_FanovaTree(e.tree_, search_spaces) for e in self._forest.estimators_]\r\n     75         self._column_to_encoded_columns = column_to_encoded_columns\r\n     76         self._variances = {}\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py in __init__(self, tree, search_spaces)\r\n     21         self._search_spaces = search_spaces\r\n     22 \r\n---> 23         statistics = self._precompute_statistics()\r\n     24         split_midpoints, split_sizes = self._precompute_split_midpoints_and_sizes()\r\n     25         subtree_active_features = self._precompute_subtree_active_features()\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/optuna/importance/_fanova/_tree.py in _precompute_statistics(self)\r\n    183                     child_values.append(statistics[child_node_index, 0])\r\n    184                     child_weights.append(statistics[child_node_index, 1])\r\n--> 185                 value = numpy.average(child_values, weights=child_weights)\r\n    186                 weight = numpy.sum(child_weights)\r\n    187                 statistics[node_index] = [value, weight]\r\n\r\n<__array_function__ internals> in average(*args, **kwargs)\r\n\r\n/opt/mt/anaconda3/envs/develop/lib/python3.8/site-packages/numpy/lib/function_base.py in average(a, axis, weights, returned)\r\n    407         scl = wgt.sum(axis=axis, dtype=result_dtype)\r\n    408         if np.any(scl == 0.0):\r\n--> 409             raise ZeroDivisionError(\r\n    410                 \"Weights sum to zero, can't be normalized\")\r\n    411 \r\n\r\nZeroDivisionError: Weights sum to zero, can't be normalized\r\n```\r\n\r\n## Steps to reproduce\r\n\r\n1. Configure and run a default study with two objectives (maximize, maximize), the main function returning the result of a rounded float for one objective and an integer division for the second objective.\r\n2. Generate a parameter importance plot with:\r\n```python\r\nfig = optuna.visualization.plot_param_importances(study, target_name=METRIC_NAME, target=lambda t: t.values[0]) \r\n```\r\n\r\n## Additional context (optional)\r\n\r\nApart from the param importances not being generated, the study runs smoothly and I can generate a Pareto front plot without any problem.\r\n\n", "before_files": [{"content": "from collections import OrderedDict\nfrom typing import Callable\nfrom typing import Dict\nfrom typing import List\nfrom typing import Optional\n\nimport numpy\n\nfrom optuna._transform import _SearchSpaceTransform\nfrom optuna.importance._base import _get_distributions\nfrom optuna.importance._base import BaseImportanceEvaluator\nfrom optuna.importance._fanova._fanova import _Fanova\nfrom optuna.study import Study\nfrom optuna.trial import FrozenTrial\nfrom optuna.trial import TrialState\n\n\nclass FanovaImportanceEvaluator(BaseImportanceEvaluator):\n    \"\"\"fANOVA importance evaluator.\n\n    Implements the fANOVA hyperparameter importance evaluation algorithm in\n    `An Efficient Approach for Assessing Hyperparameter Importance\n    <http://proceedings.mlr.press/v32/hutter14.html>`_.\n\n    Given a study, fANOVA fits a random forest regression model that predicts the objective value\n    given a parameter configuration. The more accurate this model is, the more reliable the\n    importances assessed by this class are.\n\n    .. note::\n\n        Requires the `sklearn <https://github.com/scikit-learn/scikit-learn>`_ Python package.\n\n    .. note::\n\n        Pairwise and higher order importances are not supported through this class. They can be\n        computed using :class:`~optuna.importance._fanova._fanova._Fanova` directly but is not\n        recommended as interfaces may change without prior notice.\n\n    .. note::\n\n        The performance of fANOVA depends on the prediction performance of the underlying\n        random forest model. In order to obtain high prediction performance, it is necessary to\n        cover a wide range of the hyperparameter search space. It is recommended to use an\n        exploration-oriented sampler such as :class:`~optuna.samplers.RandomSampler`.\n\n    .. note::\n\n        For how to cite the original work, please refer to\n        https://automl.github.io/fanova/cite.html.\n\n    Args:\n        n_trees:\n            The number of trees in the forest.\n        max_depth:\n            The maximum depth of the trees in the forest.\n        seed:\n            Controls the randomness of the forest. For deterministic behavior, specify a value\n            other than :obj:`None`.\n\n    \"\"\"\n\n    def __init__(\n        self, *, n_trees: int = 64, max_depth: int = 64, seed: Optional[int] = None\n    ) -> None:\n        self._evaluator = _Fanova(\n            n_trees=n_trees,\n            max_depth=max_depth,\n            min_samples_split=2,\n            min_samples_leaf=1,\n            seed=seed,\n        )\n\n    def evaluate(\n        self,\n        study: Study,\n        params: Optional[List[str]] = None,\n        *,\n        target: Optional[Callable[[FrozenTrial], float]] = None,\n    ) -> Dict[str, float]:\n        if target is None and study._is_multi_objective():\n            raise ValueError(\n                \"If the `study` is being used for multi-objective optimization, \"\n                \"please specify the `target`. For example, use \"\n                \"`target=lambda t: t.values[0]` for the first objective value.\"\n            )\n\n        distributions = _get_distributions(study, params)\n        if len(distributions) == 0:\n            return OrderedDict()\n\n        trials = []\n        for trial in study.trials:\n            if trial.state != TrialState.COMPLETE:\n                continue\n            if any(name not in trial.params for name in distributions.keys()):\n                continue\n            trials.append(trial)\n\n        trans = _SearchSpaceTransform(distributions, transform_log=False, transform_step=False)\n\n        n_trials = len(trials)\n        trans_params = numpy.empty((n_trials, trans.bounds.shape[0]), dtype=numpy.float64)\n        trans_values = numpy.empty(n_trials, dtype=numpy.float64)\n\n        for trial_idx, trial in enumerate(trials):\n            trans_params[trial_idx] = trans.transform(trial.params)\n            trans_values[trial_idx] = trial.value if target is None else target(trial)\n\n        trans_bounds = trans.bounds\n        column_to_encoded_columns = trans.column_to_encoded_columns\n\n        if trans_params.size == 0:  # `params` were given but as an empty list.\n            return OrderedDict()\n\n        # Many (deep) copies of the search spaces are required during the tree traversal and using\n        # Optuna distributions will create a bottleneck.\n        # Therefore, search spaces (parameter distributions) are represented by a single\n        # `numpy.ndarray`, coupled with a list of flags that indicate whether they are categorical\n        # or not.\n\n        evaluator = self._evaluator\n        evaluator.fit(\n            X=trans_params,\n            y=trans_values,\n            search_spaces=trans_bounds,\n            column_to_encoded_columns=column_to_encoded_columns,\n        )\n\n        importances = {}\n        for i, name in enumerate(distributions.keys()):\n            importance, _ = evaluator.get_importance((i,))\n            importances[name] = importance\n\n        total_importance = sum(importances.values())\n        for name in importances:\n            importances[name] /= total_importance\n\n        sorted_importances = OrderedDict(\n            reversed(\n                sorted(importances.items(), key=lambda name_and_importance: name_and_importance[1])\n            )\n        )\n        return sorted_importances\n", "path": "optuna/importance/_fanova/_evaluator.py"}], "after_files": [{"content": "from collections import OrderedDict\nfrom typing import Callable\nfrom typing import Dict\nfrom typing import List\nfrom typing import Optional\n\nimport numpy\n\nfrom optuna._transform import _SearchSpaceTransform\nfrom optuna.importance._base import _get_distributions\nfrom optuna.importance._base import BaseImportanceEvaluator\nfrom optuna.importance._fanova._fanova import _Fanova\nfrom optuna.study import Study\nfrom optuna.trial import FrozenTrial\nfrom optuna.trial import TrialState\n\n\nclass FanovaImportanceEvaluator(BaseImportanceEvaluator):\n    \"\"\"fANOVA importance evaluator.\n\n    Implements the fANOVA hyperparameter importance evaluation algorithm in\n    `An Efficient Approach for Assessing Hyperparameter Importance\n    <http://proceedings.mlr.press/v32/hutter14.html>`_.\n\n    Given a study, fANOVA fits a random forest regression model that predicts the objective value\n    given a parameter configuration. The more accurate this model is, the more reliable the\n    importances assessed by this class are.\n\n    .. note::\n\n        Requires the `sklearn <https://github.com/scikit-learn/scikit-learn>`_ Python package.\n\n    .. note::\n\n        Pairwise and higher order importances are not supported through this class. They can be\n        computed using :class:`~optuna.importance._fanova._fanova._Fanova` directly but is not\n        recommended as interfaces may change without prior notice.\n\n    .. note::\n\n        The performance of fANOVA depends on the prediction performance of the underlying\n        random forest model. In order to obtain high prediction performance, it is necessary to\n        cover a wide range of the hyperparameter search space. It is recommended to use an\n        exploration-oriented sampler such as :class:`~optuna.samplers.RandomSampler`.\n\n    .. note::\n\n        For how to cite the original work, please refer to\n        https://automl.github.io/fanova/cite.html.\n\n    Args:\n        n_trees:\n            The number of trees in the forest.\n        max_depth:\n            The maximum depth of the trees in the forest.\n        seed:\n            Controls the randomness of the forest. For deterministic behavior, specify a value\n            other than :obj:`None`.\n\n    \"\"\"\n\n    def __init__(\n        self, *, n_trees: int = 64, max_depth: int = 64, seed: Optional[int] = None\n    ) -> None:\n        self._evaluator = _Fanova(\n            n_trees=n_trees,\n            max_depth=max_depth,\n            min_samples_split=2,\n            min_samples_leaf=1,\n            seed=seed,\n        )\n\n    def evaluate(\n        self,\n        study: Study,\n        params: Optional[List[str]] = None,\n        *,\n        target: Optional[Callable[[FrozenTrial], float]] = None,\n    ) -> Dict[str, float]:\n        if target is None and study._is_multi_objective():\n            raise ValueError(\n                \"If the `study` is being used for multi-objective optimization, \"\n                \"please specify the `target`. For example, use \"\n                \"`target=lambda t: t.values[0]` for the first objective value.\"\n            )\n\n        distributions = _get_distributions(study, params)\n        if len(distributions) == 0:\n            return OrderedDict()\n\n        # fANOVA does not support parameter distributions with a single value.\n        # However, there is no reason to calculate parameter importance in such case anyway,\n        # since it will always be 0 as the parameter is constant in the objective function.\n        zero_importances = {name: 0.0 for name, dist in distributions.items() if dist.single()}\n        distributions = {name: dist for name, dist in distributions.items() if not dist.single()}\n\n        trials = []\n        for trial in study.trials:\n            if trial.state != TrialState.COMPLETE:\n                continue\n            if any(name not in trial.params for name in distributions.keys()):\n                continue\n            trials.append(trial)\n\n        trans = _SearchSpaceTransform(distributions, transform_log=False, transform_step=False)\n\n        n_trials = len(trials)\n        trans_params = numpy.empty((n_trials, trans.bounds.shape[0]), dtype=numpy.float64)\n        trans_values = numpy.empty(n_trials, dtype=numpy.float64)\n\n        for trial_idx, trial in enumerate(trials):\n            trans_params[trial_idx] = trans.transform(trial.params)\n            trans_values[trial_idx] = trial.value if target is None else target(trial)\n\n        trans_bounds = trans.bounds\n        column_to_encoded_columns = trans.column_to_encoded_columns\n\n        if trans_params.size == 0:  # `params` were given but as an empty list.\n            return OrderedDict()\n\n        # Many (deep) copies of the search spaces are required during the tree traversal and using\n        # Optuna distributions will create a bottleneck.\n        # Therefore, search spaces (parameter distributions) are represented by a single\n        # `numpy.ndarray`, coupled with a list of flags that indicate whether they are categorical\n        # or not.\n\n        evaluator = self._evaluator\n        evaluator.fit(\n            X=trans_params,\n            y=trans_values,\n            search_spaces=trans_bounds,\n            column_to_encoded_columns=column_to_encoded_columns,\n        )\n\n        importances = {}\n        for i, name in enumerate(distributions.keys()):\n            importance, _ = evaluator.get_importance((i,))\n            importances[name] = importance\n\n        importances = {**importances, **zero_importances}\n        total_importance = sum(importances.values())\n        for name in importances:\n            importances[name] /= total_importance\n\n        sorted_importances = OrderedDict(\n            reversed(\n                sorted(importances.items(), key=lambda name_and_importance: name_and_importance[1])\n            )\n        )\n        return sorted_importances\n", "path": "optuna/importance/_fanova/_evaluator.py"}]}