web-dev-qa-db-fra.com

Visualisation des flux en python à l'aide de vecteurs courbes (suivant le chemin)

Je voudrais tracer un champ de vecteurs avec des flèches courbes en python, comme cela peut être fait dans vfplot (voir ci-dessous) ou IDL. 

 Boussinesq flow with curved vectors

Vous pouvez vous rapprocher dans matplotlib, mais utiliser quiver() vous limite aux vecteurs droits (voir ci-dessous à gauche) alors que streamplot() ne semble pas permettre un contrôle significatif de la longueur de la flèche ou de la position de la flèche (voir ci-dessous à droite), même lorsque vous modifiez integration_direction, density et maxlength

 Example matplotlib quiver and stream plots

Alors, y a-t-il une bibliothèque python qui peut faire cela? Ou y a-t-il un moyen d'obtenir que matplotlib le fasse?

15
Kieran Hunt

Si vous regardez le fichier streamplot.py qui est inclus dans matplotlib, aux lignes 196 à 202 (ish, idk si cela a changé entre les versions - je suis sur matplotlib 2.1.2), on constate ce qui suit:

 ... (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)

changer cette partie en ceci fera l'affaire (changer l'affectation de 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)

Si vous modifiez cette option pour placer la flèche à la fin, vous pourrez générer les flèches plus à votre goût.

De plus, à partir de la documentation située en haut de la fonction, nous constatons ce qui suit:

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

La largeur de trait peut être un numpy.ndarray, et si vous pouvez pré-calculer la largeur souhaitée des flèches, vous pourrez modifier la largeur du crayon tout en dessinant les flèches. On dirait que cette partie a déjà été faite pour vous.

Ainsi, en combinaison avec le raccourcissement de la longueur maximale des flèches, l'augmentation de la densité, l'ajout de start_points et la modification de la fonction pour placer la flèche à la fin au lieu du milieu, vous pouvez obtenir le graphique souhaité. 

Avec ces modifications et le code suivant, j'ai pu obtenir un résultat beaucoup plus proche de ce que vous vouliez:

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: va copier le code source et le changer pour mettre les flèches à la fin de chaque chemin, au lieu de les placer au milieu. Ensuite, utilisez votre streamplot au lieu du streamplot matplotlib.

Edit: j'ai les largeurs de lignes à varier

4
David Culbreth

En commençant par de David Culbreth modification, je _ { réécrit } morceaux de la fonction streamplot pour obtenir le comportement souhaité. Un peu trop nombreux pour les spécifier tous ici, mais il inclut une méthode de normalisation de longueur et désactive la vérification de chevauchement de trajectoire. J'ai ajouté deux comparaisons de la nouvelle fonction curved quiver avec les streamplot et quiver d'origine.

enter image description here enter image description here

4
Kieran Hunt

Il suffit de regarder la documentation sur streamplot(), trouvé ici - si vous utilisiez quelque chose comme streamplot( ... ,minlength = n/2, maxlength = n)n est la longueur souhaitée - vous devrez jouer un peu avec ces nombres pour obtenir le graphe souhaité.

vous pouvez contrôler les points à l'aide de start_points, comme indiqué dans l'exemple fourni par @JohnKoch

Voici un exemple de la manière dont j'ai contrôlé la longueur avec streamplot() - il s'agit en fait d'un copier/coller/recadrer directement à partir de exemple depuis ci-dessus.

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: le rendait plus joli, mais pas encore tout à fait ce que nous recherchions.

0
David Culbreth