title: How can I convert a Pandas DataFrame to a three level nested dictionary?
body: <p>How can I convert a Pandas DataFrame to a three level nested dictionary using column names?</p>
  <p>The columns are not first three columns and I want it to group by column <code>artist</code> then group by column <code>album</code>, and I need it to be case insensitive, preferably without using defaultdict.</p>
  <p>This is a minimal reproducible example:</p>
  <pre class="lang-py prettyprint-override"><code>from collections import defaultdict                                               
  from itertools import product                                                     
  from pandas import DataFrame                                                      
  tree = defaultdict(lambda: defaultdict(dict))                                     
  columns = {'a': str(), 'b': str(), 'c': str(), 'd': int(), 'e': int(), 'f': int()}
  df = DataFrame(columns, index=[])                                                 
  for i, j, k in product('abcd', repeat=3):                                         
      tree[i][j][k] = list(map('abcd'.index, (i, j, k)))                            
      df.loc[len(df)] = [i, j, k, *list(map('abcd'.index, (i, j, k)))]              
  </code></pre>
  <p>How can I get a nested dictionary similar to <code>tree</code> from <code>df</code>?</p>
  <p>I am really sorry I can provide any actual examples because they wouldn't be minimal.</p>
  <p>I tried to use <code>.groupby()</code> but I only ever saw it being used with one column and I really don't know what to do with the <code>pandas.core.groupby.generic.DataFrameGroupBy</code> object it returns, I just started using it today.</p>
  <hr />
  <p>Currently I can do this:</p>
  <pre class="lang-py prettyprint-override"><code>tree1 = dict()                                                                                  
  for index, row in df.iterrows():                                                                
      if not tree1.get(row['a'].lower()):                                                         
          tree1[row['a'].lower()] = dict()                                                        
      if not tree1[row['a'].lower()].get(row['b'].lower()):                                       
          tree1[row['a'].lower()][row['b'].lower()] = dict()                                      
      tree1[row['a'].lower()][row['b'].lower()][row['c'].lower()] = [row['d'], row['e'], row['f']]
  </code></pre>
  <p>I actually implemented case insensitive <code>str</code> and <code>dict</code> but for the sake of brevity (they are very long) I wouldn't use it here.</p>
  <p>But according to this answer <a href="https://stackoverflow.com/a/55557758/16383578">https://stackoverflow.com/a/55557758/16383578</a> such method is bad, what is a better way?</p>