matplotlib 在3D轴上绘制直方图

x3naxklr  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(243)

我试图使用PolyCollection函数在3D轴上绘制一些直方图,我想要的图看起来像这样:(当然,彩色图是直方图除外)x1c 0d1x
对于我来说,我的x值是参数C_l的值的分布,y值是l的值(范围从2到33),z是每个C_l的频率(因此直方图在x-z平面上,y指定了l的每个值的直方图)。这是我拥有的代码,但我似乎无法让它工作:

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()
cigdeys3

cigdeys31#

这里还有几个未知数。首先,您的数据框架的结构仍然不清楚。更有问题的是,我们不知道您希望如何创建直方图。您的代码为1001个数据点创建1001个bin。为什么?我们也不清楚为什么要尝试创建多边形形状,当直方图是一种特定类型的条形图。考虑到这些未知因素,我试图让脚本尽可能灵活:

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()

样本输出:

更新

考虑到我们实际上在您的数据中看到了一个发展,连续的颜色图可能会提供更多的信息(并减少眼科紧急情况)。不需要太多的改变来实现这一点。

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()

样品输出(不同箱):

此版本还可以通过删除与xbins相关的所有内容轻松适应np.histogram中的bins="auto"选项。从对面站点查看的输出示例:

更新2

考虑到您的数据结构,您很可能更喜欢均匀间隔的bin。在这种情况下,我们不必单独计算每个切片的条形位置。

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()

样本输出(从相反部位观察,第一个直方图由于与其余部分相比值过大而被截断):

免责声明:滚动平均值计算改编自this SO answer

相关问题