title: Is there any way to export many pie/donut charts with exact same size and position? (matplotlib)
body: <p>I want to export some donut plots that need to have the same exact position and size (e.g. so I can paste an also circular image and have it centered just by knowing the position at which it should be pasted thanks to the standarized size/position). As an example of what would be desirable, imagine that if someone were to use a regular image viewer and go to the next image in the group, they would only see the weights and colors changing in the plot and not see any change in the position/size.</p>
  
  <p>I've tried this by configuring the same size in inches for all the figures and then exporting them with the same size in pixels; however, matplotlib still exports the plots with different sizes which, although minor, may affect the smooth visual effect I want to achieve. I had some code which added some labels and colorings but I decided to simplify the code so it can be easer for someone to help me with this issue. I've also tried applying the code seen <a href="https://kavigupta.org/2019/05/18/Setting-the-size-of-figures-in-matplotlib/" rel="nofollow noreferrer">at this article</a> (see attempt 3 in the code, which uses <em>get_size</em> and <em>set_size</em> functions) but it still has the problem of the output plots being slightly off from one another.</p>
  
  <p>Here's the code (see specially the lines which are preceded by an "attempt #" comment):</p>
  
  <pre><code># coding=utf8
  import pandas as pd
  import matplotlib.pyplot as plt
  
  from matplotlib.image import imread
  from tempfile import NamedTemporaryFile
  
  
  def get_size(fig, dpi=100):
      with NamedTemporaryFile(suffix='.png') as f:
          fig.savefig(f.name, bbox_inches='tight', dpi=dpi)
          height, width, _channels = imread(f.name).shape
          return width / dpi, height / dpi
  
  
  def set_size(fig, size, dpi=100, eps=1e-2, give_up=2, min_size_px=10):
      target_width, target_height = size
      set_width, set_height = target_width, target_height # reasonable starting point
      deltas = [] # how far we have
      while True:
          fig.set_size_inches([set_width, set_height])
          actual_width, actual_height = get_size(fig, dpi=dpi)
          set_width *= target_width / actual_width
          set_height *= target_height / actual_height
          deltas.append(abs(actual_width - target_width) + abs(actual_height - target_height))
          if deltas[-1] &lt; eps:
              return True
          if len(deltas) &gt; give_up and sorted(deltas[-give_up:]) == deltas[-give_up:]:
              return False
          if set_width * dpi &lt; min_size_px or set_height * dpi &lt; min_size_px:
              return False
  
  
  d_acts_1 = {'activity': ['Activity 1', 'Activity 3', 'Activity 4', 'Activity 7', 'Activity 9'],
              'weight': [0.2, 0.1, 0.4, 0.1, 0.2]}
  d_acts_2 = {'activity': ['Activity 1', 'Activity 3', 'Activity 4', 'Activity 7', 'Activity 9'],
              'weight': [0.1, 0.2, 0.1, 0.2, 0.4]}
  d_acts_3 = {'activity': ['Activity 2', 'Activity 4', 'Activity 5', 'Activity 6', 'Activity 7'],
              'weight': [0.2, 0.4, 0.1, 0.1, 0.2]}
  d_acts_4 = {'activity': ['Activity 1', 'Activity 2', 'Activity 5', 'Activity 7', 'Activity 8'],
              'weight': [0.1, 0.3, 0.4, 0.1, 0.1]}
  
  df_acts_1 = pd.DataFrame(data=d_acts_1)
  df_acts_2 = pd.DataFrame(data=d_acts_2)
  df_acts_3 = pd.DataFrame(data=d_acts_3)
  df_acts_4 = pd.DataFrame(data=d_acts_4)
  
  df_groups_1 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
                                   'weight': [0.3, 0.5, 0.2]})
  df_groups_2 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
                                   'weight': [0.1, 0.3, 0.6]})
  df_groups_3 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
                                   'weight': [0.7, 0.3, 0.0]})
  df_groups_4 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
                                   'weight': [0.4, 0.6, 0.0]})
  
  df_grp_act = pd.DataFrame(data={'groups': [df_groups_1, df_groups_2, df_groups_3, df_groups_4],
                                  'activities': [df_acts_1, df_acts_2, df_acts_3, df_acts_4]})
  
  # create donut plots
  i = 1
  for _, row in df_grp_act.iterrows():
      df_groups = row['groups']
      df_activities = row['activities']
  
      # create lists ignoring "0%" content, for outer and inner rings
      outer_names = df_groups[df_groups['weight'] != 0].group.tolist()
      outer_weights = df_groups[df_groups['weight'] != 0].weight.tolist()
      inner_names = df_activities[df_activities['weight'] != 0].activity.tolist()
      inner_weights = df_activities[df_activities['weight'] != 0].weight.tolist()
  
      # create blank figure and add donut plots
      fig, ax = plt.subplots()
      # fig.set_size_inches(8, 8)
  
      # pie_outer, _ = ax.pie(outer_weights, radius=1.3, labels=outer_names)
      # inner_inner_labels = list(map(lambda x: str(round(x, 2) * 100)[:-2] + '%', inner_weights))
      # pie_inner, texts = ax.pie(inner_weights, radius=1.3 - 0.3, labels=inner_inner_labels, labeldistance=0.7)
      pie_outer, _ = ax.pie(outer_weights, radius=1.3)
      pie_inner, _ = ax.pie(inner_weights, radius=1.3 - 0.3)
      plt.setp(pie_outer, width=0.3, edgecolor='white')
      plt.setp(pie_inner, width=0.4, edgecolor='white')
  
      # attempt 1
      ax.axis('equal')  # equalizes axis units
      plt.savefig(f"result-infographics/plots/plot.{i}.png")
  
      # # attempt 2
      # ax.axis('equal')  # equalizes axis units
      # my_dpi = 96
      # fig.set_size_inches(900 / my_dpi, 800 / my_dpi)
      # # plt.show()
      # plt.savefig(f"result-infographics/plots/plot.{i}.png", dpi=my_dpi)
  
      # # attempt 3
      # set_size(fig, (8, 8))
      # plt.savefig(f"result-infographics/plots/plot.{i}.png", bbox_inches='tight')
      # print(imread(f"result-infographics/plots/plot.{i}.png").shape)  # outputs (250, 500, 4)
  
      i += 1
  
  </code></pre>
  
  <p>Example plots which present this problem: <a href="https://i.stack.imgur.com/I3pH9.png" rel="nofollow noreferrer">Plot 1</a> <a href="https://i.stack.imgur.com/cgfjD.png" rel="nofollow noreferrer">Plot 2</a></p>
  
  <p>I'd want a solution that uses matplotlib as I've found it is generally straightforward to make donut plots with it, but any alternative by which I could get this working would be appreciated as well.</p>