将matplotlib轮廓限制/掩盖到数据区

时间:2017-05-20 06:35:17

标签: python matplotlib plot

我有一个pandas DataFrame,由x,y和z列给出非均匀间隔的数据点,其中x和y是变量对,z是因变量。例如:

import matplotlib.pyplot as plt
from matploblib.mlab import griddata
import numpy as np
import pandas as pd

df = pd.DataFrame({'x':[0, 0, 1, 1, 3, 3, 3, 4, 4, 4], 
                   'y':[0, 1, 0, 1, 0.2, 0.7, 1.4, 0.2, 1.4, 2], 
                   'z':[50, 40, 40, 30, 30, 30, 20, 20, 20, 10]})

x = df['x']
y = df['y']
z = df['z']

我想在x和y上做因变量z的等高线图。为此,我创建了一个新网格,使用matplotlib.mlab的griddata函数插入数据。

xi = np.linspace(x.min(), x.max(), 100)
yi = np.linspace(y.min(), y.max(), 100)
z_grid = griddata(x, y, z, xi, yi, interp='linear')
plt.contourf(xi, yi, z_grid, 15)
plt.scatter(x, y, color='k') # The original data points
plt.show()

虽然这有效,但输出并不是我想要的。我不希望griddata在x和y数据的最小值和最大值给出的边界之外进行插值。下面的图是调用plt.show()后显示的内容,然后以紫色突出显示我想要插入和轮廓化的数据区域。紫色线外的轮廓应该是空白的。我怎么能掩盖外围数据?

Plot as created by mpl Plot as it should be

遗憾的是linked question没有回答我的问题,因为我没有明确的数学方法来定义进行三角测量的条件。是否可以根据数据单独定义一个条件来屏蔽数据,以上面的Dataframe为例?

2 个答案:

答案 0 :(得分:3)

this question的答案所示,人们可能会引入掩盖价值的条件。

问题的句子 “我不希望griddata在x和y数据的最小值和最大值给出的边界之外进行插值。”意味着存在一些最小/最大条件,可以使用它。

如果不是这种情况,可以使用路径剪切轮廓。需要指定此路径的点,因为没有通用的方法可以知道哪些点应该是边缘。下面的代码针对三种不同的可能路径执行此操作。

for

enter image description here

答案 1 :(得分:1)

欧内斯特的答案是一个很好的解决方案,但是对于许多轮廓来说却很慢。我没有剪裁每个蒙版,而是通过构造所需剪裁蒙版的补多边形来构建蒙版。

这是基于欧内斯特接受的答案的代码:

import numpy as np
import pandas as pd
import matplotlib.tri as tri
import matplotlib.pyplot as plt
from descartes import PolygonPatch
from shapely.geometry import Polygon

df = pd.DataFrame({'x':[0, 0, 1, 1, 3, 3, 3, 4, 4, 4], 
                   'y':[0, 1, 0, 1, 0.2, 0.7, 1.4, 0.2, 1.4, 2], 
                   'z':[50, 40, 40, 30, 30, 30, 20, 20, 20, 10]})

points = df[['x', 'y']]
values = df[['z']]

xi = np.linspace(points.x.min(), points.x.max(), 100)
yi = np.linspace(points.y.min(), points.y.max(), 100)

triang = tri.Triangulation(points.x, points.y)
interpolator = tri.LinearTriInterpolator(triang, values.z)
Xi, Yi = np.meshgrid(xi, yi)
zi = interpolator(Xi, Yi)

clipindex = [ [0,2,4,7,8,9,6,3,1,0],
              [0,2,4,7,5,8,9,6,3,1,0],
              [0,2,4,7,8,9,6,5,3,1,0]]

fig, axes = plt.subplots(ncols=3, sharey=True, figsize=(10,4))

for i, ax in enumerate(axes):

    ax.set_xlim(-0.5, 4.5)
    ax.set_ylim(-0.2, 2.2)
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()    

    cont = ax.contourf(Xi, Yi, zi, 15)
    ax.scatter(points.x, points.y, color='k', zorder=2) # The original data points
    ax.plot(points.x[clipindex[i]], points.y[clipindex[i]], color="crimson", zorder=1)

    #### 'Universe polygon': 
    ext_bound = Polygon([(xlim[0], ylim[0]), (xlim[0], ylim[1]), (xlim[1], ylim[1]), (xlim[1], ylim[0]), (xlim[0], ylim[0])])
    #### Clipping mask as polygon:
    inner_bound = Polygon([ (row.x, row.y) for idx, row in points.iloc[clipindex[i]].iterrows() ])
    #### Mask as the symmetric difference of both polygons:
    mask = ext_bound.symmetric_difference(inner_bound)

   ax.add_patch(PolygonPatch(mask, facecolor='white', zorder=1, edgecolor='white'))

plt.show()

enter image description here