简体   繁体   中英

Plotting histograms on 3D axes with Python

I am trying to plot a few histograms on a 3d axis using the PolyCollection function, my desired plot looks something like this: (except of course the coloured plots are histograms) 在此处输入图像描述

For me, my x-values are the distribution of values of a parameter C_l, y-values are the values of l (ranging from 2 to 33) and z is the frequency of each C_l (so the histogram is on the xz plane, with y specifying the histogram for each value of l). This is the code I have but I can't seem to get it to work:

fig = plt.figure()
ax = fig.gca(projection='3d')
nside=16

'multi_dens_auto_cl_dist is just a 33x1001 matrix of all the C_l values, with the rows denoting each iteration I calculated previously and the columns being the l's)
xs=np.linspace(multi_dens_auto_cl_dist.min(),multi_dens_auto_cl_dist.max(),num=1001)

def cc(arg):
    return mcolors.to_rgba(arg, alpha=0.6)

verts = []
zs = np.arange(2,2*nside+1,1)

for z in zs:
    ys,binvals,_ = plt.hist(multi_dens_auto_cl_dist[:][z],bins=xs)
    ys[0], ys[-1] = 0, 0
    verts.append(list(zip(xs, ys)))

poly = PolyCollection(verts,facecolors=[cc('r'), cc('g'), cc('b'), cc('y')]*4+[cc('r')])
poly.set_alpha(0.7)
ax.add_collection3d(poly, zs=zs, zdir='y')

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')


plt.title('Density auto power spectrum distribution')
plt.show()

Any help would be greatly appreciated thank you!

Data file of multi_dens_auto_cl_dist

There are still several unknowns here. For one, it is still unclear what the structure of your dataframe is. Even more problematic, we don't know how you want to create your histograms. Your code creates 1001 bins for 1001 data points. Why? It is also not clear why you try to create polygon shapes when a histogram is a specific type of bar chart. I have tried to keep the script as flexible as possible given these unknowns:

from matplotlib import pyplot as plt
import numpy as np
from cycler import cycler
import pandas as pd

inputarr = np.loadtxt("data.txt")
df = pd.DataFrame(inputarr.reshape(1001, 33))
#determine the number of columns
ncol = df.shape[1]

fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(projection="3d")

#since you have so many columns, it is difficult to give them all unique colors
#but we can define through which colors we cycle
#you could also create a range of colors along a color map and give each histogram 
#its unique color, which would then be similar to neighbouring colors
color_cycler = (cycler(color=["tab:orange", "yellow", "red", "blue", "green"]))
ax.set_prop_cycle(color_cycler)

#define the yticks, i.e., the column numbers
yticks = np.arange(ncol)

#just to demonstrate that bins don't have to be evenly spaced, we define normalized bins 
xbinnorm = [0, 0.1, 0.2, 0.3, 0.5, 1]
#we adapt the normalized bins to the minimum and maximum of the entire dataframe
xbins = [df.min().min() + i * (df.max().max()-df.min().min()) for i in xbinnorm]

#calculate now the histogram and plot it for each column
for ytick in yticks:
    
    #extract the current column from your df by its number
    col =  df.iloc[:, ytick]
    
    #determine the histogram values, here you have to adapt it to your needs
    histvals, edges = np.histogram(col, bins=xbins)
    
    #calculate the center and width of each bar
    #obviously not necessary to do this for each column if you always have the same bins 
    #but if you choose for np.histogram other parameters, the bins may not be the same for each histogram
    xcenter = np.convolve(edges, np.ones(2), "valid")/2
    xwidth = np.diff(edges)

    #plot the histogram as a bar for each bin
    ax.bar(left=xcenter, height=histvals, width=xwidth, zs=ytick, zdir="y", alpha=0.666)

ax.set_xlabel("bin")
ax.set_ylabel("column")
ax.set_zlabel("value")

#label every other column number
ax.set_yticks(yticks[::2])
#label bin edges, obviously only possible if all have the same bins
ax.set_xticks(xbins)

plt.show()

Sample output:

在此处输入图像描述

Update
Given that we actually see in your data a development, a continuous colormap might be more informative (and cause less ophthalmological emergencies). Not much change needed to achieve this.

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

inputarr = np.loadtxt("data.txt")
df = pd.DataFrame(inputarr.reshape(1001, 33))
#determine the number of columns
ncol = df.shape[1]

fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(projection="3d")

#define the colormap 
my_cmap = plt.cm.inferno

#define the yticks, i.e., the column numbers
yticks = np.arange(ncol)

#just to demonstrate that bins don't have to be evenly spaced, we define normalized bins 
xbinnorm = [0, 0.1, 0.3, 0.5, 0.8, 1]
#we adapt the normalized bins to the minimum and maximum of the entire dataframe
xbins = [df.min().min() + i * (df.max().max()-df.min().min()) for i in xbinnorm]

#calculate now the histogram and plot it for each column
for i, ytick in enumerate(yticks):

    #extract the current column from your df by its number
    col =  df.iloc[:, ytick]

    #determine the histogram values, here you have to adapt it to your needs
    histvals, edges = np.histogram(col, bins=xbins)

    #calculate the center and width of each bar
    #obviously not necessary to do this for each column if you always have the same bins 
    #but if you choose for np.histogram other parameters, the bins may not be the same for each histogram
    xcenter = np.convolve(edges, np.ones(2), "valid")/2
    xwidth = np.diff(edges)

    #plot the histogram as a bar for each bin
    #now with continuous color mapping and edgecolor, so we can better see all bars
    ax.bar(left=xcenter, height=histvals, width=xwidth, zs=ytick, zdir="y", color=my_cmap(1-i/ncol), alpha=0.666, edgecolor="grey")

ax.set_xlabel("bin")
ax.set_ylabel("column")
ax.set_zlabel("value")

#label every other column number
ax.set_yticks(yticks[::2])
#label bin edges, obviously only possible if all have the same bins
ax.set_xticks(xbins)

plt.show()

Sample output (with different bins): 在此处输入图像描述

This version can also easily adapted to the bins="auto" option in np.histogram by removing everything related to xbins . Sample output with view from the opposite site:

在此处输入图像描述

Update2

Given your data structure, you most likely prefer evenly spaced bins. In this case, we don't have to calculate the bar position for each slice individually.

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

inputarr = np.loadtxt("data.txt")
df = pd.DataFrame(inputarr.reshape(1001, 33))
#determine the number of columns
ncol = df.shape[1]

fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(projection="3d")

#define the colormap 
my_cmap = plt.cm.inferno

#define the yticks, i.e., the column numbers
yticks = np.arange(ncol)

#we create evenly spaced bins between the minimum and maximum of the entire dataframe
xbins = np.linspace(df.min().min(), df.max().max(), 100)
#and calculate the center and widths of the bars
xcenter = np.convolve(xbins, np.ones(2), "valid")/2
xwidth = np.diff(xbins)

#calculate now the histogram and plot it for each column
for i, ytick in enumerate(yticks):

    #extract the current column from your df by its number
    col =  df.iloc[:, ytick]

    #determine the histogram values, here you have to adapt it to your needs
    histvals, _ = np.histogram(col, bins=xbins)

    #plot the histogram as a bar for each bin
    #now with continuous color mapping and edgecolor, but thinner lines, so we can better see all bars
    ax.bar(left=xcenter, height=histvals, width=xwidth, zs=ytick, zdir="y", color=my_cmap(i/ncol), alpha=0.666, edgecolor="grey", linewidth=0.3)

ax.set_xlabel("bin")
ax.set_ylabel("column")
ax.set_zlabel("value")

#label every other column number
ax.set_yticks(yticks[::2])
ax.set_zlim3d(0,60)
plt.show()

Sample output (view from the opposite site, the first histograms are cut off because of excessive values in comparison to the rest):

![![在此处输入图像描述

Disclaimer: The rolling mean calculation was adapted from this SO answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM