Matplotlib arrows indicate the wrong path

I create a groundwater elevation path and channel in matplotlib

The contour indicates that the height decreases in many areas, but the groundwater flow (stream) is directed upward. I circled the arrows, which seemed to be pointing in the wrong direction.

The arrows at the bottom of the map appear to be pointing in the right direction. Does anyone know why this could be?

enter image description here

And here is the bulk of the code that 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) 
+5
source share
2 answers

Summary

I suppose, but your problem is probably due to the fact that you have an inherent transposition. 2D numpy arrays are indexed as a row, column. The index "x, y" is a column, a row. In this context, numpy.gradient will mainly return dy, dx, not dx, dy.

Try changing the line:

 dx, dy = np.gradient(zi) 

in

 dy, dx = np.gradient(zi) 

Also, if your depths are defined as positive, this should be:

 dy, dx = np.gradient(-zi) 

However, I assume that you have agreements on positive depth, so I will leave this part of the examples below. (Thus, higher values ​​are expected to be lower / lower in the examples below, and water will flow to higher values.)

Reproducing the problem

For example, if we modify the code that you used to use random data, and fill in a few variables that go beyond the scope of your code sample (so this is a separate 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: enter image description here

Note that there are many places where the flow lines are not perpendicular to the contours. This is even easier than the wrong direction of the arrows, that something is going wrong. (Although the perpendicular implies an aspect ratio of 1 for the graph, which is not entirely true for these graphs unless you set it.)

Problem fix

If we just change the line

 dx, dy = np.gradient(zi) 

in

 dy, dx = np.gradient(zi) 

We get the correct result:

enter image description here


Interpolation Suggestions

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

Firstly, this is not a “smooth” interpolation method. It uses delaunay triangulation, which makes “sharp” ridges at the borders of a triangle. This leads to abnormal gradients in these places.

Secondly, it restricts the 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 interpolation) is a much better choice for interpolation.

As an example, if we modify a piece of code to use 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() 

enter image description here

(You will notice that the intersections are not completely perpendicular due to the uneven aspect ratio of the graph. All of them are 90 degrees if we set the aspect ratio of the graph to 1.)

As a parallel comparison of two methods:

enter image description here

+18
source

You can specify an arrow style with arrowstyle='->' . Try both of them 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='->') 
+1
source

Source: https://habr.com/ru/post/944918/


All Articles