Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何使用matplotlib删除/忽略较小的等高线_Python_Matplotlib_Plot_Matplotlib Basemap - Fatal编程技术网

Python 如何使用matplotlib删除/忽略较小的等高线

Python 如何使用matplotlib删除/忽略较小的等高线,python,matplotlib,plot,matplotlib-basemap,Python,Matplotlib,Plot,Matplotlib Basemap,我试图绘制压力水平的等高线。我使用的是一个netCDF文件,其中包含更高分辨率的数据(范围从3公里到27公里)。由于高分辨率的数据集,我得到了很多不需要绘制的压力值(更确切地说,我不介意忽略一些不重要值的等高线)。我已经根据这个链接中给出的示例编写了一些绘图脚本 绘制后的图像如下所示 从这张图片中,我圈出了一些小的、不需要绘制的轮廓。此外,我还想绘制上图中提到的更平滑的所有轮廓线。总的来说,我希望得到如下轮廓图像:- 我认为可能的解决办法是 找出绘制等高线所需的点的数量,如果这些线数量较少,

我试图绘制压力水平的
等高线。我使用的是一个netCDF文件,其中包含更高分辨率的数据(范围从3公里到27公里)。由于高分辨率的数据集,我得到了很多不需要绘制的压力值(更确切地说,我不介意忽略一些不重要值的等高线)。我已经根据这个链接中给出的示例编写了一些绘图脚本

绘制后的图像如下所示

从这张图片中,我圈出了一些小的、不需要绘制的轮廓。此外,我还想绘制上图中提到的更平滑的所有
轮廓线。总的来说,我希望得到如下轮廓图像:-

我认为可能的解决办法是

  • 找出绘制等高线所需的点的数量,如果这些线数量较少,则遮罩/忽略这些线
  • 找到轮廓的区域(因为我只想省略圆形轮廓)并省略/遮罩较小的区域
  • 通过将距离增加到50 km-100 km来降低分辨率(仅等高线)
  • 我能够使用SO线程成功获得分数

    但我无法使用这些要点实施上述任何建议的解决方案

    我们非常感谢任何实施上述建议解决方案的解决方案

    编辑:-

    @安德拉斯·迪克 我使用
    print'diameter is',diameter
    del(level.get_paths()[kp])
    行检查代码是否过滤掉了所需的直径。以下是我设置直径<15000时的过滤器消息:

    diameter is  9099.66295612
    diameter is  13264.7838257
    diameter is  445.574234531
    diameter is  1618.74618114
    diameter is  1512.58974168
    
    但是,生成的图像没有任何效果。所有的照片看起来都和上面的照片一样。我很确定我已经保存了这个图(在绘制风倒钩之后)

    关于降低分辨率的解决方案,
    plt.contour(x[::2,::2],y[::2,::2],mslp[::2,::2])
    。我必须应用一些过滤器使曲线平滑

    删除行的完整工作示例代码:-

    下面是供您查看的示例代码

    #!/usr/bin/env python
    from netCDF4 import Dataset
    import matplotlib
    matplotlib.use('agg')
    import matplotlib.pyplot as plt
    import numpy as np
    import scipy.ndimage
    from mpl_toolkits.basemap import interp
    from mpl_toolkits.basemap import Basemap
    
    
    # Set default map
    west_lon = 68
    east_lon = 93
    south_lat = 7
    north_lat = 23
    
    nc = Dataset('ncfile.nc')
    # Get this variable for later calucation
    temps = nc.variables['T2']
    time = 0  # We will take only first interval for this example
    # Draw basemap
    m = Basemap(projection='merc', llcrnrlat=south_lat, urcrnrlat=north_lat,
                    llcrnrlon=west_lon, urcrnrlon=east_lon, resolution='l')
    m.drawcoastlines()
    m.drawcountries(linewidth=1.0)
    # This sets the standard grid point structure at full resolution
    x, y = m(nc.variables['XLONG'][0], nc.variables['XLAT'][0])
    
    # Set figure margins
    width = 10
    height = 8
    
    plt.figure(figsize=(width, height))
    plt.rc("figure.subplot", left=.001)
    plt.rc("figure.subplot", right=.999)
    plt.rc("figure.subplot", bottom=.001)
    plt.rc("figure.subplot", top=.999)
    
    plt.figure(figsize=(width, height), frameon=False)
    
    # Convert Surface Pressure to Mean Sea Level Pressure
    stemps = temps[time] + 6.5 * nc.variables['HGT'][time] / 1000.
    mslp = nc.variables['PSFC'][time] * np.exp(9.81 / (287.0 * stemps) * nc.variables['HGT'][time]) * 0.01 + (
        6.7 * nc.variables['HGT'][time] / 1000)
    
    # Contour only at 2 hpa interval
    level = []
    for i in range(mslp.min(), mslp.max(), 1):
        if i % 2 == 0:
            if i >= 1006 and i <= 1018:
                level.append(i)
    
    # Save mslp values to upload to SO thread
    # np.savetxt('mslp.txt', mslp, fmt='%.14f', delimiter=',')
    
    P = plt.contour(x, y, mslp, V=2, colors='b', linewidths=2, levels=level)
    
    
    # Solution suggested by Andras Deak
    for level in P.collections:
        for kp,path in enumerate(level.get_paths()):
            # include test for "smallness" of your choice here:
            # I'm using a simple estimation for the diameter based on the
            #    x and y diameter...
            verts = path.vertices # (N,2)-shape array of contour line coordinates
            diameter = np.max(verts.max(axis=0) - verts.min(axis=0))
    
            if diameter < 15000: # threshold to be refined for your actual dimensions!
                #print 'diameter is ', diameter
                del(level.get_paths()[kp])  # no remove() for Path objects:(
                #level.remove() # This does not work. produces ValueError: list.remove(x): x not in list
    
    plt.gcf().canvas.draw()
    
    
    plt.savefig('dummy', bbox_inches='tight')
    plt.close()
    
    结果如下

    在搜索了几个小时后,我发现这个SO线程存在类似的问题:-


    但是那里提供的解决方案都不管用,上面类似的问题没有合适的解决方案。如果这个问题得到解决,那么代码就是完美和完整的

    这是一个非常糟糕的解决方案,但这是我唯一想到的解决方案。使用链接到的中的
    get\u contour\u verts
    函数,可能与
    matplotlib.\u cntr
    模块一起使用,以便最初不会绘制任何内容。这将为您提供一个等高线、截面、顶点等的列表。然后您必须浏览该列表并
    pop
    不需要的等高线。例如,您可以通过计算最小直径来实现这一点;如果点之间的最大距离小于某个截止值,则将其丢弃

    这将为您留下
    LineCollection
    对象的列表。现在,如果您创建一个
    图形
    实例,则可以使用
    轴。添加集合
    以添加列表中的所有
    线集合

    我很快就查出来了,但似乎很有效。如果有机会的话,我会回来举一个最简单的例子。希望有帮助


    编辑:这里是一个基本想法的MWE。我不熟悉plt.\u cntr.cntr,因此我最终使用plt.contour获得初始轮廓对象。结果,你最终得到了两位数;你只需要关闭第一个。您可以使用任何功能替换
    checkDiameter
    。我想你可以把线段变成一个
    多边形
    ,然后计算面积,但你必须自己计算出来。如果您在使用此代码时遇到问题,请告诉我,但它至少对我有效

    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    
    def checkDiameter(seg, tol=.3):
        # Function for screening line segments. NB: Not actually a proper diameter.
        diam = (seg[:,0].max() - seg[:,0].min(),
                seg[:,1].max() - seg[:,1].min())
        return not (diam[0] < tol or diam[1] < tol)
    
    # Create testing data
    x = np.linspace(-1,1, 21)
    xx, yy = np.meshgrid(x,x)
    z = np.exp(-(xx**2 + .5*yy**2))
    
    # Original plot with plt.contour
    fig0, ax0 = plt.subplots()
    # Make sure this contour object actually has a tiny contour to remove
    cntrObj = ax0.contour(xx,yy,z, levels=[.2,.4,.6,.8,.9,.95,.99,.999])
    
    # Primary loop: Copy contours into a new LineCollection
    lineNew = list()
    for lineOriginal in cntrObj.collections:
        # Get properties of the original LineCollection
        segments = lineOriginal.get_segments()
        propDict = lineOriginal.properties()
        propDict = {key: value for (key,value) in propDict.items()
            if key in ['linewidth','color','linestyle']}  # Whatever parameters you want to carry over
        # Filter out the lines with small diameters
        segments = [seg for seg in segments if checkDiameter(seg)]
        # Create new LineCollection out of the OK segments
        if len(segments) > 0:
            lineNew.append(mpl.collections.LineCollection(segments, **propDict))
    
    # Make new plot with only these line collections; display results
    fig1, ax1 = plt.subplots()
    ax1.set_xlim(ax0.get_xlim())
    ax1.set_ylim(ax0.get_ylim())
    for line in lineNew:
        ax1.add_collection(line)
    plt.show()
    
    将numpy导入为np
    将matplotlib导入为mpl
    将matplotlib.pyplot作为plt导入
    def检查直径(seg,tol=.3):
    #用于筛选线段的功能。注:实际上不是一个合适的直径。
    直径=(seg[:,0].max()-seg[:,0].min(),
    seg[:,1].max()-seg[:,1].min()
    不返回(直径[0]0:
    lineNew.append(mpl.collections.LineCollection(段,**propDict))
    #仅使用这些线集合创建新绘图;显示结果
    图1,ax1=plt.subplot()
    ax1.set_xlim(ax0.get_xlim())
    ax1.set_ylim(ax0.get_ylim())
    对于lineNew中的行:
    ax1.添加_集合(行)
    plt.show()
    
    FYI:带有
    propDict
    的位仅用于自动从原始绘图中引入一些线属性。不过,你不能一次使用整本词典。首先,它包含旧绘图的线段,但您可以将这些线段替换为新线段。但第二,它似乎包含许多相互冲突的参数:多个线宽,
    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    
    def checkDiameter(seg, tol=.3):
        # Function for screening line segments. NB: Not actually a proper diameter.
        diam = (seg[:,0].max() - seg[:,0].min(),
                seg[:,1].max() - seg[:,1].min())
        return not (diam[0] < tol or diam[1] < tol)
    
    # Create testing data
    x = np.linspace(-1,1, 21)
    xx, yy = np.meshgrid(x,x)
    z = np.exp(-(xx**2 + .5*yy**2))
    
    # Original plot with plt.contour
    fig0, ax0 = plt.subplots()
    # Make sure this contour object actually has a tiny contour to remove
    cntrObj = ax0.contour(xx,yy,z, levels=[.2,.4,.6,.8,.9,.95,.99,.999])
    
    # Primary loop: Copy contours into a new LineCollection
    lineNew = list()
    for lineOriginal in cntrObj.collections:
        # Get properties of the original LineCollection
        segments = lineOriginal.get_segments()
        propDict = lineOriginal.properties()
        propDict = {key: value for (key,value) in propDict.items()
            if key in ['linewidth','color','linestyle']}  # Whatever parameters you want to carry over
        # Filter out the lines with small diameters
        segments = [seg for seg in segments if checkDiameter(seg)]
        # Create new LineCollection out of the OK segments
        if len(segments) > 0:
            lineNew.append(mpl.collections.LineCollection(segments, **propDict))
    
    # Make new plot with only these line collections; display results
    fig1, ax1 = plt.subplots()
    ax1.set_xlim(ax0.get_xlim())
    ax1.set_ylim(ax0.get_ylim())
    for line in lineNew:
        ax1.add_collection(line)
    plt.show()
    
    # dummy example based on matplotlib.pyplot.clabel example:
    import matplotlib
    import numpy as np
    import matplotlib.cm as cm
    import matplotlib.mlab as mlab
    import matplotlib.pyplot as plt
    
    delta = 0.025
    x = np.arange(-3.0, 3.0, delta)
    y = np.arange(-2.0, 2.0, delta)
    X, Y = np.meshgrid(x, y)
    Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
    Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
    # difference of Gaussians
    Z = 10.0 * (Z2 - Z1)
    
    
    plt.figure()
    CS = plt.contour(X, Y, Z)
    
    for level in CS.collections:
        for kp,path in reversed(list(enumerate(level.get_paths()))):
            # go in reversed order due to deletions!
    
            # include test for "smallness" of your choice here:
            # I'm using a simple estimation for the diameter based on the
            #    x and y diameter...
            verts = path.vertices # (N,2)-shape array of contour line coordinates
            diameter = np.max(verts.max(axis=0) - verts.min(axis=0))
    
            if diameter<1: # threshold to be refined for your actual dimensions!
                del(level.get_paths()[kp])  # no remove() for Path objects:(
    
    # this might be necessary on interactive sessions: redraw figure
    plt.gcf().canvas.draw()
    
    import numpy as np
    import matplotlib.pyplot as plt
    
    # insert actual data here...
    Z = np.loadtxt('mslp.txt',delimiter=',')
    X,Y = np.meshgrid(np.linspace(0,300000,Z.shape[1]),np.linspace(0,200000,Z.shape[0]))
    p1,p2 = 1006,1018
    
    # this is almost the same as the original, although it will produce
    # [p1, p1+2, ...] instead of `[Z.min()+n, Z.min()+n+2, ...]`
    levels = np.arange(np.maximum(Z.min(),p1),np.minimum(Z.max(),p2),2)
    
    
    #control
    plt.figure()
    CS = plt.contour(X, Y, Z, colors='b', linewidths=2, levels=levels)
    
    
    #modified
    plt.figure()
    CS = plt.contour(X, Y, Z, colors='b', linewidths=2, levels=levels)
    
    for level in CS.collections:
        for kp,path in reversed(list(enumerate(level.get_paths()))):
            # go in reversed order due to deletions!
    
            # include test for "smallness" of your choice here:
            # I'm using a simple estimation for the diameter based on the
            #    x and y diameter...
            verts = path.vertices # (N,2)-shape array of contour line coordinates
            diameter = np.max(verts.max(axis=0) - verts.min(axis=0))
    
            if diameter<15000: # threshold to be refined for your actual dimensions!
                del(level.get_paths()[kp])  # no remove() for Path objects:(
    
    # this might be necessary on interactive sessions: redraw figure
    plt.gcf().canvas.draw()
    plt.show()
    
    import scipy.interpolate as interp   #the new one
    
    # assume you have X,Y,Z,levels defined as before
    
    # start resampling stuff
    dN = 10 # use every dN'th element of the gridded input data
    my_slice = [slice(None,None,dN),slice(None,None,dN)]
    
    # downsampled data
    X2,Y2,Z2 = X[my_slice],Y[my_slice],Z[my_slice]
    # same as X2 = X[::dN,::dN] etc.
    
    # upsampling with griddata over original mesh
    Zsmooth = interp.griddata(np.array([X2.ravel(),Y2.ravel()]).T,Z2.ravel(),(X,Y),method='cubic')
    
    # plot
    plt.figure()
    CS = plt.contour(X, Y, Zsmooth, colors='b', linewidths=2, levels=levels)