2

OK I have some code that I'm using to plot 3d charts in matplotlib (based on this great answer here). Here's the simplified version:

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

plt.close("all")

data1 = pd.DataFrame.from_dict(
    {
        "0": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10},
        "1": {0: 10, 1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, 10: 9},
        "2": {0: 9, 1: 10, 2: 0, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7, 10: 8},
        "3": {0: 8, 1: 9, 2: 10, 3: 0, 4: 1, 5: 2, 6: 3, 7: 4, 8: 5, 9: 6, 10: 7},
        "4": {0: 7, 1: 8, 2: 9, 3: 10, 4: 0, 5: 1, 6: 2, 7: 3, 8: 4, 9: 5, 10: 6},
        "5": {0: 6, 1: 7, 2: 8, 3: 9, 4: 10, 5: 0, 6: 1, 7: 2, 8: 3, 9: 4, 10: 5},
        "6": {0: 5, 1: 6, 2: 7, 3: 8, 4: 9, 5: 10, 6: 0, 7: 1, 8: 2, 9: 3, 10: 4},
        "7": {0: 4, 1: 5, 2: 6, 3: 7, 4: 8, 5: 9, 6: 10, 7: 0, 8: 1, 9: 2, 10: 3},
        "8": {0: 3, 1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9, 7: 10, 8: 0, 9: 1, 10: 2},
        "9": {0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 7, 6: 8, 7: 9, 8: 10, 9: 0, 10: 1},
        "10": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10, 10: 0},
    }
)
data2 = pd.DataFrame.from_dict(
    {
        "0": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10},
        "1": {0:np.nan,1:0.0,2:1.0,3:2.0,4:3.0,5:4.0,6:5.0,7:6.0,8:7.0,9:8.0,10:9.0,},
        "2":{0:np.nan,1:np.nan,2:0.0,3:1.0,4:2.0,5:3.0,6:4.0,7:5.0,8:6.0,9:7.0,10:8.0,},
        "3":{0:np.nan,1:np.nan,2:np.nan,3:0.0,4:1.0,5:2.0,6:3.0,7:4.0,8:5.0,9:6.0,10:7.0,},
        "4":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:0.0,5:1.0,6:2.0,7:3.0,8:4.0,9:5.0,10:6.0,},
        "5":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:0.0,6:1.0,7:2.0,8:3.0,9:4.0,10:5.0,},
        "6":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:np.nan,6:0.0,7:1.0,8:2.0,9:3.0,10:4.0,},
        "7":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:np.nan,6:np.nan,7:0.0,8:1.0,9:2.0,10:3.0,},
        "8":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:np.nan,6:np.nan,7:np.nan,8:0.0,9:1.0,10:2.0,},
        "9":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:np.nan,6:np.nan,7:np.nan,8:np.nan,9:0.0,10:1.0,},
        "10":{0:np.nan,1:np.nan,2:np.nan,3:np.nan,4:np.nan,5:np.nan,6:np.nan,7:np.nan,8:np.nan,9:np.nan,10:0.0,},
    }
)

fig=plt.figure(figsize=(8,3))
ax1=fig.add_subplot(121, projection="3d")
ax2=fig.add_subplot(122, projection="3d")

#thickness of the bars
dx, dy = 1, 1

# set up positions for the bars
xpos = np.arange(data1.shape[0])
ypos = np.arange(data1.shape[1])

# create meshgrid
# print xpos before and after this block if not clear
xpos, ypos = np.meshgrid(xpos, ypos)
xpos = xpos.flatten()
ypos = ypos.flatten()

# the bars starts from 0 attitude
zpos = np.zeros(data1.shape).flatten()

# the bars' heights
dz1 = data1.values.ravel()
dz2 = data2.values.ravel()

ma = np.nanmax(data1.values)
norm = matplotlib.colors.Normalize(vmin=0, vmax=10, clip=True)

# # plot
ax1.bar3d(xpos, ypos, zpos, dx, dy, dz1, shade=True, ec="k", lw=0.1)
ax2.bar3d(xpos, ypos, zpos, dx, dy, dz2, shade=True, ec="k", lw=0.1)

# set up the axes furniture
for ax in [ax1, ax2]:

    ax.set_xticks(xpos + dx / 2)
    ax.set_yticks(ypos + dy / 2)
    ax.w_yaxis.set_ticklabels([])
    ax.w_xaxis.set_ticklabels([])
    ax.w_zaxis.set_ticklabels([])
    ax.set_zlim(0, 10.5)
    ax.set_xlim(0, 10.5)
    ax.set_ylim(0, 10.5)

    # name the axes
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")

plt.show()

It results in a figure that looks like this:

a figure showing a maplotlib graph where some of the columns are out of sequence

The graph on the left shows everything in the right place (though you might have to run the code and rotate the graph to see for yourself) but the one on the right plots the bars out of sequence and we end up with something a bit... odd.

My question is

  1. what's going on here, and
  2. is there any way of fixing this within matplotlib, if so how?

Thanks!

Will
  • 621
  • 6
  • 19
  • Graph looks OK, it'll be clear if you let matplotlib show the labels. The `y` axis goes in the different direction as you (and I) may have thought. Try [adjusting view point](https://stackoverflow.com/a/12905458/4238408) of the axis if you don't feel like that perticular view. – Quang Hoang Apr 01 '21 at 18:58
  • Not fussed about the labelling - this is a simplified bit of code, and I must have snuck something in by mistake. It's the order that the bars are displayed in on the right that's the problem. Matplotlib is rendering earlier bars behind later bars. – Will Apr 01 '21 at 19:08
  • Yes, that's what I mean by direction of `y` being different. By default it's pointing inward as shown in the plot. If you want the different ordering, you will need to adjust the view point. – Quang Hoang Apr 01 '21 at 19:11
  • Sorry if I'm being stupid - but I just rotated the graph and it doesn't make any difference, there are still some bars plotted on top of things they should be behind? – Will Apr 01 '21 at 19:13
  • 2
    Ah, I see what you mean. Maybe `bar3d` is not playing well with `np.nan`, see if `dz2 = data2.fillna(0).values.ravel()` helps – Quang Hoang Apr 01 '21 at 19:16
  • Perfect! I have no idea why that worked, but it definitely does. Thank you – Will Apr 01 '21 at 19:18
  • 1
    Also try to filter out the `nan` values `notnas = ~np.isnan(dz2) ; ax2.bar3d(xpos[notnas], ypos[notnas], zpos[notnas], dx, dy, dz2[notnas], shade=True, ec="k", lw=0.1)` will not plot the `nan` as `0`. – Quang Hoang Apr 01 '21 at 19:21

0 Answers0