We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Make `column_importances` optional
If the column importance module gets automatically disabled (see #681) an issue may arise where the information required for a `ModelAnalysis` object is not available. Example stacktrace:

```python
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-10-b3f165881113> in <module>
     16 
     17 # Analyze the ensembles
---> 18 predictor.analyze_ensemble(enc_data)

/tmp/b380bd15a0ec89f57a82f719f514e67f0cae00fe7d0085d816353697296992059.py in analyze_ensemble(self, enc_data)
   1439         # --------------- #
   1440         log.info("Analyzing the ensemble of mixers")
-> 1441         self.model_analysis, self.runtime_analyzer = model_analyzer(
   1442             data=encoded_test_data,
   1443             train_data=encoded_train_data,

~/Documents/lightwood/lightwood/analysis/analyze.py in model_analyzer(predictor, data, train_data, stats_info, target, ts_cfg, dtype_dict, accuracy_functions, analysis_blocks)
     91         test_sample_size=len(encoded_val_data),
     92         confusion_matrix=runtime_analyzer['cm'],
---> 93         column_importances=runtime_analyzer['column_importances'],
     94         histograms=stats_info.histograms,
     95         dtypes=dtype_dict

KeyError: 'column_importances'
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lightwood/analysis/analyze.py`
Content:
```
1 from typing import Dict, List, Tuple, Optional
2 
3 from lightwood.helpers.log import log
4 from lightwood.api import dtype
5 from lightwood.ensemble import BaseEnsemble
6 from lightwood.analysis.base import BaseAnalysisBlock
7 from lightwood.data.encoded_ds import EncodedDs
8 from lightwood.encoder.text.pretrained import PretrainedLangEncoder
9 from lightwood.api.types import ModelAnalysis, StatisticalAnalysis, TimeseriesSettings, PredictionArguments
10 
11 
12 def model_analyzer(
13     predictor: BaseEnsemble,
14     data: EncodedDs,
15     train_data: EncodedDs,
16     stats_info: StatisticalAnalysis,
17     target: str,
18     ts_cfg: TimeseriesSettings,
19     dtype_dict: Dict[str, str],
20     accuracy_functions,
21     analysis_blocks: Optional[List[BaseAnalysisBlock]] = []
22 ) -> Tuple[ModelAnalysis, Dict[str, object]]:
23     """
24     Analyses model on a validation subset to evaluate accuracy, estimate feature importance and generate a
25     calibration model to estimating confidence in future predictions.
26 
27     Additionally, any user-specified analysis blocks (see class `BaseAnalysisBlock`) are also called here.
28 
29     :return:
30     runtime_analyzer: This dictionary object gets populated in a sequential fashion with data generated from
31     any `.analyze()` block call. This dictionary object is stored in the predictor itself, and used when
32     calling the `.explain()` method of all analysis blocks when generating predictions.
33 
34     model_analysis: `ModelAnalysis` object that contains core analysis metrics, not necessarily needed when predicting.
35     """
36 
37     runtime_analyzer = {}
38     data_type = dtype_dict[target]
39 
40     # retrieve encoded data representations
41     encoded_train_data = train_data
42     encoded_val_data = data
43     data = encoded_val_data.data_frame
44     input_cols = list([col for col in data.columns if col != target])
45 
46     # predictive task
47     is_numerical = data_type in (dtype.integer, dtype.float, dtype.array, dtype.tsarray, dtype.quantity)
48     is_classification = data_type in (dtype.categorical, dtype.binary)
49     is_multi_ts = ts_cfg.is_timeseries and ts_cfg.nr_predictions > 1
50     has_pretrained_text_enc = any([isinstance(enc, PretrainedLangEncoder)
51                                    for enc in encoded_train_data.encoders.values()])
52 
53     # raw predictions for validation dataset
54     args = {} if not is_classification else {"predict_proba": True}
55     normal_predictions = predictor(encoded_val_data, args=PredictionArguments.from_dict(args))
56     normal_predictions = normal_predictions.set_index(data.index)
57 
58     # ------------------------- #
59     # Run analysis blocks, both core and user-defined
60     # ------------------------- #
61     kwargs = {
62         'predictor': predictor,
63         'target': target,
64         'input_cols': input_cols,
65         'dtype_dict': dtype_dict,
66         'normal_predictions': normal_predictions,
67         'data': data,
68         'train_data': train_data,
69         'encoded_val_data': encoded_val_data,
70         'is_classification': is_classification,
71         'is_numerical': is_numerical,
72         'is_multi_ts': is_multi_ts,
73         'stats_info': stats_info,
74         'ts_cfg': ts_cfg,
75         'accuracy_functions': accuracy_functions,
76         'has_pretrained_text_enc': has_pretrained_text_enc
77     }
78 
79     for block in analysis_blocks:
80         log.info("The block %s is now running its analyze() method", block.__class__.__name__)
81         runtime_analyzer = block.analyze(runtime_analyzer, **kwargs)
82 
83     # ------------------------- #
84     # Populate ModelAnalysis object
85     # ------------------------- #
86     model_analysis = ModelAnalysis(
87         accuracies=runtime_analyzer['score_dict'],
88         accuracy_histogram=runtime_analyzer['acc_histogram'],
89         accuracy_samples=runtime_analyzer['acc_samples'],
90         train_sample_size=len(encoded_train_data),
91         test_sample_size=len(encoded_val_data),
92         confusion_matrix=runtime_analyzer['cm'],
93         column_importances=runtime_analyzer['column_importances'],
94         histograms=stats_info.histograms,
95         dtypes=dtype_dict
96     )
97 
98     return model_analysis, runtime_analyzer
99
```
--- 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/lightwood/analysis/analyze.py b/lightwood/analysis/analyze.py
--- a/lightwood/analysis/analyze.py
+++ b/lightwood/analysis/analyze.py
@@ -84,13 +84,13 @@
     # Populate ModelAnalysis object
     # ------------------------- #
     model_analysis = ModelAnalysis(
-        accuracies=runtime_analyzer['score_dict'],
-        accuracy_histogram=runtime_analyzer['acc_histogram'],
-        accuracy_samples=runtime_analyzer['acc_samples'],
+        accuracies=runtime_analyzer.get('score_dict', {}),
+        accuracy_histogram=runtime_analyzer.get('acc_histogram', {}),
+        accuracy_samples=runtime_analyzer.get('acc_samples', {}),
         train_sample_size=len(encoded_train_data),
         test_sample_size=len(encoded_val_data),
         confusion_matrix=runtime_analyzer['cm'],
-        column_importances=runtime_analyzer['column_importances'],
+        column_importances=runtime_analyzer.get('column_importances', {}),
         histograms=stats_info.histograms,
         dtypes=dtype_dict
     )

{"golden_diff": "diff --git a/lightwood/analysis/analyze.py b/lightwood/analysis/analyze.py\n--- a/lightwood/analysis/analyze.py\n+++ b/lightwood/analysis/analyze.py\n@@ -84,13 +84,13 @@\n     # Populate ModelAnalysis object\n     # ------------------------- #\n     model_analysis = ModelAnalysis(\n-        accuracies=runtime_analyzer['score_dict'],\n-        accuracy_histogram=runtime_analyzer['acc_histogram'],\n-        accuracy_samples=runtime_analyzer['acc_samples'],\n+        accuracies=runtime_analyzer.get('score_dict', {}),\n+        accuracy_histogram=runtime_analyzer.get('acc_histogram', {}),\n+        accuracy_samples=runtime_analyzer.get('acc_samples', {}),\n         train_sample_size=len(encoded_train_data),\n         test_sample_size=len(encoded_val_data),\n         confusion_matrix=runtime_analyzer['cm'],\n-        column_importances=runtime_analyzer['column_importances'],\n+        column_importances=runtime_analyzer.get('column_importances', {}),\n         histograms=stats_info.histograms,\n         dtypes=dtype_dict\n     )\n", "issue": "Make `column_importances` optional\nIf the column importance module gets automatically disabled (see #681) an issue may arise where the information required for a `ModelAnalysis` object is not available. Example stacktrace:\r\n\r\n```python\r\n---------------------------------------------------------------------------\r\nKeyError                                  Traceback (most recent call last)\r\n<ipython-input-10-b3f165881113> in <module>\r\n     16 \r\n     17 # Analyze the ensembles\r\n---> 18 predictor.analyze_ensemble(enc_data)\r\n\r\n/tmp/b380bd15a0ec89f57a82f719f514e67f0cae00fe7d0085d816353697296992059.py in analyze_ensemble(self, enc_data)\r\n   1439         # --------------- #\r\n   1440         log.info(\"Analyzing the ensemble of mixers\")\r\n-> 1441         self.model_analysis, self.runtime_analyzer = model_analyzer(\r\n   1442             data=encoded_test_data,\r\n   1443             train_data=encoded_train_data,\r\n\r\n~/Documents/lightwood/lightwood/analysis/analyze.py in model_analyzer(predictor, data, train_data, stats_info, target, ts_cfg, dtype_dict, accuracy_functions, analysis_blocks)\r\n     91         test_sample_size=len(encoded_val_data),\r\n     92         confusion_matrix=runtime_analyzer['cm'],\r\n---> 93         column_importances=runtime_analyzer['column_importances'],\r\n     94         histograms=stats_info.histograms,\r\n     95         dtypes=dtype_dict\r\n\r\nKeyError: 'column_importances'\r\n```\r\n\n", "before_files": [{"content": "from typing import Dict, List, Tuple, Optional\n\nfrom lightwood.helpers.log import log\nfrom lightwood.api import dtype\nfrom lightwood.ensemble import BaseEnsemble\nfrom lightwood.analysis.base import BaseAnalysisBlock\nfrom lightwood.data.encoded_ds import EncodedDs\nfrom lightwood.encoder.text.pretrained import PretrainedLangEncoder\nfrom lightwood.api.types import ModelAnalysis, StatisticalAnalysis, TimeseriesSettings, PredictionArguments\n\n\ndef model_analyzer(\n    predictor: BaseEnsemble,\n    data: EncodedDs,\n    train_data: EncodedDs,\n    stats_info: StatisticalAnalysis,\n    target: str,\n    ts_cfg: TimeseriesSettings,\n    dtype_dict: Dict[str, str],\n    accuracy_functions,\n    analysis_blocks: Optional[List[BaseAnalysisBlock]] = []\n) -> Tuple[ModelAnalysis, Dict[str, object]]:\n    \"\"\"\n    Analyses model on a validation subset to evaluate accuracy, estimate feature importance and generate a\n    calibration model to estimating confidence in future predictions.\n\n    Additionally, any user-specified analysis blocks (see class `BaseAnalysisBlock`) are also called here.\n\n    :return:\n    runtime_analyzer: This dictionary object gets populated in a sequential fashion with data generated from\n    any `.analyze()` block call. This dictionary object is stored in the predictor itself, and used when\n    calling the `.explain()` method of all analysis blocks when generating predictions.\n\n    model_analysis: `ModelAnalysis` object that contains core analysis metrics, not necessarily needed when predicting.\n    \"\"\"\n\n    runtime_analyzer = {}\n    data_type = dtype_dict[target]\n\n    # retrieve encoded data representations\n    encoded_train_data = train_data\n    encoded_val_data = data\n    data = encoded_val_data.data_frame\n    input_cols = list([col for col in data.columns if col != target])\n\n    # predictive task\n    is_numerical = data_type in (dtype.integer, dtype.float, dtype.array, dtype.tsarray, dtype.quantity)\n    is_classification = data_type in (dtype.categorical, dtype.binary)\n    is_multi_ts = ts_cfg.is_timeseries and ts_cfg.nr_predictions > 1\n    has_pretrained_text_enc = any([isinstance(enc, PretrainedLangEncoder)\n                                   for enc in encoded_train_data.encoders.values()])\n\n    # raw predictions for validation dataset\n    args = {} if not is_classification else {\"predict_proba\": True}\n    normal_predictions = predictor(encoded_val_data, args=PredictionArguments.from_dict(args))\n    normal_predictions = normal_predictions.set_index(data.index)\n\n    # ------------------------- #\n    # Run analysis blocks, both core and user-defined\n    # ------------------------- #\n    kwargs = {\n        'predictor': predictor,\n        'target': target,\n        'input_cols': input_cols,\n        'dtype_dict': dtype_dict,\n        'normal_predictions': normal_predictions,\n        'data': data,\n        'train_data': train_data,\n        'encoded_val_data': encoded_val_data,\n        'is_classification': is_classification,\n        'is_numerical': is_numerical,\n        'is_multi_ts': is_multi_ts,\n        'stats_info': stats_info,\n        'ts_cfg': ts_cfg,\n        'accuracy_functions': accuracy_functions,\n        'has_pretrained_text_enc': has_pretrained_text_enc\n    }\n\n    for block in analysis_blocks:\n        log.info(\"The block %s is now running its analyze() method\", block.__class__.__name__)\n        runtime_analyzer = block.analyze(runtime_analyzer, **kwargs)\n\n    # ------------------------- #\n    # Populate ModelAnalysis object\n    # ------------------------- #\n    model_analysis = ModelAnalysis(\n        accuracies=runtime_analyzer['score_dict'],\n        accuracy_histogram=runtime_analyzer['acc_histogram'],\n        accuracy_samples=runtime_analyzer['acc_samples'],\n        train_sample_size=len(encoded_train_data),\n        test_sample_size=len(encoded_val_data),\n        confusion_matrix=runtime_analyzer['cm'],\n        column_importances=runtime_analyzer['column_importances'],\n        histograms=stats_info.histograms,\n        dtypes=dtype_dict\n    )\n\n    return model_analysis, runtime_analyzer\n", "path": "lightwood/analysis/analyze.py"}], "after_files": [{"content": "from typing import Dict, List, Tuple, Optional\n\nfrom lightwood.helpers.log import log\nfrom lightwood.api import dtype\nfrom lightwood.ensemble import BaseEnsemble\nfrom lightwood.analysis.base import BaseAnalysisBlock\nfrom lightwood.data.encoded_ds import EncodedDs\nfrom lightwood.encoder.text.pretrained import PretrainedLangEncoder\nfrom lightwood.api.types import ModelAnalysis, StatisticalAnalysis, TimeseriesSettings, PredictionArguments\n\n\ndef model_analyzer(\n    predictor: BaseEnsemble,\n    data: EncodedDs,\n    train_data: EncodedDs,\n    stats_info: StatisticalAnalysis,\n    target: str,\n    ts_cfg: TimeseriesSettings,\n    dtype_dict: Dict[str, str],\n    accuracy_functions,\n    analysis_blocks: Optional[List[BaseAnalysisBlock]] = []\n) -> Tuple[ModelAnalysis, Dict[str, object]]:\n    \"\"\"\n    Analyses model on a validation subset to evaluate accuracy, estimate feature importance and generate a\n    calibration model to estimating confidence in future predictions.\n\n    Additionally, any user-specified analysis blocks (see class `BaseAnalysisBlock`) are also called here.\n\n    :return:\n    runtime_analyzer: This dictionary object gets populated in a sequential fashion with data generated from\n    any `.analyze()` block call. This dictionary object is stored in the predictor itself, and used when\n    calling the `.explain()` method of all analysis blocks when generating predictions.\n\n    model_analysis: `ModelAnalysis` object that contains core analysis metrics, not necessarily needed when predicting.\n    \"\"\"\n\n    runtime_analyzer = {}\n    data_type = dtype_dict[target]\n\n    # retrieve encoded data representations\n    encoded_train_data = train_data\n    encoded_val_data = data\n    data = encoded_val_data.data_frame\n    input_cols = list([col for col in data.columns if col != target])\n\n    # predictive task\n    is_numerical = data_type in (dtype.integer, dtype.float, dtype.array, dtype.tsarray, dtype.quantity)\n    is_classification = data_type in (dtype.categorical, dtype.binary)\n    is_multi_ts = ts_cfg.is_timeseries and ts_cfg.nr_predictions > 1\n    has_pretrained_text_enc = any([isinstance(enc, PretrainedLangEncoder)\n                                   for enc in encoded_train_data.encoders.values()])\n\n    # raw predictions for validation dataset\n    args = {} if not is_classification else {\"predict_proba\": True}\n    normal_predictions = predictor(encoded_val_data, args=PredictionArguments.from_dict(args))\n    normal_predictions = normal_predictions.set_index(data.index)\n\n    # ------------------------- #\n    # Run analysis blocks, both core and user-defined\n    # ------------------------- #\n    kwargs = {\n        'predictor': predictor,\n        'target': target,\n        'input_cols': input_cols,\n        'dtype_dict': dtype_dict,\n        'normal_predictions': normal_predictions,\n        'data': data,\n        'train_data': train_data,\n        'encoded_val_data': encoded_val_data,\n        'is_classification': is_classification,\n        'is_numerical': is_numerical,\n        'is_multi_ts': is_multi_ts,\n        'stats_info': stats_info,\n        'ts_cfg': ts_cfg,\n        'accuracy_functions': accuracy_functions,\n        'has_pretrained_text_enc': has_pretrained_text_enc\n    }\n\n    for block in analysis_blocks:\n        log.info(\"The block %s is now running its analyze() method\", block.__class__.__name__)\n        runtime_analyzer = block.analyze(runtime_analyzer, **kwargs)\n\n    # ------------------------- #\n    # Populate ModelAnalysis object\n    # ------------------------- #\n    model_analysis = ModelAnalysis(\n        accuracies=runtime_analyzer.get('score_dict', {}),\n        accuracy_histogram=runtime_analyzer.get('acc_histogram', {}),\n        accuracy_samples=runtime_analyzer.get('acc_samples', {}),\n        train_sample_size=len(encoded_train_data),\n        test_sample_size=len(encoded_val_data),\n        confusion_matrix=runtime_analyzer['cm'],\n        column_importances=runtime_analyzer.get('column_importances', {}),\n        histograms=stats_info.histograms,\n        dtypes=dtype_dict\n    )\n\n    return model_analysis, runtime_analyzer\n", "path": "lightwood/analysis/analyze.py"}]}