Couleur d'un graphe python

Bonjour, est-ce qu'il existe une méthode pour récupérer la couleur d'un graphe Python ?

Pour le contexte, je trace le graphe d'une suite de fonctions $(f_n)$ pour quelques valeurs de $n$ et je souhaite marquer à chaque fois le point où le maximum est atteint. Je voudrais que ce point soit de la même couleur que le graphe que je trace. Voici un code minimal pour tracer $f_5, f_{10}, f_{15}$ et qui ne me donne pas entière satisfaction :
import matplotlib.pyplot as plt

def f(n, x):
    return(x**n*(1 - x))
    
N = 10**3
X = [k/N for k in range(N)]
for n in [5, 10, 15]:
    Y = [f(n, x) for x in X]
    plt.plot(X, Y)
    plt.plot([n/(n + 1)], [f(n, n/(n + 1))], 'o')
plt.show()        

Je trace classiquement le graphe de chaque $f_n$ et j'ajoute le point d'abscisse $n/(n + 1)$ qui est sur le graphe. Et je voudrais que ce point ait la même couleur que le graphe de $f_n$. J'aurais bien récupéré la couleur du graphe que trace plt.plot(X, Y), puis je l'aurais ajouté dans la liste en dessous. Comme chaque plot(X, Y) est un objet de type list, je crains que ce ne soit pas possible directement ?

Une autre possibilité serait de fixer une liste de couleurs dès le départ et d'attribuer la même à chaque graphe dans un tour de boucle. Mais dans ce cas, je trouve ça un peu plus laborieux car il faut éviter le dépassement du nombre de couleurs que j'aurai dans ma liste (ce qui se fait évidemment).

Autre chose, j'ai vu que je n'avais jamais placé un seul point comme je le fais dans ma boucle. Du coup, je fais comme si j'avais dû placer plein de points comme habituellement, avec une liste d'abscisses et une liste d'ordonnées. Je présume qu'il y a plus malin ?

Réponses

  • Bonjour,

    Je propose ceci (il y a quelques petites améliorations çà et là) :
    #! /usr/bin/env python3
    
    import matplotlib.pyplot as plt
    import numpy as np
    from functools import partial
    
    def f(n, x):
        return(x**n*(1 - x))
    
    N = 10**3
    X = np.linspace(0, 1, N)        # ça va jusqu'à 1 inclus
    
    for n in [5, 10, 15]:
        ff = partial(f, n)
        plt.plot(X, ff(X))
        color = plt.gca().lines[-1].get_color()
        plt.plot([n/(n + 1)], [ff(n/(n + 1))], color=color, marker='o')
    
    plt.show()
    
    122388
  • Merci, ça fait exactement ce que je veux. J'utilise parfois linspace mais je ne connaissais pas partial, et en fait tout le module functools, dont je devrai lire la doc plusieurs fois pour l'utiliser :-D
    En tout cas, partial est effectivement bien pratique pour utiliser linspace facilement dans mon cas.

    Je ne connaissais pas non plus plt.gca() que je ne pense pas comprendre :
    - plt.gca() renvoie les axes d'après ce que je lis : je ne sais pas comment Python gère les axes, mais juste les coordonnées du point de début, du point de fin de chaque axe et la couleur ?
    - la méthode lines renvoie les parties du graphe crées au fur et à mesure ? Et le [-1] sert à obtenir la dernière "ligne" créée pour ensuite prendre sa couleur ?

    Et je suis épaté que color = color ne pose aucun problème dans plot, même si je sais que le premier doit être un argument optionnel de plot. Par sécurité, j'aurais nommé la couleur la couleur renvoyée dans la ligne d'avant avec un autre nom.
  • Je ne pratique pas régulièrement avec matplotlib, donc ne pourrai pas être super précis sur tous les points.

    Un objet Axes contient à peu près tout ce qui fait un graphe, notamment les axes de coordonnées (Axis), les “ticks”, des « artistes » de type Text pour ce qui est écrit avec des caractères, etc. Dans mon exemple, on peut faire à la fin (ici, avec IPython) :
    In [2]: plt.gca().get_children()
    Out[2]: 
    [<matplotlib.lines.Line2D at 0x7f4adb1c1af0>,
     <matplotlib.lines.Line2D at 0x7f4adb1c1e20>,
     <matplotlib.lines.Line2D at 0x7f4adb1d8220>,
     <matplotlib.lines.Line2D at 0x7f4adb1d8550>,
     <matplotlib.lines.Line2D at 0x7f4adb1d88e0>,
     <matplotlib.lines.Line2D at 0x7f4adb1d8c10>,
     <matplotlib.spines.Spine at 0x7f4addd649d0>,
     <matplotlib.spines.Spine at 0x7f4addd64ac0>,
     <matplotlib.spines.Spine at 0x7f4addd64bb0>,
     <matplotlib.spines.Spine at 0x7f4addd64ca0>,
     <matplotlib.axis.XAxis at 0x7f4addd64940>,
     <matplotlib.axis.YAxis at 0x7f4adb2100a0>,
     Text(0.5, 1.0, ''),
     Text(0.0, 1.0, ''),
     Text(1.0, 1.0, ''),
     <matplotlib.patches.Rectangle at 0x7f4adb22a760>]
    
    Les 6 objets matplotlib.lines.Line2D sont :
    • les trois courbes ;
    • les trois points ajoutés... sous forme de courbe réduite à un point.
    Le [-1] de mon exemple sert effectivement à récupérer le dernier artiste matplotlib.lines.Line2D ajouté à l'Axes, c'est-à-dire la dernière courbe tracée. À noter que l'on pourrait aussi utiliser plt.gca().get_lines()[-1] (j'ai trouvé le .lines de mon message précédent sur StackOverflow alors que la méthode Axes.get_lines() est documentée ici).

    Concernant le color=color, s'il fallait éviter tout nom de keyword argument des fonctions et méthodes que l'on appelle, il y aurait souvent des problèmes...
  • Effectivement, éviter le color = color n'est pas très rationnel, mais ça m'a sauté aux yeux dans ton premier message, sans raison finalement.

    Merci pour l'explication, je fais un peu toujours la même chose avec matplotlib et le nombre de possibilités me paraît gigantesque.
Connectez-vous ou Inscrivez-vous pour répondre.