Remove unnecessary nested dict
State: closed | Author: mariamabarham (CONTRIBUTOR)

This PR is removing unnecessary nested dictionary used in some datasets. For now the following datasets are updated:

- MLQA

- RACE

Will be adding more if necessary.

#378 

Comments: We can probably scan the dataset scripts with a regexpr to try to identify this pattern cc @patrickvonplaten maybe

@mariamabarham This script should work. I tested it for a couple of datasets. There might be exceptions where the script breaks - did not test everything.

```python
#!/usr/bin/env python3

from nlp import prepare_module, DownloadConfig, import_main_class, hf_api
import tempfile


def scan_for_nested_unnecessary_dict(dataset_name):

    def load_builder_class(dataset_name):
        module_path = prepare_module(dataset_name, download_config=DownloadConfig(force_download=True))
        return import_main_class(module_path)

    def load_configs(dataset_name):
        builder_cls = load_builder_class(dataset_name)
        if len(builder_cls.BUILDER_CONFIGS) == 0:
            return [None]
        return builder_cls.BUILDER_CONFIGS

    def scan_features_for_nested_dict(features):
        is_sequence = False
        if hasattr(features, "_type"):
            if features._type != 'Sequence':
                return False
            else:
                is_sequence = True
                features = features.feature

        if isinstance(features, list):
            for value in features:
                if scan_features_for_nested_dict(value):
                    return True
            return False

        elif isinstance(features, dict):
            for key, value in features.items():
                if is_sequence and len(features.keys()) == 1 and hasattr(features[key], "_type") and features[key]._type != "Sequence":
                    return True
                if scan_features_for_nested_dict(value):
                    return True
            return False
        elif hasattr(features, "_type"):
            return False
        else:
            raise ValueError(f"{features} should be either a list, a dict or a feature")

    configs = load_configs(dataset_name)

    for config in configs:
        with tempfile.TemporaryDirectory() as processed_temp_dir:
            # create config and dataset
            dataset_builder_cls = load_builder_class(dataset_name)
            name = config.name if config is not None else None
            dataset_builder = dataset_builder_cls(name=name, cache_dir=processed_temp_dir)

        is_nested_dict_in_dataset = scan_features_for_nested_dict(dataset_builder._info().features)
        if is_nested_dict_in_dataset:
            print(f"{dataset_name} with {name} needs refactoring")


if __name__ == "__main__":
    scan_for_nested_unnecessary_dict("race")  # prints True
    scan_for_nested_unnecessary_dict("mlqa")  # prints True
    scan_for_nested_unnecessary_dict("squad")  # prints Nothing

    # ran the following lines for 1min and seems to work -> didn't check for all datasets though
#    api = hf_api.HfApi()
#    all_datasets = [x.id for x in api.dataset_list(with_community_datasets=False)]
#    for dataset in all_datasets:
#        scan_for_nested_unnecessary_dict(dataset)
```

> @mariamabarham This script should work. I tested it for a couple of datasets. There might be exceptions where the script breaks - did not test everything.
> 
> ```python
> #!/usr/bin/env python3
> 
> from nlp import prepare_module, DownloadConfig, import_main_class, hf_api
> import tempfile
> 
> 
> def scan_for_nested_unnecessary_dict(dataset_name):
> 
>     def load_builder_class(dataset_name):
>         module_path = prepare_module(dataset_name, download_config=DownloadConfig(force_download=True))
>         return import_main_class(module_path)
> 
>     def load_configs(dataset_name):
>         builder_cls = load_builder_class(dataset_name)
>         if len(builder_cls.BUILDER_CONFIGS) == 0:
>             return [None]
>         return builder_cls.BUILDER_CONFIGS
> 
>     def scan_features_for_nested_dict(features):
>         is_sequence = False
>         if hasattr(features, "_type"):
>             if features._type != 'Sequence':
>                 return False
>             else:
>                 is_sequence = True
>                 features = features.feature
> 
>         if isinstance(features, list):
>             for value in features:
>                 if scan_features_for_nested_dict(value):
>                     return True
>             return False
> 
>         elif isinstance(features, dict):
>             for key, value in features.items():
>                 if is_sequence and len(features.keys()) == 1 and hasattr(features[key], "_type") and features[key]._type != "Sequence":
>                     return True
>                 if scan_features_for_nested_dict(value):
>                     return True
>             return False
>         else:
>             raise ValueError(f"{features} should be either a list of a dict")
> 
>     configs = load_configs(dataset_name)
> 
>     for config in configs:
>         with tempfile.TemporaryDirectory() as processed_temp_dir:
>             # create config and dataset
>             dataset_builder_cls = load_builder_class(dataset_name)
>             name = config.name if config is not None else None
>             dataset_builder = dataset_builder_cls(name=name, cache_dir=processed_temp_dir)
> 
>         is_nested_dict_in_dataset = scan_features_for_nested_dict(dataset_builder._info().features)
>         if is_nested_dict_in_dataset:
>             print(f"{dataset_name} with {name} needs refactoring")
> 
> 
> if __name__ == "__main__":
>     scan_for_nested_unnecessary_dict("race")  # prints True
>     scan_for_nested_unnecessary_dict("mlqa")  # prints True
>     scan_for_nested_unnecessary_dict("squad")  # prints Nothing
> 
>     # ran the following lines for 1min and seems to work -> didn't check for all datasets though
> #    api = hf_api.HfApi()
> #    all_datasets = [x.id for x in api.dataset_list(with_community_datasets=False)]
> #    for dataset in all_datasets:
> #        scan_for_nested_unnecessary_dict(dataset)
> ```

Great, I will try it

I'm not sure the work on this PR was finished @lhoestq  cc @mariamabarham @patrickvonplaten 

Sorry for that, apparently there are other datasets that could have unnecessary nested dicts.
We can have another PR to scan and fix the other datasets.