Matplotlib streamplot箭头指向错误的方向 [英] Matplotlib streamplot arrows pointing the wrong way

查看:65
本文介绍了Matplotlib streamplot箭头指向错误的方向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在生成地下水高程等值线和matplotlib中的流图



等高线表示在许多区域中高程正在减小,但是指出了地下水流量(流图)上坡。我已经圈了圈箭头,这些箭头似乎指向了错误的方向。



指向地图底部的箭头似乎指向了正确的方向。有人知道为什么会这样吗?





以下是生成此图的大多数代码:

  #create空数组填充! 
x_values = []
y_values = []
z_values = []

#迭代井并在井中填充井数据
well_arr:
x_values.append(well ['xpos'])
y_values.append(well ['ypos'])
z_values.append(well ['value'])

#根据插值函数的要求初始化numpy数组
x = np.array(x_values,dtype = np.float)
y = np.array(y_values,dtype = np.float)
z = np.array(z_values,dtype = np.float)

#创建一个x,y坐标元组列表
points = zip(x,y)

#创建一个可插值数据的网格
xi,yi = np.linspace(0,image ['width'],image ['width']),
np.linspace(0 ,image ['height'],image ['height'])
xi,yi = np.meshgrid(xi,yi)

#使用matlab griddata函数$ b插入数据$ b zi = griddata(x,y,z,xi,yi,interp ='nn')

#创建一个matplotlib图形并调整宽度和高度
fig = plt.figure (figsize =(image ['width'] / 7 2,image ['height'] / 72))

#创建一个子图,如果仅指定了一个子图,则只接管整个图形
ax = fig.add_subplot(111,frameon = False,xticks = [],yticks = [])

#创建轮廓
kwargs = {}
如果ground_contours:
kwargs ['colors'] ='b'

CS = plt.contour(xi,yi,zi,linewidths = linewidth,** kwargs)

#添加一个流图
dx, dy = np.gradient(zi)
plt.streamplot(xi,yi,dx,dy,color ='c',density = 1,arrowize = 3)


解决方案

摘要



我在猜测,但是您的问题可能是因为您进行了固有的移调。 2D numpy数组索引为行,列。 x,y索引是列,行。在这种情况下, numpy.gradient 基本上将返回dy,dx而不是dx,dy。



尝试更改行:

  dx,dy = np.gradient(zi)

至:

  dy,dx = np.gradient (zi)

此外,如果您将深度定义为向上,则应该为:

  dy,dx = np.gradient(-zi)

但是,我假设您具有正负深度约定,因此,我将在下面的示例中保留这一部分。 (因此,在下面的示例数据中,较高的值被假定为较高/较低,并且水将流向较高的值。)



再现问题



例如,如果我们修改您给的代码以使用随机数据并填写一些来自代码示例范围之外的变量(因此这是一个独立的示例):

 导入numpy为np 
import matplotlib.pyplot as plt
从matplotlib.mlab导入griddata

#生成一些可重现但随机的数据
np.random.seed(1981)
宽度,高度= 200,300
x,y,z = np.random .random((3,10))
x * =宽度
y * =高度

#创建一个x,y坐标元组列表
points = zip(x ,y)

#创建一个可插值数据的网格
xi,yi = np.linspace(0,width,width),np.linspace(0,height,height)
xi,yi = np.meshgrid(xi,yi)

#使用matlab网格数据函数
zi =插入数据griddata(x,y,z,xi,yi,interp ='nn')

#创建一个matplotlib图形并调整宽度和高度
fig = plt.figure()

#创建一个子图,如果仅指定一个子图,则仅接管整个图形
ax = fig.add_subplot(111,frameon = False,xticks = [],yticks = [])

#创建轮廓
CS = plt.contour(xi,yi,zi,linewidths = 1,colors ='b')

#添加一个流图
dx,dy = np.gradient(zi)
plt.streamplot(xi,yi,dx,dy,color ='c',density = 1,arrowize = 3)

plt.show()

结果将如下所示:



请注意,有很多地方流线不垂直于轮廓。这比错误的箭头方向更容易指示。 (尽管垂直假定该图的宽高比为1,除非您设置了该比例,否则对于这些图来说并不是真的。)



解决问题



如果我们只是换行

  dx,dy = np.gradient (zi)

至:

  dy,dx = np.gradient(zi)

我们得到正确的结果:








插值建议



在旁注中,<$在这种情况下,c $ c> griddata 是一个糟糕的选择。



首先,它不是平滑插值方法。它使用delaunay三角剖分法,在三角形边界处形成尖锐的山脊。这会导致这些位置出现异常梯度。



第二,它将插值限制为数据点的凸包,这可能是一个好选择,也可能不是一个好选择。



径向基函数(或任何其他平滑插值)是插值的更好选择。



例如,如果我们修改您的代码段以使用RBF:

  import numpy as np 
import matplotlib.pyplot as plt
from scipy.interpolate import Rbf

#生成数据
np.random.seed(1981 )
宽度,高度= 200,300
x,y,z = np.random.random((3,10))
x * =宽度
y * =高度

#创建一个可插值数据的网格
xi,yi = np.mgrid [0:width:1j * width,0:height:1j * height]

#使用matlab网格数据函数插值数据
interp = Rbf(x,y,z,function ='linear')
zi = interp(xi,yi)

#创建一个matplotlib图形并调整宽度和高度
图,ax = plt.subplots(subplot_kw = dict(frameon = False,xticks = [],yticks = []))

#创建轮廓并绘制流线图
CS = plt.contour(xi,yi,zi,线宽= 1,colors ='b')
dy,dx = np.gradient(zi.T)
plt.streamplot(xi [:,0],yi [0 ,:],dx,dy,color ='c',density = 1,arrowize = 3)

plt .show()



(您会注意到,由于绘图的长宽比不相等,所以交点不太垂直。如果将图的纵横比设置为1,它们都是90度。)



作为这两种方法的并排比较: / p>


I am generating a groundwater elevation contour and a streamplot in matplotlib

The contour indicates that the elevation is decreasing in many areas but the groundwater flow (streamplot) is pointed uphill. I have circled the arrows that seem to be pointed the wrong direction.

The arrows toward the bottom of the map appear to be pointed the correct direction. Does anyone know why this might be?

And here is most of the code which generates this plot:

#create empty arrays to fill up!
x_values = []
y_values = []
z_values = []

#iterate over wells and fill the arrays with well data
for well in well_arr:
    x_values.append(well['xpos'])
    y_values.append(well['ypos'])
    z_values.append(well['value'])

#initialize numpy array as required for interpolation functions
x = np.array(x_values, dtype=np.float)
y = np.array(y_values, dtype=np.float)
z = np.array(z_values, dtype=np.float)

#create a list of x, y coordinate tuples
points = zip(x, y)

#create a grid on which to interpolate data
xi, yi = np.linspace(0, image['width'], image['width']),
         np.linspace(0, image['height'], image['height'])
xi, yi = np.meshgrid(xi, yi)

#interpolate the data with the matlab griddata function
zi = griddata(x, y, z, xi, yi, interp='nn')

#create a matplotlib figure and adjust the width and heights
fig = plt.figure(figsize=(image['width']/72, image['height']/72))

#create a single subplot, just takes over the whole figure if only one is specified
ax = fig.add_subplot(111, frameon=False, xticks=[], yticks=[])

#create the contours
kwargs = {}
if groundwater_contours:
    kwargs['colors'] = 'b'

CS = plt.contour(xi, yi, zi, linewidths=linewidth, **kwargs)

#add a streamplot
dx, dy = np.gradient(zi)
plt.streamplot(xi, yi, dx, dy, color='c', density=1, arrowsize=3)

解决方案

Summary

I'm guessing, but your problem is probably because you have an inherent transpose going on. 2D numpy arrays are indexed as row, column. "x, y" indexing is column, row. In this context, numpy.gradient is basically going to return dy, dx and not dx, dy.

Try changing the line:

dx, dy = np.gradient(zi)

to:

dy, dx = np.gradient(zi)

Also, if your depths are defined as positive-up, it should be:

dy, dx = np.gradient(-zi)

However, I'm assuming you have positive-down depth conventions, so I'll leave that part of of the examples below. (So higher values are assumed to be deeper/lower in the example data below, and water will flow towards the high values.)

Reproducing the problem

For example, if we modify the code you gave to use random data and fill in a few variables that are coming from outside the scope of your code sample (so that it's a stand-alone example):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata

# Generate some reproducible but random data
np.random.seed(1981)
width, height = 200, 300
x, y, z = np.random.random((3,10))
x *= width
y *= height

#create a list of x, y coordinate tuples
points = zip(x, y)

#create a grid on which to interpolate data
xi, yi = np.linspace(0, width, width), np.linspace(0, height, height)
xi, yi = np.meshgrid(xi, yi)

#interpolate the data with the matlab griddata function
zi = griddata(x, y, z, xi, yi, interp='nn')

#create a matplotlib figure and adjust the width and heights
fig = plt.figure()

#create a single subplot, just takes over the whole figure if only one is specified
ax = fig.add_subplot(111, frameon=False, xticks=[], yticks=[])

#create the contours
CS = plt.contour(xi, yi, zi, linewidths=1, colors='b')

#add a streamplot
dx, dy = np.gradient(zi)
plt.streamplot(xi, yi, dx, dy, color='c', density=1, arrowsize=3)

plt.show()

The result will look like this:

Notice that there are lots of places where the flow lines are not perpendicular to the contours. That's an even easier indicator than the incorrect direction of the arrows that something is going wrong. (Though "perpendicular" assumes an aspect ratio of 1 for the plot, which isn't quite true for these plots unless you set it.)

Fixing the problem

If we just change the line

dx, dy = np.gradient(zi)

to:

dy, dx = np.gradient(zi)

We'll get the correct result:


Interpolation suggestions

On a side note, griddata is a poor choice in this case.

First, it's not a "smooth" interpolation method. It uses delaunay triangulation, which makes "sharp" ridges at triangle boundaries. This leads to anomalous gradients in those locations.

Second, it limits interpolation to the convex hull of your data points, which may or may not be a good choice.

A radial basis function (or any other smooth interpolant) is a much better choice for interpolation.

As an example, if we modify your code snippet to use an RBF:

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import Rbf

# Generate data
np.random.seed(1981)
width, height = 200, 300
x, y, z = np.random.random((3,10))
x *= width
y *= height

#create a grid on which to interpolate data
xi, yi = np.mgrid[0:width:1j*width, 0:height:1j*height]

#interpolate the data with the matlab griddata function
interp = Rbf(x, y, z, function='linear')
zi = interp(xi, yi)

#create a matplotlib figure and adjust the width and heights
fig, ax = plt.subplots(subplot_kw=dict(frameon=False, xticks=[], yticks=[]))

#create the contours and streamplot
CS = plt.contour(xi, yi, zi, linewidths=1, colors='b')
dy, dx = np.gradient(zi.T)
plt.streamplot(xi[:,0], yi[0,:], dx, dy, color='c', density=1, arrowsize=3)

plt.show()

(You'll notice the intersections are not quite perpendicular due to the non-equal aspect ratio of the plot. They're all 90 degrees if we set the aspect ratio of the plot to 1, however.)

As a side-by-side comparison of the two methods:

这篇关于Matplotlib streamplot箭头指向错误的方向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆