1

enter image description here
I am plotting multiple rectangles using matplotlib.patches.Rectangle. I need to add dimensions for each. Instead of regular X coordinates of graph i need to mention dimensions of each rectangle. In the given picture I need to show x coordinates at the borders not the standard 250, 500, 750..

part1 = matplotlib.patches.Rectangle((ip.iloc[i,7], ip.iloc[i,8]), ip.iloc[i,3], ip.iloc[i,4], color =np.random.rand(3)) 
ax.add_patch(part1)
plt.text(ip.iloc[i,7]+(0.5*ip.iloc[i,4]), (ip.iloc[i,8]+(0.5*ip.iloc[i,3])),ip.iloc[i,0],rotation='vertical', color = 'white',fontsize=8)
Zephyr
  • 11,891
  • 53
  • 45
  • 80
Spandan Rout
  • 79
  • 3
  • 9

1 Answers1

1

CASE 1: SINGLE GROUP

If you have a ip dataframe with a 'x' and a 'y' columns with the coordinates of your rectangles which belong to a single group, you can set your ticks with ax.set_xticks(ip['x']) and ax.set_yticks(ip['y']). In this way you will get all ticks, except the last one which you have to include with max(ip['x']) + ip.iloc[np.argmax(ip['x']), ip.columns.get_loc('width')] and max(ip['y']) + ip.iloc[np.argmax(ip['y']), ip.columns.get_loc('height')].
With your ip dataframe, those columns should be the 8th (7 python index) and the 9th (8 python index) ones. Check the code below as a reference.

Code

import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

ip = pd.DataFrame({'x': [0, 260, 520, 0, 260, 520],
                   'y': [0, 0, 0, 120, 120, 120],
                   'width': [260, 260, 230, 260, 260, 230],
                   'height': [120, 120, 120, 130, 130, 130],
                   'text': np.random.randint(0, 1000, 6)})

fig, ax = plt.subplots()

for i in range(len(ip)):
    part1 = matplotlib.patches.Rectangle((ip.iloc[i,0], ip.iloc[i,1]), ip.iloc[i,2], ip.iloc[i,3], color=np.random.rand(3))
    ax.add_patch(part1)
    plt.text(ip.iloc[i,0]+(0.5*ip.iloc[i,2]), (ip.iloc[i,1]+(0.5*ip.iloc[i,3])), ip.iloc[i,4], rotation='vertical', color='white', fontsize=8)

xticks = list(ip['x'])
xticks.append(max(ip['x']) + ip.iloc[np.argmax(ip['x']), ip.columns.get_loc('width')])
yticks = list(ip['y'])
yticks.append(max(ip['y']) + ip.iloc[np.argmax(ip['y']), ip.columns.get_loc('height')])
ax.set_xticks(xticks)
ax.set_yticks(yticks)

ax.set_xlim([min(ip['x']), max(ip['x']) + ip.iloc[np.argmax(ip['x']), ip.columns.get_loc('width')]])
ax.set_ylim([min(ip['y']), max(ip['y']) + ip.iloc[np.argmax(ip['y']), ip.columns.get_loc('height')]])
plt.show()

Dataframe

     x    y  width  height  text
0    0    0    260     120   372
1  260    0    260     120   543
2  520    0    230     120   174
3    0  120    260     130   140
4  260  120    260     130    27
5  520  120    230     130   800

Result

enter image description here


CASE 2: DOUBLE GRUOP

If you have two different groups of areas, as in your image, one below 300 on y and the other above this threshold, you can duplicate the x axis in order to have the lower ticks on the bottom axis (ax1) and the upper ticks on the top axis (ax2). Check the code below for reference.
Please, note that here I used a different approach in order to simplify a bit the code and the readability.

Code

import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

ip = pd.DataFrame({'x': [0, 260, 520, 0, 260, 520, 0, 250, 500, 0],
                   'y': [0, 0, 0, 120, 120, 120, 300, 300, 300, 410],
                   'width': [260, 260, 230, 260, 260, 230, 200, 170, 150, 250],
                   'height': [120, 120, 120, 130, 130, 130, 110, 110, 110, 120],
                   'text': np.random.randint(0, 1000, 10)})

fig, ax1 = plt.subplots()
ax2 = ax1.twiny()

for i in range(len(ip)):
    part1 = matplotlib.patches.Rectangle((ip.iloc[i,0], ip.iloc[i,1]), ip.iloc[i,2], ip.iloc[i,3], color=np.random.rand(3))
    ax1.add_patch(part1)
    ax1.text(ip.iloc[i,0]+(0.5*ip.iloc[i,2]), (ip.iloc[i,1]+(0.5*ip.iloc[i,3])), ip.iloc[i,4], rotation='vertical', color='white', fontsize=8)

lower_ip = ip[ip['y'] < 300]
upper_ip = ip[ip['y'] >= 300]

xticks1 = list(lower_ip['x'] + lower_ip['width']) # select the right-side limit of each lower area
xticks1.extend(lower_ip['x'])                     # add the left-side limit of each lower area
xticks1 = set(xticks1)                            # filter by unique values
xticks1 = list(xticks1)                           # convert back to list
xticks1.sort()                                    # sort in increasing order

xticks2 = list(upper_ip['x'] + upper_ip['width']) # select the right-side limit of each upper area
xticks2.extend(upper_ip['x'])                     # add the left-side limit of each upper area
xticks2 = set(xticks2)                            # filter by unique values
xticks2 = list(xticks2)                           # convert back to list
xticks2.sort()                                    # sort in increasing order

# set equal min value for both axes
if xticks1[0] > xticks2[0]:
    xticks2.append(xticks1[0])
elif xticks1[0] < xticks2[0]:
    xticks1.append(xticks2[0])

# set equal max value for both axes
if xticks1[-1] > xticks2[-1]:
    xticks2.append(xticks1[-1])
elif xticks1[-1] < xticks2[-1]:
    xticks1.append(xticks2[-1])

# set lower and upper x ticks
ax1.set_xticks(xticks1)
ax2.set_xticks(xticks2)

yticks = list(ip['y'] + ip['height']) # select the down-side limit of each area
yticks.extend(ip['y'])                # add the up-side limit of each area
yticks = set(yticks)                  # filter by unique values
yticks = list(yticks)                 # convert back to list
yticks.sort()                         # sort in increasing order
ax1.set_yticks(yticks)                # set y ticks

ax1.set_xlim([min(ip['x']), max(ip['x']) + ip.iloc[np.argmax(ip['x']), ip.columns.get_loc('width')]])
ax1.set_ylim([min(ip['y']), max(ip['y']) + ip.iloc[np.argmax(ip['y']), ip.columns.get_loc('height')]])
plt.show()

Dataframe

     x    y  width  height  text
0    0    0    260     120   457
1  260    0    260     120   217
2  520    0    230     120   467
3    0  120    260     130   495
4  260  120    260     130   941
5  520  120    230     130   998
6    0  300    200     110    50
7  250  300    170     110   623
8  500  300    150     110   934
9    0  410    250     120   366

Result

enter image description here

Zephyr
  • 11,891
  • 53
  • 45
  • 80
  • thanks for your help. I have a further query. If you refer to my figure, there is are 2 sets of rectangles.. the second set which starts from 300 in the figure. Can we modify the xticks & yticks for them to show X & Y coordinates separately for the second figure? Hope i am clear with my question? Please help. – Spandan Rout Aug 03 '20 at 11:37
  • Thank you for your excellent reponses.. It really helped me. But yet again I still have some extra queries. I wanted to label these tow sets differently and in the Y side of the graph. Like naming them as "SET-1" for the first group and "SET-2" for the second group. Is that possible to create 2 different textboxes in the Y-side of the graph? – Spandan Rout Aug 05 '20 at 08:26
  • Yes, you can look at [this answer](https://stackoverflow.com/a/62613876/9214357) as a reference for adding and additional text boxes for the axis label. If you do not manage to adapt that code to your case, I think you should open another question in order to keep this thread as clean as possible, since this further query does not fall into the above question :-) – Zephyr Aug 05 '20 at 08:32
  • Sure Understood. Thank You!! – Spandan Rout Aug 05 '20 at 08:34