Matplotlib streamplot arrows pointing the wrong way

青春壹個敷衍的年華 提交于 2019-12-03 03:41:32

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:

You can specify the arrow style with arrowstyle='->'. Try both of these and see if this works for you:

plt.streamplot(xi, yi, dx, dy, color='c', density=1, arrowsize=3, 
               arrowstyle='<-')

plt.streamplot(xi, yi, dx, dy, color='c', density=1, arrowsize=3, 
               arrowstyle='->')
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!