简体   繁体   English

将 matplotlib 轮廓限制/屏蔽到数据区域

[英]Limit/mask matplotlib contour to data area

I have a pandas DataFrame with non-uniformly spaced data points given by an x, y and z column, where x and y are pairs of variables and z is the dependent variable.我有一个由 x、y 和 z 列给出的非均匀间隔数据点的 Pandas DataFrame,其中 x 和 y 是变量对,z 是因变量。 For example:例如:

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']

I want to do a contour plot of the dependent variable z over x and y.我想在 x 和 y 上绘制因变量 z 的等高线图。 For this, I create a new grid to interpolate the data on using matplotlib.mlab's griddata function.为此,我创建了一个新网格来使用 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()

While this works, the output is not what I want.虽然这有效,但输出不是我想要的。 I do not want griddata to interpolate outside of the boundaries given by the min and max values of the x and y data.我不希望 griddata 在 x 和 y 数据的最小值和最大值给出的边界之外进行插值。 The following plots are what shows up after calling plt.show(), and then highlighted in purple what area of the data I want to have interpolated and contoured.以下图是调用 plt.show() 后显示的图,然后以紫色突出显示我想要插值和绘制轮廓的数据区域。 The contour outside the purple line is supposed to be blank.紫色线外的轮廓应该是空白的。 How could I go about masking the outlying data?我怎么能去屏蔽外围数据?

由 mpl 创建的绘图 按原样绘制

The linked question does unfortunately not answer my question, as I don't have a clear mathematical way to define the conditions on which to do a triangulation.不幸的是, 链接的问题没有回答我的问题,因为我没有明确的数学方法来定义进行三角剖分的条件。 Is it possible to define a condition to mask the data based on the data alone, taking the above Dataframe as an example?是否可以单独根据数据定义一个条件来屏蔽数据,以上面的Dataframe为例?

As seen in the answer to this question one may introduce a condition to mask the values.正如在这个问题的答案中所见,可能会引入一个条件来掩盖这些值。

The sentence from the question "I do not want griddata to interpolate outside of the boundaries given by the min and max values of the x and y data."问题中的句子“我不希望 griddata 在 x 和 y 数据的最小值和最大值给定的边界之外进行插值。” implies that there is some min/max condition present, which can be used.意味着存在一些可以使用的最小/最大条件。

Should that not be the case, one may clip the contour using a path.如果不是这种情况,可以使用路径裁剪轮廓。 The points of this path need to be specified as there is no generic way of knowing which points should be the edges.需要指定这条路径的点,因为没有通用的方法来知道哪些点应该是边缘。 The code below does this for three different possible paths.下面的代码针对三种不同的可能路径执行此操作。

import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from matplotlib.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']

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

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)
for i, ax in enumerate(axes):
    cont = ax.contourf(xi, yi, z_grid, 15)
    ax.scatter(x, y, color='k') # The original data points
    ax.plot(x[clipindex[i]], y[clipindex[i]], color="crimson")

    clippath = Path(np.c_[x[clipindex[i]], y[clipindex[i]]])
    patch = PathPatch(clippath, facecolor='none')
    ax.add_patch(patch)
    for c in cont.collections:
        c.set_clip_path(patch)

plt.show()

在此处输入图片说明

Ernest's answer is a great solution, but very slow for lots of contours.欧内斯特的答案是一个很好的解决方案,但对于很多轮廓来说非常慢。 Instead of clipping every one of them, I built a mask by constructing the complement polygon of the desired clipping mask.我没有剪切每一个,而是通过构建所需剪切蒙版的补多边形来构建一个蒙版。

Here is the code based on Ernest's accepted answer:这是基于 Ernest 接受的答案的代码:

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

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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