it-swarm.com.de

Flussvisualisierung in Python mit gekrümmten Vektoren

Ich möchte ein Vektorfeld mit gekrümmten Pfeilen in Python zeichnen, wie dies in vfplot (siehe unten) oder IDL möglich ist. 

 Boussinesq flow with curved vectors

In matplotlib können Sie sich näher bringen, aber mit quiver() sind Sie auf gerade Vektoren beschränkt (siehe unten links), während streamplot() keine aussagekräftige Kontrolle über die Länge des Pfeils oder die Pfeilspitze (siehe unten rechts) erlaubt, selbst wenn integration_direction, density und geändert wird maxlength

 Example matplotlib quiver and stream plots

Gibt es eine Python-Bibliothek, die das kann? Oder gibt es eine Möglichkeit, Matplotlib dazu zu bringen?

15
Kieran Hunt

Wenn Sie in matplotlib die Datei streamplot.py betrachten, die sich in den Zeilen 196 - 202 befindet (ish, idk, falls sich dies zwischen den Versionen geändert hat - ich bin in matplotlib 2.1.2), sehen wir Folgendes:

 ... (to line 195)
    # Add arrows half way along each trajectory.
    s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
    n = np.searchsorted(s, s[-1] / 2.)
    arrow_tail = (tx[n], ty[n])
    arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
 ... (after line 196)

wenn Sie diesen Teil ändern, wird der Trick ausgeführt (Ändern der Zuweisung von n):

 ... (to line 195)
    # Add arrows half way along each trajectory.
    s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
    n = np.searchsorted(s, s[-1]) ### THIS IS THE EDITED LINE! ###
    arrow_tail = (tx[n], ty[n])
    arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
 ... (after line 196)

Wenn Sie dies ändern, um den Pfeil am Ende zu platzieren, können Sie die Pfeile nach Ihren Wünschen generieren.

Darüber hinaus sehen Sie in den Dokumenten oben in der Funktion Folgendes:

*linewidth* : numeric or 2d array
        vary linewidth when given a 2d array with the same shape as velocities.

Die Linienbreite kann ein numpy.ndarray sein. Wenn Sie die gewünschte Breite der Pfeile vorberechnen können, können Sie die Stiftbreite beim Zeichnen der Pfeile ändern. Es sieht so aus, als ob dieser Teil bereits für Sie erledigt wurde.

In Kombination mit dem Verkürzen der Pfeile maxlength, dem Erhöhen der Dichte und dem Hinzufügen von Startpunkten sowie dem Optimieren der Funktion, um den Pfeil anstelle der Mitte an das Ende zu setzen, können Sie den gewünschten Graphen erhalten. 

Mit diesen Änderungen und dem folgenden Code konnte ich ein Ergebnis viel näher an das heranbringen, was Sie wollten:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat

w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)

fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])

grains = 10
tmp = Tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
    xs += x
ys = Tuple(np.linspace(-2, 2, grains))*grains


seed_points = np.array([list(xs), list(ys)])
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])

strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=np.array(5*np.random.random_sample((100, 100))**2 + 1), cmap='winter', density=10,
                      minlength=0.001, maxlength = 0.07, arrowstyle='fancy',
                      integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')

plt.tight_layout()
plt.show()

 sample matplotlib graph

tl; dr: go kopiert den Quellcode und ändert ihn, um die Pfeile am Ende jedes Pfades anstatt in die Mitte zu setzen. Verwenden Sie dann Ihr Streamplot anstelle des Matplotlib-Streamplots.

Edit: Ich habe die Linienbreiten zu variieren

4
David Culbreth

Beginnend mit David Culbreths - Modifikation, schrieb ich Chunks der streamplot-Funktion um, um das gewünschte Verhalten zu erreichen. Etwas zu zahlreich, um sie alle hier anzugeben, aber sie enthält eine Methode zur Normalisierung der Länge und deaktiviert die Überprüfung der Bahnüberlappung. Ich habe zwei Vergleiche der neuen curved quiver-Funktion mit den ursprünglichen streamplot und quiver angehängt.

enter image description here enter image description here

4
Kieran Hunt

Wenn Sie sich die Dokumentation zu streamplot() ansehen, finden Sie here - Wenn Sie etwas wie streamplot( ... ,minlength = n/2, maxlength = n) verwenden, wobei n die gewünschte Länge ist, müssen Sie ein wenig mit diesen Zahlen spielen, um die gewünschte Grafik zu erhalten

sie können die Punkte mit start_points steuern, wie im Beispiel von @JohnKoch gezeigt

Hier ist ein Beispiel, wie ich die Länge mit streamplot() gesteuert habe - es ist so ziemlich ein einfaches Kopieren/Einfügen/Ernten aus dem Beispiel von oben.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat

w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)

fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])

grains = 10
tmp = Tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
    xs += x
ys = Tuple(np.linspace(-2, 2, grains))*grains


seed_points = np.array([list(xs), list(ys)])
arrowStyle = pat.ArrowStyle.Fancy()
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])
strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=1.5, cmap='winter', density=10,
                      minlength=0.001, maxlength = 0.1, arrowstyle='->',
                      integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')

plt.tight_layout()
plt.show()

Edit: hat es schöner gemacht, aber immer noch nicht ganz das, wonach wir gesucht haben.

0
David Culbreth