We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Drift titles are not clearly connected to their plots
![image](https://user-images.githubusercontent.com/17730502/156195682-d5949e14-b152-41c8-b16c-222c4ff071ea.png)

This example (taken from https://docs.deepchecks.com/en/latest/examples/vision/guides/quickstart_in_5_minutes.html) shows that it is very hard to understand that the "area" title is the property that is drifted. This happens in the common code for drift plots. Need to find a clearer way to indicate the drifted feature, either:
1. Find a way to center and enlarge the title / add bold / underscore (while still having the "Additional output" title be superior to it)
2. Add the feature name also to the title of the plots themselves. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `deepchecks/utils/distribution/drift.py`
Content:
```
1 # ----------------------------------------------------------------------------
2 # Copyright (C) 2021-2022 Deepchecks (https://www.deepchecks.com)
3 #
4 # This file is part of Deepchecks.
5 # Deepchecks is distributed under the terms of the GNU Affero General
6 # Public License (version 3 or later).
7 # You should have received a copy of the GNU Affero General Public License
8 # along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.
9 # ----------------------------------------------------------------------------
10 #
11 """Common utilities for distribution checks."""
12 
13 from typing import Tuple, Union, Hashable, Callable
14 
15 from scipy.stats import wasserstein_distance
16 import numpy as np
17 import pandas as pd
18 
19 import plotly.graph_objects as go
20 from plotly.subplots import make_subplots
21 
22 from deepchecks.utils.distribution.plot import drift_score_bar_traces, feature_distribution_traces
23 from deepchecks.utils.distribution.preprocessing import preprocess_2_cat_cols_to_same_bins
24 from deepchecks.core.errors import DeepchecksValueError
25 
26 
27 PSI_MIN_PERCENTAGE = 0.01
28 
29 
30 __all__ = ['calc_drift_and_plot']
31 
32 
33 def psi(expected_percents: np.ndarray, actual_percents: np.ndarray):
34     """
35     Calculate the PSI (Population Stability Index).
36 
37     See https://www.lexjansen.com/wuss/2017/47_Final_Paper_PDF.pdf
38 
39     Parameters
40     ----------
41     expected_percents: np.ndarray
42         array of percentages of each value in the expected distribution.
43     actual_percents: : np.ndarray
44         array of percentages of each value in the actual distribution.
45     Returns
46     -------
47     psi
48         The PSI score
49 
50     """
51     psi_value = 0
52     for i in range(len(expected_percents)):
53         # In order for the value not to diverge, we cap our min percentage value
54         e_perc = max(expected_percents[i], PSI_MIN_PERCENTAGE)
55         a_perc = max(actual_percents[i], PSI_MIN_PERCENTAGE)
56         value = (e_perc - a_perc) * np.log(e_perc / a_perc)
57         psi_value += value
58 
59     return psi_value
60 
61 
62 def earth_movers_distance(dist1: Union[np.ndarray, pd.Series], dist2: Union[np.ndarray, pd.Series]):
63     """
64     Calculate the Earth Movers Distance (Wasserstein distance).
65 
66     See https://en.wikipedia.org/wiki/Wasserstein_metric
67 
68     Function is for numerical data only.
69 
70     Parameters
71     ----------
72     dist1 : Union[np.ndarray, pd.Series]
73         array of numberical values.
74     dist2 : Union[np.ndarray, pd.Series]
75         array of numberical values to compare dist1 to.
76     Returns
77     -------
78     Any
79         the Wasserstein distance between the two distributions.
80 
81     """
82     unique1 = np.unique(dist1)
83     unique2 = np.unique(dist2)
84 
85     sample_space = list(set(unique1).union(set(unique2)))
86 
87     val_max = max(sample_space)
88     val_min = min(sample_space)
89 
90     if val_max == val_min:
91         return 0
92 
93     dist1 = (dist1 - val_min) / (val_max - val_min)
94     dist2 = (dist2 - val_min) / (val_max - val_min)
95 
96     return wasserstein_distance(dist1, dist2)
97 
98 
99 def calc_drift_and_plot(train_column: pd.Series, test_column: pd.Series, plot_title: Hashable,
100                         column_type: str, max_num_categories: int = 10) -> Tuple[float, str, Callable]:
101     """
102     Calculate drift score per column.
103 
104     Parameters
105     ----------
106     train_column : pd.Series
107         column from train dataset
108     test_column : pd.Series
109         same column from test dataset
110     plot_title : Hashable
111         title of plot
112     column_type : str
113         type of column (either "numerical" or "categorical")
114     max_num_categories : int , default: 10
115         Max number of allowed categories. If there are more, they are binned into an "Other" category.
116     Returns
117     -------
118     Tuple[float, str, Callable]
119         drift score of the difference between the two columns' distributions (Earth movers distance for
120         numerical, PSI for categorical)
121         graph comparing the two distributions (density for numerical, stack bar for categorical)
122     """
123     train_dist = train_column.dropna().values.reshape(-1)
124     test_dist = test_column.dropna().values.reshape(-1)
125 
126     if column_type == 'numerical':
127         scorer_name = "Earth Mover's Distance"
128 
129         train_dist = train_dist.astype('float')
130         test_dist = test_dist.astype('float')
131 
132         score = earth_movers_distance(dist1=train_dist, dist2=test_dist)
133 
134         bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score)
135         dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist)
136 
137     elif column_type == 'categorical':
138         scorer_name = 'PSI'
139         expected_percents, actual_percents, _ = \
140             preprocess_2_cat_cols_to_same_bins(dist1=train_dist, dist2=test_dist, max_num_categories=max_num_categories)
141         score = psi(expected_percents=expected_percents, actual_percents=actual_percents)
142 
143         bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score, bar_max=1)
144         dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist, is_categorical=True,
145                                                                             max_num_categories=max_num_categories)
146     else:
147         # Should never reach here
148         raise DeepchecksValueError(f'Unsupported column type for drift: {column_type}')
149 
150     fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2, shared_yaxes=False, shared_xaxes=False,
151                         row_heights=[0.1, 0.9],
152                         subplot_titles=['Drift Score - ' + scorer_name, 'Distribution Plot'])
153 
154     fig.add_traces(bar_traces, rows=[1] * len(bar_traces), cols=[1] * len(bar_traces))
155     fig.add_traces(dist_traces, rows=[2] * len(dist_traces), cols=[1] * len(dist_traces))
156 
157     shared_layout = go.Layout(
158         xaxis=bar_x_axis,
159         yaxis=bar_y_axis,
160         xaxis2=dist_x_axis,
161         yaxis2=dist_y_axis,
162         legend=dict(
163             title='Dataset',
164             yanchor='top',
165             y=0.6),
166         width=700,
167         height=400,
168         title=plot_title
169     )
170 
171     fig.update_layout(shared_layout)
172 
173     return score, scorer_name, fig
174
```
--- 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/deepchecks/utils/distribution/drift.py b/deepchecks/utils/distribution/drift.py
--- a/deepchecks/utils/distribution/drift.py
+++ b/deepchecks/utils/distribution/drift.py
@@ -149,7 +149,7 @@
 
     fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2, shared_yaxes=False, shared_xaxes=False,
                         row_heights=[0.1, 0.9],
-                        subplot_titles=['Drift Score - ' + scorer_name, 'Distribution Plot'])
+                        subplot_titles=[f'Drift Score ({scorer_name})', 'Distribution Plot'])
 
     fig.add_traces(bar_traces, rows=[1] * len(bar_traces), cols=[1] * len(bar_traces))
     fig.add_traces(dist_traces, rows=[2] * len(dist_traces), cols=[1] * len(dist_traces))
@@ -165,7 +165,7 @@
             y=0.6),
         width=700,
         height=400,
-        title=plot_title
+        title=dict(text=plot_title, x=0.5, xanchor='center')
     )
 
     fig.update_layout(shared_layout)

{"golden_diff": "diff --git a/deepchecks/utils/distribution/drift.py b/deepchecks/utils/distribution/drift.py\n--- a/deepchecks/utils/distribution/drift.py\n+++ b/deepchecks/utils/distribution/drift.py\n@@ -149,7 +149,7 @@\n \n     fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2, shared_yaxes=False, shared_xaxes=False,\n                         row_heights=[0.1, 0.9],\n-                        subplot_titles=['Drift Score - ' + scorer_name, 'Distribution Plot'])\n+                        subplot_titles=[f'Drift Score ({scorer_name})', 'Distribution Plot'])\n \n     fig.add_traces(bar_traces, rows=[1] * len(bar_traces), cols=[1] * len(bar_traces))\n     fig.add_traces(dist_traces, rows=[2] * len(dist_traces), cols=[1] * len(dist_traces))\n@@ -165,7 +165,7 @@\n             y=0.6),\n         width=700,\n         height=400,\n-        title=plot_title\n+        title=dict(text=plot_title, x=0.5, xanchor='center')\n     )\n \n     fig.update_layout(shared_layout)\n", "issue": "[BUG] Drift titles are not clearly connected to their plots\n![image](https://user-images.githubusercontent.com/17730502/156195682-d5949e14-b152-41c8-b16c-222c4ff071ea.png)\r\n\r\nThis example (taken from https://docs.deepchecks.com/en/latest/examples/vision/guides/quickstart_in_5_minutes.html) shows that it is very hard to understand that the \"area\" title is the property that is drifted. This happens in the common code for drift plots. Need to find a clearer way to indicate the drifted feature, either:\r\n1. Find a way to center and enlarge the title / add bold / underscore (while still having the \"Additional output\" title be superior to it)\r\n2. Add the feature name also to the title of the plots themselves. \n", "before_files": [{"content": "# ----------------------------------------------------------------------------\n# Copyright (C) 2021-2022 Deepchecks (https://www.deepchecks.com)\n#\n# This file is part of Deepchecks.\n# Deepchecks is distributed under the terms of the GNU Affero General\n# Public License (version 3 or later).\n# You should have received a copy of the GNU Affero General Public License\n# along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.\n# ----------------------------------------------------------------------------\n#\n\"\"\"Common utilities for distribution checks.\"\"\"\n\nfrom typing import Tuple, Union, Hashable, Callable\n\nfrom scipy.stats import wasserstein_distance\nimport numpy as np\nimport pandas as pd\n\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\nfrom deepchecks.utils.distribution.plot import drift_score_bar_traces, feature_distribution_traces\nfrom deepchecks.utils.distribution.preprocessing import preprocess_2_cat_cols_to_same_bins\nfrom deepchecks.core.errors import DeepchecksValueError\n\n\nPSI_MIN_PERCENTAGE = 0.01\n\n\n__all__ = ['calc_drift_and_plot']\n\n\ndef psi(expected_percents: np.ndarray, actual_percents: np.ndarray):\n    \"\"\"\n    Calculate the PSI (Population Stability Index).\n\n    See https://www.lexjansen.com/wuss/2017/47_Final_Paper_PDF.pdf\n\n    Parameters\n    ----------\n    expected_percents: np.ndarray\n        array of percentages of each value in the expected distribution.\n    actual_percents: : np.ndarray\n        array of percentages of each value in the actual distribution.\n    Returns\n    -------\n    psi\n        The PSI score\n\n    \"\"\"\n    psi_value = 0\n    for i in range(len(expected_percents)):\n        # In order for the value not to diverge, we cap our min percentage value\n        e_perc = max(expected_percents[i], PSI_MIN_PERCENTAGE)\n        a_perc = max(actual_percents[i], PSI_MIN_PERCENTAGE)\n        value = (e_perc - a_perc) * np.log(e_perc / a_perc)\n        psi_value += value\n\n    return psi_value\n\n\ndef earth_movers_distance(dist1: Union[np.ndarray, pd.Series], dist2: Union[np.ndarray, pd.Series]):\n    \"\"\"\n    Calculate the Earth Movers Distance (Wasserstein distance).\n\n    See https://en.wikipedia.org/wiki/Wasserstein_metric\n\n    Function is for numerical data only.\n\n    Parameters\n    ----------\n    dist1 : Union[np.ndarray, pd.Series]\n        array of numberical values.\n    dist2 : Union[np.ndarray, pd.Series]\n        array of numberical values to compare dist1 to.\n    Returns\n    -------\n    Any\n        the Wasserstein distance between the two distributions.\n\n    \"\"\"\n    unique1 = np.unique(dist1)\n    unique2 = np.unique(dist2)\n\n    sample_space = list(set(unique1).union(set(unique2)))\n\n    val_max = max(sample_space)\n    val_min = min(sample_space)\n\n    if val_max == val_min:\n        return 0\n\n    dist1 = (dist1 - val_min) / (val_max - val_min)\n    dist2 = (dist2 - val_min) / (val_max - val_min)\n\n    return wasserstein_distance(dist1, dist2)\n\n\ndef calc_drift_and_plot(train_column: pd.Series, test_column: pd.Series, plot_title: Hashable,\n                        column_type: str, max_num_categories: int = 10) -> Tuple[float, str, Callable]:\n    \"\"\"\n    Calculate drift score per column.\n\n    Parameters\n    ----------\n    train_column : pd.Series\n        column from train dataset\n    test_column : pd.Series\n        same column from test dataset\n    plot_title : Hashable\n        title of plot\n    column_type : str\n        type of column (either \"numerical\" or \"categorical\")\n    max_num_categories : int , default: 10\n        Max number of allowed categories. If there are more, they are binned into an \"Other\" category.\n    Returns\n    -------\n    Tuple[float, str, Callable]\n        drift score of the difference between the two columns' distributions (Earth movers distance for\n        numerical, PSI for categorical)\n        graph comparing the two distributions (density for numerical, stack bar for categorical)\n    \"\"\"\n    train_dist = train_column.dropna().values.reshape(-1)\n    test_dist = test_column.dropna().values.reshape(-1)\n\n    if column_type == 'numerical':\n        scorer_name = \"Earth Mover's Distance\"\n\n        train_dist = train_dist.astype('float')\n        test_dist = test_dist.astype('float')\n\n        score = earth_movers_distance(dist1=train_dist, dist2=test_dist)\n\n        bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score)\n        dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist)\n\n    elif column_type == 'categorical':\n        scorer_name = 'PSI'\n        expected_percents, actual_percents, _ = \\\n            preprocess_2_cat_cols_to_same_bins(dist1=train_dist, dist2=test_dist, max_num_categories=max_num_categories)\n        score = psi(expected_percents=expected_percents, actual_percents=actual_percents)\n\n        bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score, bar_max=1)\n        dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist, is_categorical=True,\n                                                                            max_num_categories=max_num_categories)\n    else:\n        # Should never reach here\n        raise DeepchecksValueError(f'Unsupported column type for drift: {column_type}')\n\n    fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2, shared_yaxes=False, shared_xaxes=False,\n                        row_heights=[0.1, 0.9],\n                        subplot_titles=['Drift Score - ' + scorer_name, 'Distribution Plot'])\n\n    fig.add_traces(bar_traces, rows=[1] * len(bar_traces), cols=[1] * len(bar_traces))\n    fig.add_traces(dist_traces, rows=[2] * len(dist_traces), cols=[1] * len(dist_traces))\n\n    shared_layout = go.Layout(\n        xaxis=bar_x_axis,\n        yaxis=bar_y_axis,\n        xaxis2=dist_x_axis,\n        yaxis2=dist_y_axis,\n        legend=dict(\n            title='Dataset',\n            yanchor='top',\n            y=0.6),\n        width=700,\n        height=400,\n        title=plot_title\n    )\n\n    fig.update_layout(shared_layout)\n\n    return score, scorer_name, fig\n", "path": "deepchecks/utils/distribution/drift.py"}], "after_files": [{"content": "# ----------------------------------------------------------------------------\n# Copyright (C) 2021-2022 Deepchecks (https://www.deepchecks.com)\n#\n# This file is part of Deepchecks.\n# Deepchecks is distributed under the terms of the GNU Affero General\n# Public License (version 3 or later).\n# You should have received a copy of the GNU Affero General Public License\n# along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.\n# ----------------------------------------------------------------------------\n#\n\"\"\"Common utilities for distribution checks.\"\"\"\n\nfrom typing import Tuple, Union, Hashable, Callable\n\nfrom scipy.stats import wasserstein_distance\nimport numpy as np\nimport pandas as pd\n\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\nfrom deepchecks.utils.distribution.plot import drift_score_bar_traces, feature_distribution_traces\nfrom deepchecks.utils.distribution.preprocessing import preprocess_2_cat_cols_to_same_bins\nfrom deepchecks.core.errors import DeepchecksValueError\n\n\nPSI_MIN_PERCENTAGE = 0.01\n\n\n__all__ = ['calc_drift_and_plot']\n\n\ndef psi(expected_percents: np.ndarray, actual_percents: np.ndarray):\n    \"\"\"\n    Calculate the PSI (Population Stability Index).\n\n    See https://www.lexjansen.com/wuss/2017/47_Final_Paper_PDF.pdf\n\n    Parameters\n    ----------\n    expected_percents: np.ndarray\n        array of percentages of each value in the expected distribution.\n    actual_percents: : np.ndarray\n        array of percentages of each value in the actual distribution.\n    Returns\n    -------\n    psi\n        The PSI score\n\n    \"\"\"\n    psi_value = 0\n    for i in range(len(expected_percents)):\n        # In order for the value not to diverge, we cap our min percentage value\n        e_perc = max(expected_percents[i], PSI_MIN_PERCENTAGE)\n        a_perc = max(actual_percents[i], PSI_MIN_PERCENTAGE)\n        value = (e_perc - a_perc) * np.log(e_perc / a_perc)\n        psi_value += value\n\n    return psi_value\n\n\ndef earth_movers_distance(dist1: Union[np.ndarray, pd.Series], dist2: Union[np.ndarray, pd.Series]):\n    \"\"\"\n    Calculate the Earth Movers Distance (Wasserstein distance).\n\n    See https://en.wikipedia.org/wiki/Wasserstein_metric\n\n    Function is for numerical data only.\n\n    Parameters\n    ----------\n    dist1 : Union[np.ndarray, pd.Series]\n        array of numberical values.\n    dist2 : Union[np.ndarray, pd.Series]\n        array of numberical values to compare dist1 to.\n    Returns\n    -------\n    Any\n        the Wasserstein distance between the two distributions.\n\n    \"\"\"\n    unique1 = np.unique(dist1)\n    unique2 = np.unique(dist2)\n\n    sample_space = list(set(unique1).union(set(unique2)))\n\n    val_max = max(sample_space)\n    val_min = min(sample_space)\n\n    if val_max == val_min:\n        return 0\n\n    dist1 = (dist1 - val_min) / (val_max - val_min)\n    dist2 = (dist2 - val_min) / (val_max - val_min)\n\n    return wasserstein_distance(dist1, dist2)\n\n\ndef calc_drift_and_plot(train_column: pd.Series, test_column: pd.Series, plot_title: Hashable,\n                        column_type: str, max_num_categories: int = 10) -> Tuple[float, str, Callable]:\n    \"\"\"\n    Calculate drift score per column.\n\n    Parameters\n    ----------\n    train_column : pd.Series\n        column from train dataset\n    test_column : pd.Series\n        same column from test dataset\n    plot_title : Hashable\n        title of plot\n    column_type : str\n        type of column (either \"numerical\" or \"categorical\")\n    max_num_categories : int , default: 10\n        Max number of allowed categories. If there are more, they are binned into an \"Other\" category.\n    Returns\n    -------\n    Tuple[float, str, Callable]\n        drift score of the difference between the two columns' distributions (Earth movers distance for\n        numerical, PSI for categorical)\n        graph comparing the two distributions (density for numerical, stack bar for categorical)\n    \"\"\"\n    train_dist = train_column.dropna().values.reshape(-1)\n    test_dist = test_column.dropna().values.reshape(-1)\n\n    if column_type == 'numerical':\n        scorer_name = \"Earth Mover's Distance\"\n\n        train_dist = train_dist.astype('float')\n        test_dist = test_dist.astype('float')\n\n        score = earth_movers_distance(dist1=train_dist, dist2=test_dist)\n\n        bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score)\n        dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist)\n\n    elif column_type == 'categorical':\n        scorer_name = 'PSI'\n        expected_percents, actual_percents, _ = \\\n            preprocess_2_cat_cols_to_same_bins(dist1=train_dist, dist2=test_dist, max_num_categories=max_num_categories)\n        score = psi(expected_percents=expected_percents, actual_percents=actual_percents)\n\n        bar_traces, bar_x_axis, bar_y_axis = drift_score_bar_traces(score, bar_max=1)\n        dist_traces, dist_x_axis, dist_y_axis = feature_distribution_traces(train_dist, test_dist, is_categorical=True,\n                                                                            max_num_categories=max_num_categories)\n    else:\n        # Should never reach here\n        raise DeepchecksValueError(f'Unsupported column type for drift: {column_type}')\n\n    fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2, shared_yaxes=False, shared_xaxes=False,\n                        row_heights=[0.1, 0.9],\n                        subplot_titles=[f'Drift Score ({scorer_name})', 'Distribution Plot'])\n\n    fig.add_traces(bar_traces, rows=[1] * len(bar_traces), cols=[1] * len(bar_traces))\n    fig.add_traces(dist_traces, rows=[2] * len(dist_traces), cols=[1] * len(dist_traces))\n\n    shared_layout = go.Layout(\n        xaxis=bar_x_axis,\n        yaxis=bar_y_axis,\n        xaxis2=dist_x_axis,\n        yaxis2=dist_y_axis,\n        legend=dict(\n            title='Dataset',\n            yanchor='top',\n            y=0.6),\n        width=700,\n        height=400,\n        title=dict(text=plot_title, x=0.5, xanchor='center')\n    )\n\n    fig.update_layout(shared_layout)\n\n    return score, scorer_name, fig\n", "path": "deepchecks/utils/distribution/drift.py"}]}