Catalyst
Catalyst

Reputation: 426

How do I increase the gap of 3D bar chart in y-axis?

I have a 4*7 3D bar chart where I would like to increase the gap or spacing of the 7 bars in the y-axis.

Below is my code:

import pandas as pd
import matplotlib.pyplot as plt  
from mpl_toolkits.mplot3d import axes3d
import numpy as np


# Set plotting style
plt.style.use('seaborn-white')

dz=[]
z0 = np.array([ 1.,  3.,  11.,   8.,   7.,   6.,   6.,   6.,   5.,   4.,
                3.,   11.,   10.,  1.,  1.,  7., 1.,  3.,  11.,   8.,
                8.,   7.,   6.,   6., 1.,  1.,  7., 1.,])
dz.append(z0)

z1 =[ 5.,   5.,   8.,   4.,   2.,   0.,   0.,   0.,   0.,   0.,   0.,
      1.,   6.,  5.,   7.,   2., 1.,  3.,  11.,   8., 8.,   7.,   6.,   6.,
      1.,  1.,  7., 1.,]

dz.append(z1)

z2 =[ 15.,   5.,   8.,   2.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
      3.,   5.,  2.,   7.,   2., 1.,  3.,  11.,   8., 8.,   7.,   6.,   6.,
      1.,  1.,  7., 1.,]

dz.append(z2)

_zpos = z0*0


xlabels = pd.Index(['X01', 'X02', 'X03', 'X04'], dtype='object')

ylabels = pd.Index(['Y01', 'Y02', 'Y03', 'Y04', 'Y05', 'Y06', 'Y07'], dtype='object')

x = np.arange(xlabels.shape[0])

y = np.arange(ylabels.shape[0])

x_M, y_M = np.meshgrid(x, y, copy=False)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection='3d')

# Making the intervals in the axes match with their respective entries
ax.w_xaxis.set_ticks(x + 0.5/2.)
ax.w_yaxis.set_ticks(y + 0.5/2.)

# Renaming the ticks as they were before
ax.w_xaxis.set_ticklabels(xlabels)
ax.w_yaxis.set_ticklabels(ylabels)

# Labeling the 3 dimensions
ax.set_xlabel('X label')
ax.set_ylabel('Y label')
ax.set_zlabel('Z label')

# Choosing the range of values to be extended in the set colormap
values = np.linspace(0.2, 1., x_M.ravel().shape[0])

# Selecting an appropriate colormap

colors = ['#FFC04C', 'blue', '#3e9a19', 
          '#599be5','#bf666f','#a235bf','#848381','#fb90d6','#fb9125']

# Increase the number of segment to 3 by changing the X in 'range(X)' to 3.
for i in range(3):
    ax.bar3d(x_M.ravel(), y_M.ravel(), _zpos, dx=0.3, dy=0.3, dz=dz[i], 
              color=colors[i])
    _zpos += dz[i]
 

#plt.gca().invert_xaxis()
#plt.gca().invert_yaxis()
Segment1_proxy         = plt.Rectangle((0, 0), 1, 1, fc="#FFC04C")   
Segment2_proxy         = plt.Rectangle((0, 0), 1, 1, fc="blue")
Segment3_proxy         = plt.Rectangle((0, 0), 1, 1, fc="#3e9a19")

ax.legend([Segment1_proxy,
           Segment2_proxy,
           Segment3_proxy],['Segment1',
                            'Segment2',
                            'Segment3'
         ])
plt.show()

I have tried to look for solutions but couldn't find any in relation to 3D bar chart. I suppose those solutions for 2D bar chart can't be applied here (or I'm might be wrong) and thus I'm not sure where to start (just started learning matplotlib a day ago). Can someone help please?

Post below edited on 31st March 2021

Thanks Yozhikoff for providing solutions to the above questions but would like to improve the visualisation...

I increased the gap between x-axis, i.e., increasing the gap between X01 and X02 by changing the syntax to

ax.set_box_aspect((10, 3, 1)) 

and

for i in range(3):
    ax.bar3d(x_M.ravel(), y_M.ravel(), _zpos, dx=0.03, dy=0.3, dz=dz[i], 
              color=colors[i], lightsource=ls)
    _zpos += dz[i]

which then generated the figure below:

enter image description here

As you may see that the z-axis is squeezed but if I changed the value for ax.set_box_aspect to (30,3,5), the gap between the x-axis doesn't seem to increase much and the whole figure becomes small.

enter image description here

Any advice?

Upvotes: 0

Views: 838

Answers (1)

Yozhikoff
Yozhikoff

Reputation: 66

Tested in python 3.11.2, matplotlib 3.7.1

You can try using the ax.set_box_aspect() method.

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection='3d')
ax.set_box_aspect((1, 3, 1))

Works especially good combined with carefully selected dx and dy arguments of ax.bar3d().

There are several ways to further improve the aesthetics:

  1. Find a better angle using plt.gca().view_init(elev=15, azim=20)
  2. Make a LightSource that highlights your plot in a right way
    ls = mpl.colors.LightSource(azdeg=30, altdeg=10)
    ax.bar3d(..., lightsource=ls)
    

In your case, I'd do something like this:

import pandas as pd
import matplotlib.pyplot as plt  
from mpl_toolkits.mplot3d import axes3d
import numpy as np
import matplotlib as mpl

dz=[]
z0 = np.array([ 1.,  3.,  11.,   8.,   7.,   6.,   6.,   6.,   5.,   4.,
                3.,   11.,   10.,  1.,  1.,  7., 1.,  3.,  11.,   8.,
                8.,   7.,   6.,   6., 1.,  1.,  7., 1.,])
dz.append(z0)

z1 =[ 5.,   5.,   8.,   4.,   2.,   0.,   0.,   0.,   0.,   0.,   0.,
      1.,   6.,  5.,   7.,   2., 1.,  3.,  11.,   8., 8.,   7.,   6.,   6.,
      1.,  1.,  7., 1.,]

dz.append(z1)

z2 =[ 15.,   5.,   8.,   2.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
      3.,   5.,  2.,   7.,   2., 1.,  3.,  11.,   8., 8.,   7.,   6.,   6.,
      1.,  1.,  7., 1.,]

dz.append(z2)

_zpos = z0*0


xlabels = pd.Index(['X01', 'X02', 'X03', 'X04'], dtype='object')

ylabels = pd.Index(['Y01', 'Y02', 'Y03', 'Y04', 'Y05', 'Y06', 'Y07'], dtype='object')

x = np.arange(xlabels.shape[0])

y = np.arange(ylabels.shape[0])

x_M, y_M = np.meshgrid(x, y, copy=False)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection='3d')
ax.set_box_aspect((1, 3.5, 1))
ax.view_init(15, 35)

ls = mpl.colors.LightSource(azdeg=30, altdeg=10)

# Making the intervals in the axes match with their respective entries
ax.xaxis.set_ticks(x + 0.5/2.)
ax.yaxis.set_ticks(y + 0.5/2.)

# Renaming the ticks as they were before
ax.xaxis.set_ticklabels(xlabels)
ax.yaxis.set_ticklabels(ylabels)

# Labeling the 3 dimensions
ax.set_xlabel('X label')
ax.set_ylabel('Y label')
ax.set_zlabel('Z label')

# Choosing the range of values to be extended in the set colormap
values = np.linspace(0.2, 1., x_M.ravel().shape[0])

# Selecting an appropriate colormap

colors = ['#FFC04C', 'blue', '#3e9a19', 
          '#599be5','#bf666f','#a235bf','#848381','#fb90d6','#fb9125']

# Increase the number of segment to 3 by changing the X in 'range(X)' to 3.
for i in range(3):
    ax.bar3d(x_M.ravel(), y_M.ravel(), _zpos, dx=0.2, dy=0.1, dz=dz[i], 
              color=colors[i], lightsource=ls)
    _zpos += dz[i]
 

Segment1_proxy         = plt.Rectangle((0, 0), 1, 1, fc="#FFC04C")   
Segment2_proxy         = plt.Rectangle((0, 0), 1, 1, fc="blue")
Segment3_proxy         = plt.Rectangle((0, 0), 1, 1, fc="#3e9a19")

ax.legend([Segment1_proxy,
           Segment2_proxy,
           Segment3_proxy],['Segment1',
                            'Segment2',
                            'Segment3'
         ])

plt.show()

enter image description here

Upvotes: 2

Related Questions