Generating combinations of specific values of a nested dict












0














I am developing a framework that allows to specify a machine learning model via a yaml file with different parameters nested so the configuration files are easy to read for humans.



I would like to give users the option of instead of specifying a parameter giving a range of options to try via a list.



Then I have to take this and generate all the possible valid combinations for the parameters the user has given multiple values for.



To mark which parameters are in fact lists and which ones are multiple values, I have opted to choose that combination values begin with 'multi_' (though if you have a different take I would be interested to hear it!).



So for example an user could write:



config = {
'train_config': {'param1': 1, 'param2': [1,2,3], 'multi_param3':[2,3,4]},
'model_config': {'cnn_layers': [{'units':3},{'units':4}], 'multi_param4': [[1,2], [3,4]]}
}


Indicating that 6 configuration files must be generated, where the values of 'param3' and 'param4' take all the possible combinations.



I have written a generator function to do this:



from pandas.io.json.normalize import nested_to_record 
import itertools
import operator
from functools import reduce

from collections import MutableMapping
from contextlib import suppress

def generate_multi_conf(config):
flat = nested_to_record(config)
flat = { tuple(key.split('.')): value for key, value in flat.items()}
multi_config_flat = { key[:-1] + (key[-1][6:],) : value for key, value in flat.items() if key[-1][:5]=='multi'}
if len(multi_config_flat) == 0: return # if there are no multi params this generator is empty
keys, values = zip(*multi_config_flat.items())

# delete the multi_params
# taken from https://stackoverflow.com/a/49723101/4841832
def delete_keys_from_dict(dictionary, keys):
for key in keys:
with suppress(KeyError):
del dictionary[key]
for value in dictionary.values():
if isinstance(value, MutableMapping):
delete_keys_from_dict(value, keys)
to_delete = ['multi_' + key[-1] for key, _ in multi_config_flat.items()]
delete_keys_from_dict(config, to_delete)

for values in itertools.product(*values):
experiment = dict(zip(keys, values))
for setting, value in experiment.items():
reduce(operator.getitem, setting[:-1], config)[setting[-1]] = value
yield config


Iterating over this with the example above gives:



{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
{'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}


Which is the result expected.



Any feedback on how to make this code more readable would be very much appreciated!










share|improve this question



























    0














    I am developing a framework that allows to specify a machine learning model via a yaml file with different parameters nested so the configuration files are easy to read for humans.



    I would like to give users the option of instead of specifying a parameter giving a range of options to try via a list.



    Then I have to take this and generate all the possible valid combinations for the parameters the user has given multiple values for.



    To mark which parameters are in fact lists and which ones are multiple values, I have opted to choose that combination values begin with 'multi_' (though if you have a different take I would be interested to hear it!).



    So for example an user could write:



    config = {
    'train_config': {'param1': 1, 'param2': [1,2,3], 'multi_param3':[2,3,4]},
    'model_config': {'cnn_layers': [{'units':3},{'units':4}], 'multi_param4': [[1,2], [3,4]]}
    }


    Indicating that 6 configuration files must be generated, where the values of 'param3' and 'param4' take all the possible combinations.



    I have written a generator function to do this:



    from pandas.io.json.normalize import nested_to_record 
    import itertools
    import operator
    from functools import reduce

    from collections import MutableMapping
    from contextlib import suppress

    def generate_multi_conf(config):
    flat = nested_to_record(config)
    flat = { tuple(key.split('.')): value for key, value in flat.items()}
    multi_config_flat = { key[:-1] + (key[-1][6:],) : value for key, value in flat.items() if key[-1][:5]=='multi'}
    if len(multi_config_flat) == 0: return # if there are no multi params this generator is empty
    keys, values = zip(*multi_config_flat.items())

    # delete the multi_params
    # taken from https://stackoverflow.com/a/49723101/4841832
    def delete_keys_from_dict(dictionary, keys):
    for key in keys:
    with suppress(KeyError):
    del dictionary[key]
    for value in dictionary.values():
    if isinstance(value, MutableMapping):
    delete_keys_from_dict(value, keys)
    to_delete = ['multi_' + key[-1] for key, _ in multi_config_flat.items()]
    delete_keys_from_dict(config, to_delete)

    for values in itertools.product(*values):
    experiment = dict(zip(keys, values))
    for setting, value in experiment.items():
    reduce(operator.getitem, setting[:-1], config)[setting[-1]] = value
    yield config


    Iterating over this with the example above gives:



    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
    {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}


    Which is the result expected.



    Any feedback on how to make this code more readable would be very much appreciated!










    share|improve this question

























      0












      0








      0







      I am developing a framework that allows to specify a machine learning model via a yaml file with different parameters nested so the configuration files are easy to read for humans.



      I would like to give users the option of instead of specifying a parameter giving a range of options to try via a list.



      Then I have to take this and generate all the possible valid combinations for the parameters the user has given multiple values for.



      To mark which parameters are in fact lists and which ones are multiple values, I have opted to choose that combination values begin with 'multi_' (though if you have a different take I would be interested to hear it!).



      So for example an user could write:



      config = {
      'train_config': {'param1': 1, 'param2': [1,2,3], 'multi_param3':[2,3,4]},
      'model_config': {'cnn_layers': [{'units':3},{'units':4}], 'multi_param4': [[1,2], [3,4]]}
      }


      Indicating that 6 configuration files must be generated, where the values of 'param3' and 'param4' take all the possible combinations.



      I have written a generator function to do this:



      from pandas.io.json.normalize import nested_to_record 
      import itertools
      import operator
      from functools import reduce

      from collections import MutableMapping
      from contextlib import suppress

      def generate_multi_conf(config):
      flat = nested_to_record(config)
      flat = { tuple(key.split('.')): value for key, value in flat.items()}
      multi_config_flat = { key[:-1] + (key[-1][6:],) : value for key, value in flat.items() if key[-1][:5]=='multi'}
      if len(multi_config_flat) == 0: return # if there are no multi params this generator is empty
      keys, values = zip(*multi_config_flat.items())

      # delete the multi_params
      # taken from https://stackoverflow.com/a/49723101/4841832
      def delete_keys_from_dict(dictionary, keys):
      for key in keys:
      with suppress(KeyError):
      del dictionary[key]
      for value in dictionary.values():
      if isinstance(value, MutableMapping):
      delete_keys_from_dict(value, keys)
      to_delete = ['multi_' + key[-1] for key, _ in multi_config_flat.items()]
      delete_keys_from_dict(config, to_delete)

      for values in itertools.product(*values):
      experiment = dict(zip(keys, values))
      for setting, value in experiment.items():
      reduce(operator.getitem, setting[:-1], config)[setting[-1]] = value
      yield config


      Iterating over this with the example above gives:



      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}


      Which is the result expected.



      Any feedback on how to make this code more readable would be very much appreciated!










      share|improve this question













      I am developing a framework that allows to specify a machine learning model via a yaml file with different parameters nested so the configuration files are easy to read for humans.



      I would like to give users the option of instead of specifying a parameter giving a range of options to try via a list.



      Then I have to take this and generate all the possible valid combinations for the parameters the user has given multiple values for.



      To mark which parameters are in fact lists and which ones are multiple values, I have opted to choose that combination values begin with 'multi_' (though if you have a different take I would be interested to hear it!).



      So for example an user could write:



      config = {
      'train_config': {'param1': 1, 'param2': [1,2,3], 'multi_param3':[2,3,4]},
      'model_config': {'cnn_layers': [{'units':3},{'units':4}], 'multi_param4': [[1,2], [3,4]]}
      }


      Indicating that 6 configuration files must be generated, where the values of 'param3' and 'param4' take all the possible combinations.



      I have written a generator function to do this:



      from pandas.io.json.normalize import nested_to_record 
      import itertools
      import operator
      from functools import reduce

      from collections import MutableMapping
      from contextlib import suppress

      def generate_multi_conf(config):
      flat = nested_to_record(config)
      flat = { tuple(key.split('.')): value for key, value in flat.items()}
      multi_config_flat = { key[:-1] + (key[-1][6:],) : value for key, value in flat.items() if key[-1][:5]=='multi'}
      if len(multi_config_flat) == 0: return # if there are no multi params this generator is empty
      keys, values = zip(*multi_config_flat.items())

      # delete the multi_params
      # taken from https://stackoverflow.com/a/49723101/4841832
      def delete_keys_from_dict(dictionary, keys):
      for key in keys:
      with suppress(KeyError):
      del dictionary[key]
      for value in dictionary.values():
      if isinstance(value, MutableMapping):
      delete_keys_from_dict(value, keys)
      to_delete = ['multi_' + key[-1] for key, _ in multi_config_flat.items()]
      delete_keys_from_dict(config, to_delete)

      for values in itertools.product(*values):
      experiment = dict(zip(keys, values))
      for setting, value in experiment.items():
      reduce(operator.getitem, setting[:-1], config)[setting[-1]] = value
      yield config


      Iterating over this with the example above gives:



      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 2}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 3}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [1, 2]}}
      {'train_config': {'param1': 1, 'param2': [1, 2, 3], 'param3': 4}, 'model_config': {'cnn_layers': [{'units': 3}, {'units': 4}], 'param4': [3, 4]}}


      Which is the result expected.



      Any feedback on how to make this code more readable would be very much appreciated!







      python-3.x generator






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 21 mins ago









      Jsevillamol

      1354




      1354



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210275%2fgenerating-combinations-of-specific-values-of-a-nested-dict%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210275%2fgenerating-combinations-of-specific-values-of-a-nested-dict%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Feedback on college project

          Futebolista

          Albești (Vaslui)