如何组织图中的图例、颜色条和图像的位置?

How to organize the position of the legend, colorbar and image in the plot?

我正在尝试绘制一些数据,但我不喜欢这些项目的组织方式。 例如,我想要一个更大的图像和一个更小的颜色条。当我修改图形的大小时仍然不成比例。而且我还想将每个钻孔标记为图例,以便我可以识别它。

这是我现在的图像:

这是代码:

# Create data
l = [2, 3, 4, 5,6]
n = 20
labels = [item for item in l for i in range(n)]
random.shuffle(labels,random.random)
labels =np.array(labels)
label_unique = np.unique(labels)

n = 100
x = np.linspace(613000, 615000, num=n) + np.random.uniform(-5, 5, size=n)
y = np.linspace(7763800, 7765800, num=n) + np.random.uniform(-5, 5, size=n)
z = np.linspace(1230, 1260, num=n) + np.random.uniform(-5, 5, size=n)
cpt1 = pd.DataFrame(list(zip(x, y, z,labels)),
              columns=['x','y', 'z','labels'])

l = [2, 3, 4, 5,6]
n = 60
labels = [item for item in l for i in range(n)]
random.shuffle(labels,random.random)
labels =np.array(labels)
label_unique = np.unique(labels)


cpt2 = pd.DataFrame(list(zip(x, y, z,labels)),
              columns=['x','y', 'z','labels'])

n = 400
x = np.linspace(613000, 615000, num=n) + np.random.uniform(-7, 7, size=n)
y = np.linspace(7763800, 7765800, num=n) + np.random.uniform(-7, 7, size=n)
z = np.linspace(1230, 1260, num=n) + np.random.uniform(-7, 7, size=n)
l = [2, 3, 4, 5,6]
n = 80
labels = [item for item in l for i in range(n)]
random.shuffle(labels,random.random)
labels =np.array(labels)
label_unique = np.unique(labels)

cpt3 = pd.DataFrame(list(zip(x, y, z,labels)),
              columns=['x','y', 'z','labels'])

cpt = [cpt1,cpt2,cpt3]

legend = cpt1.columns.values.tolist()


fig = plt.figure(figsize = (20, 9))
ax = plt.axes(projection ="3d")

# Add x, y gridlines
ax.grid(b = True, color ='grey',
        linestyle ='-.', linewidth = 0.3,
        alpha = 0.2)


# Creating color map
my_cmap = plt.get_cmap('hsv')
for  count, c in enumerate(cpt):
    x = c.x
    y = c.y
    z = c.z
    colorz = c.labels



    # Creating plot
    sctt = ax.scatter3D(x, y, z,
                        alpha = 0.8,
                        c = colorz,
                        cmap = my_cmap,
                        marker ='^',label = legend[count])

ax.set_xlabel('X-axis', fontweight ='bold')
ax.set_ylabel('Y-axis', fontweight ='bold')
ax.set_zlabel('Z-axis', fontweight ='bold')
fig.colorbar(sctt, ax = ax, shrink = 0.3, aspect = 5,orientation="horizontal")
plt.legend(bbox_to_anchor=(1.5,1), loc="upper left")
plt.show()

问题分为两部分,此答案至少分为三部分。

设置导入和合成数据。每当我发现自己重新输入或 copy/pasting 具有不同参数的复杂行时,我都会改用一个函数:



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

# Fake data of about the right shape
def syncoords(pars, n):
    '''pars: tuple or list of min, max, abs_variance'''
    return np.linspace(pars[0], pars[1], num=n) + \
        np.random.uniform(-1*pars[2], pars[2], size=n)

def synbore(n, xparams, yparams, zparams, zonevalues):
    '''create n entries for x,y,z, and zone from parameter tuples
       xyzparams: tuple of min, max, abs_variance
       zonevalues: list of zone values'''
    return pd.DataFrame({'x': syncoords(xparams, n),
                         'y': syncoords(yparams, n),
                         'z': syncoords(zparams, n),
                         'Zone': np.random.choice(zonevalues, size=n)})


boreparams = [['melaza', 10,
               (61300, 61500, 5), (77638, 77658, 5), (5023, 5400, .5),
               [2,3,4,5,6]],
              ['miel',   23,
               (45000, 45555, 5), (69712, 68800, 5), (4701, 5100, .7),
               [2,3,4,5,6]],
              ['jalea',  50,
               (50432, 50000, 6), (38200, 38600, 6), (5050, 5600, .9),
               [4,5,6,7,8]]] 

我没有停留在数据帧列表中,因为我总是希望我的数据与其 ID 字符串“一起旅行”。当我有两个列表时,我必须验证编辑和更新是否始终匹配。把它变成一个字典并没有让剩下的代码不再存在,所以我们的数据集的字典:


# I like my data to travel with its ID, which dictionaries are great for. 
# boredict entries: {"ID": xyzZone_dataframe}
# easy to make a dict from a list of (k, v) pairs, 
# so a lambda function to do that:
boredict = dict(map(lambda l:(l[0],
                              synbore(l[1],l[2],l[3],l[4],l[5])),
                    boreparams))

# Get ready to plot
fig = plt.figure(figsize=(11, 8.5)) # Or A? papersize
ax = plt.axes(projection ="3d")
ax.set_xlabel('X-axis', fontweight ='bold')
ax.set_ylabel('Y-axis', fontweight ='bold')
ax.set_zlabel('Z-axis', fontweight ='bold')
ax.grid(b = True, color ='grey',
        linestyle ='-.', linewidth = 0.3,
        alpha = 0.2)

# TODO: collect the max-min of all the Zones so one colormap works for all
# see https://matplotlib.org/stable/tutorials/colors/colormapnorms.html
# and https://matplotlib.org/stable/tutorials/colors/colorbar_only.html

for bname in boredict:
    # plot the actual bore data in 3D+colormap
    bdata = boredict[bname]
    sctt = ax.scatter3D(bdata.x, bdata.y, bdata.z,
                        alpha = 0.8,
                        c = bdata.Zone,
                        cmap = plt.get_cmap('hsv'),
                        marker ='^')
    # and a different marker to match the bore with the legend 
    ax.scatter3D(bdata.x[-1:], bdata.y[-1:], bdata.z[-1:] + 25,
                 marker = 'v',
                 s = 80, 
                 label = bname) 

最后是剧情布局管理。 3D 绘图需要大量的空白来让角旋转,但是您可以 trim 填充颜色条 (pad = 0) 和图形本身,使用 subplots_adjust。我也喜欢更大但更细的颜色条。


fig.colorbar(sctt, ax = ax,
             shrink = 0.4, aspect = 16, pad = 0, 
             orientation="horizontal")

plt.legend(bbox_to_anchor=(1.1, .8), loc="upper left")
fig.subplots_adjust(left=0, right=1,bottom=0,top=1) #reduce whitespace around the fig
plt.show()

这个图还需要做一件事——在这里,我们根据要在循环中绘制的最后一个数据帧创建一个颜色条,那个数据帧。但也许数据帧有不同的 Zone 数据范围!我们想要一个可以同时准确应用于所有数据的颜色条。这意味着要查看所有数据两次,一次是为了弄清楚颜色条的范围是什么,然后再次将它们与整体颜色条一起绘制出来。我在您要执行此操作的代码中添加了#TODO 注释,并附有指向现有 questions/answers/examples.

的链接