Créez vous-même le tracé familier du flux de rayons X sur 3 jours de NOAA / GOES avec Python

Créez vous-même le tracé familier du flux de rayons X sur 3 jours de NOAA / GOES avec Python

** Remarque: il contient un contenu assez spécialisé, comme les données utilisées. Je laisserai l'explication de la physique et des termes à la littérature appropriée. Pour ceux qui sont venus voir à partir de la recherche, etc., l'explication de certains modules est donnée à partir de [4. Explication de chaque partie de code](# 4-Explication de chaque partie de code), veuillez donc vous y référer. ** ** Les gens du même secteur sont conscients de la réinvention des roues, mais s'il existe une méthode plus innovante, faites-le nous savoir. Cependant, le départ d'IDL vers Python est l'une des principales hypothèses.

1. Origines

1.1. Quoi et pourquoi prenez-vous la peine de le fabriquer vous-même

Je ferai moi-même la figure suivante, que j'aurais dû voir quelque part si je faisais des recherches liées au soleil. ** Le chiffre que vous avez fait est dans [5. En fait dessiner](# 5-En fait dessiner), alors jetez-y un coup d'œil seul. ** ** goes_xrays_20170906.png

Récemment ~~ (je ne l'ai pas vu récemment donc je ne sais pas quand) ~~, la page GOES Xray flux est interactive Il s'est transformé en graphique. Ce n'est pas un problème, mais `` Il est vrai que la valeur est facile à voir, mais honnêtement, il n'y a aucune possibilité d'avoir besoin d'informations en temps réel sur l'heure et la minute du flux. ―― Le profil temporel des rayons X de ce format étant utilisé depuis longtemps, de nombreuses personnes sont soulagées de voir l'image de ce format.

1.2. Connaissances préalables

Dans cet article, dans cet article 2Fqiita-image-store.s3.amazonaws.com% 2F0% 2F222849% 2F1e675340-79ff-4aa1-1891-b79d3c369eb3.png? Ixlib = rb-1.2.2 & auto = format & gif-q = 60 & q = 75 & s = 3ea10511bee8bd J'utilise souvent le mot «taxes» qui apparaît dans. Si votre objectif est de comprendre le contenu et le code suivants, nous vous recommandons de lire la «Figure» et les «Axes» avec une compréhension rapide.

De plus, comme décrit dans la préface, les termes techniques ne sont pas expliqués. Notez s'il vous plaît. J'expliquerai les modules, etc. sans termes techniques autant que possible.

2. Créer un programme

2.1. Importation de modules

Cette fois, j'ai importé divers modules que j'utilise pour la première fois afin de coller à l'apparence.

import numpy as np
import datetime,calendar,requests
import pandas as pd
from io import StringIO

import matplotlib
%matplotlib inline ##Pour dessiner sur le notebook Jupyter

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.transforms as transforms
from matplotlib.dates import DateFormatter
from matplotlib.dates import date2num
from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker, VPacker

2.2. Localisation des données

Fondamentalement, toutes les données jusqu'à GOES 15 sont ouvertes au public. Par exemple, en septembre 2017, les données moyennes GOES-15 X-ray 1 minute sont au format csv [ici](https://satdat.ngdc.noaa.gov/sem/goes/data/avg/2017/09/goes15 Il se trouve dans /csv/g15_xrs_1m_20170901_20170930.csv). Cette fois, nous allons procéder pour que nous puissions créer des graphiques sans télécharger de fichiers localement.

À ce stade, nous supposerons que vous connaissez les nombres primaires GOES et secondaires GOES lorsque vous souhaitez créer le graphique. Je suis sûr que la commande IDL contenait des informations primaires, je les ajouterai donc un jour si possible.

Pour créer un tracé de 3 jours, reportez-vous d'abord aux données d'origine. Les données radiographiques moyennes sur 1 minute du GOES sont publiées au format csv chaque mois, donc soyez prudent lorsque vous vous référez aux données sur une base mensuelle.

def read_csv_skiprows(url):
    resp = requests.get(url)
    data_text = resp.text
    
    is_skip, skip_row = False,1
    for line in data_text.split('\n'):##Divisez chaque ligne'data:'rechercher
        if 'data:' in line:
            is_skip = True
            break
        skip_row += 1

    if not is_skip:
        skip_row = 0
    return pd.read_csv(StringIO(data_text),skiprows=skip_row)

def get_goes_csv(goesnum,date,trange=3):
    ##Premier rendez-vous sur la parcelle:tstart
    if type(date)==list:
        ts = datetime.date(date[0],date[1],date[2])
    elif (type(date)==datetime.date)or(type(date)==datetime.datetime):
        ts = date
    ##Dernière date sur la parcelle:tend
    te = ts+datetime.timedelta(days=trange)
    url = []
    if ts.month!=te.month: ##Si le graphique s'étend sur des mois
        for i,t in enumerate([ts,te]): ##Obtenir l'index avec enumerate
            ##Obtenez le dernier jour de chaque mois
            last_day = calendar.monthrange(t.year,t.month)[1]
            url.append('https://satdat.ngdc.noaa.gov/sem/goes/data/avg/{:04}/{:02}/goes{:02}/csv/g{:02}_xrs_1m_{:04}{:02}{:02}_{:02}{:02}{:02}.csv'.format(t.year,t.month,goesnum,goesnum,t.year,t.month,1,t.year,t.month,last_day))
            if i==0:
                d = read_csv_skiprows(url[i])
            else: ##Cette fois, on suppose que le mois ne sera pas traversé plus d'une fois.
                df = pd.concat([d,read_csv_skiprows(url[i])])
            
    else:
        last_day = calendar.monthrange(ts.year,ts.month)[1]
        url.append('https://satdat.ngdc.noaa.gov/sem/goes/data/avg/{:04}/{:02}/goes{:02}/csv/g{:02}_xrs_1m_{:04}{:02}{:02}_{:02}{:02}{:02}.csv'\
                    .format(ts.year,ts.month,goesnum,goesnum,ts.year,ts.month,1,ts.year,ts.month,last_day))
        df = read_csv_skiprows(url[0])
    
    ##La colonne de date étant de type str, remplacez-la par un type qui peut gérer l'heure
    df.time_tag = pd.to_datetime(df.time_tag)
    ##Renvoie les données entre ts et te
    df_obj = df[(df.time_tag>=ts)&(df.time_tag<=te)]
    return df_obj

Bien qu'il soit dit au format csv, cela peut être un peu ennuyeux, donc ici Comme vous pouvez le voir, les données souhaitées commencent par le bas et la première partie décrit l'explication des données. Read_csv of pandas peut sauter à la ligne cible en spécifiant l'argument de skiprows ou header quand il y a une telle description, mais ce qui est gênant est le fichier (ou l'observation Le nombre de lignes pour atteindre les données varie en fonction de l'heure).
Cependant, comme il y a une description de `` 'data:' 'juste avant les données, trouvez le nombre de lignes à sauter avec ceci comme cible. Cette méthode était auparavant enseignée à teratail.

Avec le traitement ci-dessus, les données de rayons X GOES peuvent être acquises. Si vous souhaitez créer votre propre tracé de rayons X GOES pour le moment, ajoutez des données GOES à Sunpy [^ 1] Puisqu'il y a une commande à obtenir, vous pouvez également l'utiliser. [^ 1]: Cette partie ose faire référence à l'ancienne version du document. Parce que, pour une raison quelconque, le chiffre n'est pas affiché dans la dernière version du document Parce que c'est difficile.

Cependant, l'inconvénient est que vous ne pouvez obtenir des données que d'un seul satellite à la fois (même si cela peut être une surestimation car ce n'est pas un problème dans la plupart des cas). Par exemple, lors de la fusée éclairante du 6 septembre 2017, les données de éruption qui s'est produite à 8h57 sont juste Il est tombé dans GOES-15, et il est nécessaire de le compléter avec les données de GOES-13. Compte tenu de ce cas, il vaudrait peut-être mieux aller à la NOAA.

3. Code complété

De la conclusion, j'ai pu sortir avec le code suivant. C'est très long. Partiellement expliqué dans [4. Explication de chaque partie de code](# 4-Explication de chaque partie de code). Si vous voulez un graphique pour le moment, passez à [5. En fait dessiner](# 5-En fait dessiner).

plt.rcParams['font.family'] = 'Simplex'
plt.rcParams['font.size'] = 10
plt.rcParams['figure.dpi'] = 100

plt.rcParams['axes.linewidth'] = 1

plt.rcParams['lines.antialiased'] = False
plt.rcParams['text.antialiased'] = False
plot_antialiased = False

##Tout en unités de pixels
wdict = dict(
    grid  = 1, ##Largeur de ligne de grille
    spine = 1, ##Largeur de la ligne du cadre
    tick  = 1, ##Échelle de la largeur de la ligne
    line  = 0.01, ##Largeur de ligne du graphique
)
##Convertir un pixel en point
def px2pt(px):
    dpi = plt.rcParams['figure.dpi']
    return px*72/dpi

def plot_goes(t, trange ,goes ,savename, wdict, width=6.4, ylim=(10**-9,10**-2)):
    '''
    t      :Date de début du tracé(type=datetime.date or list)
             (e.g. datetime.date(2017,9,4) or [2017,9,4]) 
    trange :Période de tracé Quotidien(type=int) (e.g. trange=3 > 3 days plot)
    goes   :Format de liste de numéros de satellite GOES à acquérir(e.g. [15,13])
             index =0 est primaire(1-8A>red, 0.5-4A>blue)
             index =1 est secondaire(1-8A>yellow, 0.5-4A>purple)
    width  :Largeur de tracé par défaut=6.4
             if it is 'extend', extend the width with trange
    ylim   :par défaut de la plage de l'axe y=(10**-9,10**-2)
Probablement pas nécessaire de changer, mais conçu pour pouvoir changer
    '''
    if type(t)==list:
        t = datetime.date(t[0],t[1],t[2])
    elif (type(t)==datetime.date)or(type(t)==datetime.datetime):
        pass
    else:
        print('t should be a list or a datetime object.')
    
    fs_def = plt.rcParams['font.size']
    dpi    = plt.rcParams['figure.dpi']
    y0_fig = plt.rcParams['figure.subplot.bottom']
    y1_fig = plt.rcParams['figure.subplot.top']
    
    ##Réglage de la largeur
    if width=='extend':
        width = (dpi*6.4 + (dpi*6.4 - fs_def*10)/3*(trange-3))/dpi
    elif type(width)==float:
        width = width
    else:
        print('width is \'extend\' or float')

    ##Les marges gauche et droite correspondent à la taille de la police* 10 (Rapport au chiffre)
    margin = fs_def/(100*width)*10

    ##Largeur par jour(Rapport au chiffre)
    w_day  = (1-2*margin)/trange
    ax = []

    ##Cadre(axes)Création
    fig = plt.figure(figsize=(width,4.8))
    for i in range(trange):
        t_s = t + datetime.timedelta(days=i)
        t_e = t_s + datetime.timedelta(days=1)

        if i==0: ##Premier jour
            ax.append(fig.add_axes([margin+i*w_day,y0_fig,w_day,(y1_fig-y0_fig)],\
                                   yscale='log', xlim=[t_s,t_e], ylim=ylim, zorder=-i))
            new_xticks = date2num([t_s,t_e])

        else: ##Après le deuxième jour
            ax.append(fig.add_axes([margin+i*w_day,y0_fig,w_day,(y1_fig-y0_fig)],\
                                   yscale='log', xlim=[t_s,t_e], ylim=ylim, yticklabels=[], zorder=-i))
            new_xticks = date2num([t_e])

        ax[i].xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
        ax[i].xaxis.set_major_formatter(DateFormatter('%b %d'))
        ax[i].xaxis.set_minor_locator(matplotlib.dates.HourLocator(interval=3))
        ax[i].yaxis.grid(lw=px2pt(wdict['grid']),color='black')

        ax[i].tick_params(axis='x', which='major', bottom=True, top=True, direction='in', length=6, width=px2pt(wdict['tick']), pad=fs_def)
        ax[i].tick_params(axis='x', which='minor', bottom=True, top=True, direction='in', length=3, width=px2pt(wdict['tick']))

        for j in ax[i].spines.keys(): ##Ajustement de la largeur de ligne du cadre
            ax[i].spines[j].set_linewidth(px2pt(wdict['spine']))
        ##Ajuster l'échelle
        if i==0: ##Premier jour
            ax[i].tick_params(axis='y', which='both', right=True, left=True, direction='in', length=6, width=px2pt(wdict['tick']))
            ax[i].spines['right'].set_visible(False)
        elif i==trange-1: ##Dernier jour
            ax[i].tick_params(axis='y', which='both', right=True, left=False, direction='in', length=6, width=px2pt(wdict['tick']))
            ax[i].spines['left'].set_visible(False)            
        else: ##Autre
            ax[i].tick_params(axis='y', which='both', right=True, left=False, direction='in', length=6, width=px2pt(wdict['tick']))
            ax[i].spines['left'].set_visible(False)
            ax[i].spines['right'].set_visible(False)


    ## Flare class text
    f_pos = ax[-1].get_position()

    trans = transforms.blended_transform_factory(fig.transFigure,transforms.IdentityTransform())
    X10_y_display   = ax[-1].transData.transform((date2num(t_e),10**-3))[1]
    X_y_display     = ax[-1].transData.transform((date2num(t_e),10**-4))[1]
    w = X10_y_display - X_y_display

    ##Taille relative de la taille de la police par rapport à la figure entière
    ##Utilisé pour autoriser les marges lors du placement du texte de classe afin qu'il ne colle pas au cadre extérieur
    cls_text_size = plt.rcParams['font.size']/(plt.rcParams['figure.figsize'][0])/72

    for i,cls in enumerate(['X', 'M', 'C', 'B', 'A']):
        pos = [f_pos.x1,X10_y_display - w/2 - i*w]
        if (pos[1]>=ax[-1].transData.transform((date2num(t_e),ax[-1].get_ylim()[0]))[1])&\
           (pos[1]<=ax[-1].transData.transform((date2num(t_e),ax[-1].get_ylim()[1]))[1]):
            ax[-1].text(pos[0]+cls_text_size/5,pos[1],cls,horizontalalignment='left',verticalalignment='center',transform=trans)

    ## GOES number and wavelength
    ybox_b = TextArea('GOES{} 0.5-4.0 A'.format(goes[0]), textprops=dict(color='#001DFF', size='large' ,rotation=90,ha='left',va='bottom')) #Bleu
    ybox_r = TextArea('GOES{} 1.0-8.0 A'.format(goes[0]), textprops=dict(color='#FF0000', size='large' ,rotation=90,ha='left',va='bottom')) #rouge
    ybox_primary = VPacker(children=[ybox_r, ybox_b],align='bottom', pad=0, sep=2*fs_def)
    if len(goes)==2:
        ybox_p = TextArea('GOES{} 0.5-4.0 A'.format(goes[1]), textprops=dict(color='#5400A8', size='large' ,rotation=90,ha='left',va='bottom')) #violet
        ybox_y = TextArea('GOES{} 1.0-8.0 A'.format(goes[1]), textprops=dict(color='#FFA500', size='large' ,rotation=90,ha='left',va='bottom')) #Jaune
        ybox_secondary = VPacker(children=[ybox_y, ybox_p],align='bottom', pad=0, sep=2*fs_def)
        ybox = HPacker(children=[ybox_primary,ybox_secondary],align='bottom', pad=0, sep=fs_def)
        anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.25, 0.5), 
                                          bbox_transform=fig.transFigure, borderpad=0.)
    else:
        ybox = ybox_primary
        anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.5, 0.5), 
                                          bbox_transform=fig.transFigure, borderpad=0.)
    ax[-1].add_artist(anchored_ybox)

    ##Création d'étiquettes d'axe(dummy axes)
    ax_dummy = fig.add_axes([margin,y0_fig,1-margin*2,y1_fig-y0_fig],frameon=False)
    ax_dummy.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
    ax_dummy.set_xlabel('Universal Time',size='large')
    ax_dummy.set_ylabel('Watts m$^{-2}$',size='large')
    ax_dummy.set_title('GOES Xray Flux (1-minute data)',size='x-large')

    ax_dummy.text(1, 1-cls_text_size/5, 'Begin: {} {} {} 0000 UTC'.format(t.year,t.strftime('%b'),t.day),
            horizontalalignment='right',
            verticalalignment='top',
            transform=fig.transFigure)
    ## X-ray data plot
    color = iter(['#FF0000','#001DFF','#FFA500','#5400A8']) ## red, blue, yellow, purple 
    zo = iter([4,3,2,1])
    for g in goes:
        wavelength = iter(['1.0-8.0 A','0.5-4.0 A'])
        data = get_goes_csv(g,t,trange)
        xray_long  = np.ma.masked_where((data.B_AVG<=ylim[0])|(data.B_AVG>=ylim[1]),data.B_AVG)
        xray_short = np.ma.masked_where((data.A_AVG<=ylim[0])|(data.A_AVG>=ylim[1]),data.A_AVG)
        ax[0].plot(data.time_tag,xray_long, lw=px2pt(wdict['line']),c=next(color),label='GOES{} 1.0-8.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))
        ax[0].plot(data.time_tag,xray_short,lw=px2pt(wdict['line']),c=next(color),label='GOES{} 0.5-4.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))
        
    ## save graph
    fig.savefig(savename)

4. Explication de chaque partie du code

Je présenterai chaque partie. Si vous souhaitez pouvoir l'utiliser pour le moment, veuillez l'ignorer.

4.1. Police

Selon ici, la police utilisée dans IDL est Simplex Roman. Cette fois, j'étais particulier à propos de l'apparence, donc je l'ai téléchargé depuis Site de polices gratuites et je l'ai utilisé. Il n'a pas été installé en standard sous Mac OS, donc si vous souhaitez vous y tenir, veuillez le télécharger. Si tu n'en as pas besoin

plt.rcParams['font.family'] = 'Arial'

Je pense que ce sera un graphique facile à lire si vous le définissez comme tel.

4.2. Alias

plt.rcParams['lines.antialiased'] = False
plt.rcParams['text.antialiased'] = False
plot_antialiased = False

Lorsque matplotlib sort au format bitmap tel que png ou jpg, il complète et effectue automatiquement l'anti-aliasing afin qu'il n'y ait pas de dentelure. Si vous dessinez un diagramme normalement, vous n'avez pas à vous en soucier, mais cette fois, j'étais particulier à ce sujet. Si vous définissez tous les paramètres ci-dessus sur True, l'anti-aliasing sera effectué, et je pense que c'est plus sûr. De plus, il semble qu'il n'y ait aucun moyen d'annuler l'anti-aliasing de tick.

4.3. Conversion de pixel et de point

def px2pt(px):
    dpi = plt.rcParams['figure.dpi']
    return px*72/dpi

C'est aussi un engagement inutile. Dans l'image d'origine, la largeur de chaque ligne était spécifiée comme 1 pixel, je l'ai donc créée pour cela. Parce que, l'unité lors de la spécification de la largeur, etc. avec matplotlib est tout point. 1 point correspond à 1/72 de 1 pouce et la relation entre le pouce et le pixel change en fonction du dpi, ce qui est corrigé.

4.4. Réglage de la largeur

if width=='extend':
    width = (dpi*6.4 + (dpi*6.4 - fs_def*10)/3*(trange-3))/dpi
elif type(width)==float:
    width = width
else:
    print('width is \'extend\' or float')

L'image d'origine était de 480 pixels de hauteur et 640 pixels de largeur, donc les bases sont que (ppp est de 100). Cependant, si vous souhaitez créer un tracé de plus de 3 jours, vous pouvez spécifier width == 'extend' pour augmenter la taille horizontale tout en gardant la largeur par jour identique à celle du tracé de 3 jours. Fait. De plus, si vous spécifiez normalement la largeur avec le type float, elle est définie sur cette taille, mais le fonctionnement ne peut pas être garanti. Si vous y réfléchissez bien, si vous créez une intrigue de moins de 3 jours, une erreur sera lancée, donc je la décrirai plus tard ([6. Draw Simple](# 6-Draw Simple)).

4.5. Paramètres de marge

##Les marges gauche et droite correspondent à la taille de la police* 10 (Rapport au chiffre)
margin = fs_def/(100*width)*10

##Largeur par jour(Rapport au chiffre)
w_day  = (1-2*margin)/trange

Définissez les marges gauche et droite. Comme vous pouvez le voir dans le commentaire, je l'ai défini sur environ 10 fois la taille de la police.

4.6. Réglage du cadre

##Cadre(axes)Création
fig = plt.figure(figsize=(width,4.8))
for i in range(trange):
    t_s = t + datetime.timedelta(days=i)
    t_e = t_s + datetime.timedelta(days=1)

    if i==0: ##Premier jour
        ax.append(fig.add_axes([margin+i*w_day,y0_fig,w_day,(y1_fig-y0_fig)],\
                               yscale='log', xlim=[t_s,t_e], ylim=ylim, zorder=-i))
        new_xticks = date2num([t_s,t_e])

    else: ##Après le deuxième jour
        ax.append(fig.add_axes([margin+i*w_day,y0_fig,w_day,(y1_fig-y0_fig)],\
                               yscale='log', xlim=[t_s,t_e], ylim=ylim, yticklabels=[], zorder=-i))
        new_xticks = date2num([t_e])

    ax[i].xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
    ax[i].xaxis.set_major_formatter(DateFormatter('%b %d'))
    ax[i].xaxis.set_minor_locator(matplotlib.dates.HourLocator(interval=3))
    ax[i].yaxis.grid(lw=px2pt(grid_width),color='black')

    ax[i].tick_params(axis='x', which='major', bottom=True, top=True, direction='in', length=6, width=px2pt(tick_width), pad=fs_def)
    ax[i].tick_params(axis='x', which='minor', bottom=True, top=True, direction='in', length=3, width=px2pt(tick_width))

    for j in ax[i].spines.keys(): ##Ajustement de la largeur de ligne du cadre
        ax[i].spines[j].set_linewidth(px2pt(spine_width))
    ##Ajuster l'échelle
    if i==0: ##Premier jour
        ax[i].tick_params(axis='y', which='both', right=True, left=True, direction='in', length=6, width=px2pt(tick_width))
        ax[i].spines['right'].set_visible(False)
    elif i==trange-1: ##Dernier jour
        ax[i].tick_params(axis='y', which='both', right=True, left=False, direction='in', length=6, width=px2pt(tick_width))
        ax[i].spines['left'].set_visible(False)            
    else: ##Autre
        ax[i].tick_params(axis='y', which='both', right=True, left=False, direction='in', length=6, width=px2pt(tick_width))
        ax[i].spines['left'].set_visible(False)
        ax[i].spines['right'].set_visible(False)

Probablement la partie qui a pris le plus de temps pour se concentrer sur les parties les plus fines. Après tout, il semble que matplotlib n'ait pas la mémoire à l'intérieur du graphe d'origine, les moyens de le dessiner. inside_ticks.png

J'ai pensé à écrire de force une barre horizontale qui séparait la plage avec ʻax.hline () , mais je ne voulais pas faire beaucoup de force brute. Donc, quand j'ai demandé à Sync qui peut gérer IDL, il semble que IDL a une fonction pour ajouter un axe n'importe où. ~~ Donnez-le à matplotlib. ~~ Je ne pouvais pas m'en empêcher, alors j'ai décidé de m'en occuper en divisant les «impôts». Plus précisément, en spécifiant les coordonnées avec ʻadd_axes, la zone est divisée comme le montre la figure ci-dessous, et en changeant l'affichage de la bordure (elle semble être appelée spine dans matplotlib), le cadre peut être créé en toute sécurité. A completé. multiple_axes.png

4.6. Texte de la classe Flare

## Flare class text
##Obtenez les coordonnées des axes les plus à gauche
f_pos = ax[-1].get_position()

trans = transforms.blended_transform_factory(fig.transFigure,transforms.IdentityTransform())
X10_y_display   = ax[-1].transData.transform((date2num(t_e),10**-3))[1]
X_y_display     = ax[-1].transData.transform((date2num(t_e),10**-4))[1]
w = X10_y_display - X_y_display

##Taille relative de la taille de la police par rapport à la figure entière
##Utilisé pour autoriser les marges lors du placement du texte de classe afin qu'il ne colle pas au cadre extérieur
cls_text_size = plt.rcParams['font.size']/(plt.rcParams['figure.figsize'][0])/72

for i,cls in enumerate(['X', 'M', 'C', 'B', 'A']):
    pos = [f_pos.x1,X10_y_display - w/2 - i*w]
    if (pos[1]>=ax[-1].transData.transform((date2num(t_e),ax[-1].get_ylim()[0]))[1])&\
       (pos[1]<=ax[-1].transData.transform((date2num(t_e),ax[-1].get_ylim()[1]))[1]):
        ax[-1].text(pos[0]+cls_text_size/5,pos[1],cls,horizontalalignment='left',verticalalignment='center',transform=trans)

C'est la partie la moins lisible de ce code. Je suis désolé.
La classe de flare est déterminée par la valeur sur l'axe vertical, vous devez donc mettre les caractères dans ʻaxes.text à la valeur correcte. Il existe plusieurs façons de définir les coordonnées lors du placement de ʻaxes.text,

  1. Coordonnées correspondant aux pixels de la figure entière
  2. Coordonnées relatives avec (0,0) en bas à gauche et (1,1) en haut à droite
  3. Dans les axes (cadre) de la figure, la partie inférieure gauche est (0,0) et la partie supérieure droite est (1,1).
  4. Coordonnées correspondant aux valeurs de la figure

Cependant, sur cette figure, l'axe horizontal est l'heure, j'ai donc abandonné (4) (Est-ce possible d'utiliser date2num?). Par conséquent, je l'ai conçu pour que les caractères puissent être placés dans la bonne position à l'extérieur de la bordure tout en utilisant blended_transform_factory qui peut gérer un mélange de (1) à (3). En particulier,

trans = transforms.blended_transform_factory(fig.transFigure,transforms.IdentityTransform())

Ce faisant, la coordonnée x fait référence à (2; figure.transFigure) et la coordonnée y se réfère à (4;transforms.IdentityTransform ()) afin que texte puisse être placé. Il semble y avoir un gâchis d'amélioration ici ...

4.7. Création d'une légende

## GOES number and wavelength

##Définir en faisant pivoter le texte de chaque couleur de 90 degrés
ybox_b = TextArea('GOES{} 0.5-4.0 A'.format(goes[0]), textprops=dict(color='#001DFF', size='large' ,rotation=90,ha='left',va='bottom')) #Bleu
ybox_r = TextArea('GOES{} 1.0-8.0 A'.format(goes[0]), textprops=dict(color='#FF0000', size='large' ,rotation=90,ha='left',va='bottom')) #rouge

##Connectez-vous verticalement avec VPacker
ybox_primary = VPacker(children=[ybox_r, ybox_b],align='bottom', pad=0, sep=2*fs_def)

##Si vous souhaitez tracer à la fois primaire et secondaire
##Créez un secondaire de la même manière et connectez-vous horizontalement avec HPacker
if len(goes)==2:
    ybox_p = TextArea('GOES{} 0.5-4.0 A'.format(goes[1]), textprops=dict(color='#5400A8', size='large' ,rotation=90,ha='left',va='bottom')) #violet
    ybox_y = TextArea('GOES{} 1.0-8.0 A'.format(goes[1]), textprops=dict(color='#FFA500', size='large' ,rotation=90,ha='left',va='bottom')) #Jaune
    ybox_secondary = VPacker(children=[ybox_y, ybox_p],align='bottom', pad=0, sep=2*fs_def)
    ybox = HPacker(children=[ybox_primary,ybox_secondary],align='bottom', pad=0, sep=fs_def)
    ##Installation dans Anchored Offsetbox L'emplacement d'installation est décidé pour chaque numéro de loc.
    ## loc=5 est'center right'En d'autres termes, le centre droit de la figure
    anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.25, 0.5), bbox_transform=fig.transFigure, borderpad=0.)
else: ##Si seulement primaire, juste primaire
    ybox = ybox_primary
    anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.5, 0.5), bbox_transform=fig.transFigure, borderpad=0.)
ax[-1].add_artist(anchored_ybox)

Ceci est également moins lisible.

Dans l'image d'origine, la légende pivote de 90 degrés vers la droite à l'extérieur du cadre. Avec matplotlib, vous pouvez mettre la légende hors du cadre, mais il ne semble rien faire pour la faire pivoter. De plus, il semble que vous ne puissiez pas changer la couleur dans ʻaxes.text`, donc vous devez placer des textes de différentes couleurs séparément. Ceci est très difficile à faire simplement en utilisant la spécification de coordonnées. Par conséquent, en utilisant «VPacker» et «HPacker» de «matplotlib.offsetbox» qui peuvent être placés dans des positions relatives, nous avons reproduit la légende qui a été tournée en dehors de la figure. C'est une technique assez brutale.

Tout d'abord, générez chaque "texte" avec "TextArea" et concaténez-les en utilisant "VPacker" et "HPacker". La pseudo-légende ainsi complétée a été installée à l'extrémité droite de la figure en utilisant ʻAnchored Offsetbox`.

L'endroit à installer est décidé pour chaque nombre de «loc», et «loc = 5» signifie installer à l'extrémité droite avec «centre droit».

4.8. Création d'étiquettes d'axe

##Création d'étiquettes d'axe(dummy axes)
ax_dummy = fig.add_axes([margin,y0_fig,1-margin*2,y1_fig-y0_fig],frameon=False)
ax_dummy.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
ax_dummy.set_xlabel('Universal Time',size='large')
ax_dummy.set_ylabel('Watts m$^{-2}$',size='large')
ax_dummy.set_title('GOES Xray Flux (1-minute data)',size='x-large')

ax_dummy.text(1, 1-cls_text_size/5, 'Begin: {} {} {} 0000 UTC'.format(t.year,t.strftime('%b'),t.day),
        horizontalalignment='right',
        verticalalignment='top',
        transform=fig.transFigure)

J'ai utilisé la méthode ~~ (technique approximative) ~~ pour afficher les «axes» séparément pour afficher l'échelle à l'intérieur de la figure, mais grâce à cela, la position de réglage de l'étiquette sur l'axe horizontal ne peut pas être déterminée. .. Par conséquent, j'ai créé un grand «axe» factice qui n'affiche rien et lui ai défini l'étiquette d'axe. dummy_axes.png

Aussi, j'ai abandonné la reproduction détaillée des caractères affichés en haut, en bas, à gauche et à droite de l'image d'origine (car les caractères en bas ne sont pas nécessaires). Cependant, s'il n'y a pas d'heure de début des données en haut à droite, les informations de l'année des données ne seront pas connues, j'ai donc décidé d'afficher cette partie à la hâte.

4.9. Tracé des données radiographiques

## X-ray data plot
color = iter(['#FF0000','#001DFF','#FFA500','#5400A8']) ## red, blue, yellow, purple 
zo = iter([4,3,2,1])
for g in goes:
    wavelength = iter(['1.0-8.0 A','0.5-4.0 A'])
    data = get_goes_csv(g,t,trange)
    xray_long  = np.ma.masked_where((data.B_AVG<=ylim[0])|(data.B_AVG>=ylim[1]),data.B_AVG)
    xray_short = np.ma.masked_where((data.A_AVG<=ylim[0])|(data.A_AVG>=ylim[1]),data.A_AVG)
    ax[0].plot(data.time_tag,xray_long, lw=px2pt(line_width),c=next(color),label='GOES{} 1.0-8.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))
    ax[0].plot(data.time_tag,xray_short,lw=px2pt(line_width),c=next(color),label='GOES{} 0.5-4.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))

Je suis finalement arrivé à l'intrigue des données radiographiques.

Parmi les colonnes du fichier csv de données moyennes sur 1 minute de GOES, vous avez besoin de time_tag '', A_AVG '' et B_AVG '' pour tracer le flux de rayons X. time_tag '' stocke le temps par minute tel quel, et A_AVG '' et B_AVG '' stockent les données de rayons X moyennes correspondant à 0,05-0,4 nm et 0,1-0,8 nm, respectivement. Ça a été. Ceux-ci sont extraits selon la syntaxe DataFrame de pandas.

zorder est l'ordre de dessin. Plus cette valeur est élevée, plus elle est affichée au premier plan. Dans l'image d'origine, la valeur de GOES primaire était affichée au premier plan, j'ai donc défini le nombre comme tel.

Une autre chose que je fais est de spécifier clip_on = False lors du traçage. Cette fois, nous avons préparé des «axes» pour la date, il est donc nécessaire d'appeler «axes.plot» pour ce nombre, mais si vous spécifiez «clip_on = False», il sera tracé en dehors des «axes» préparés. Par conséquent, les données pour tous les jours sont tracées avec des «taxes» le premier jour. De plus, en exécutant ce clip_on = False, le tracé peut être placé sur la bordure.

De plus, la valeur est masquée à l'aide de np.ma.masked_where. Les données utilisées cette fois stockent «-99999» lorsque les données sont perdues, et si cela est tracé sur l'échelle du journal tel quel, un chiffre comme un potentiel de type puits sera créé.

De plus, si vous dessinez dans la plage de ylim = (10 ** -9, 10 ** - 2) définie par défaut, il n'y a fondamentalement aucun problème, mais si vous changez cela, clip_on = False En raison de l'effet du réglage, il est possible qu'il soit tracé dans le sens vertical. pour cette raison,

xray_long  = np.ma.masked_where((data.B_AVG<=ylim[0])|(data.B_AVG>=ylim[1]),data.B_AVG)

De cette manière, toutes les données qui sortent de la plage de l'axe y définie, y compris «-99999», sont masquées. Cela laissera le tracé masqué vide sans connexion.

Normalement, stocker np.nan rend également le tracé vide, donc je ne pense pas qu'il y ait beaucoup d'exemples d'utilisation de numpy.ma, mais lorsque vous dessinez un graphique complètement différent,` np. Parfois, il était plus pratique de mettre un masque au lieu de stocker des nan ». Par conséquent, j'utilise «numpy.ma» ici à des fins d'introduction.

5. En fait, dessiner

** Remarque: les polices Simplex peuvent ne pas être disponibles par défaut sur chaque PC. ** ** Je vais dessiner en utilisant la fonction ci-dessus. Fondamentalement, les paramètres requis sont déclarés en premier,

plt.rcParams['font.family'] = 'Simplex'
plt.rcParams['font.size'] = 10
plt.rcParams['figure.dpi'] = 100

plt.rcParams['axes.linewidth'] = 1

plt.rcParams['lines.antialiased'] = False
plt.rcParams['text.antialiased'] = False
plot_antialiased = False

##Tout en unités de pixels
wdict = dict(
    grid  = 1, ##Largeur de ligne de grille
    spine = 1, ##Largeur de la ligne du cadre
    tick  = 1, ##Échelle de la largeur de la ligne
    line  = 0.01, ##Largeur de ligne du graphique
)

plot_goes([2017,9,4], 3 ,[15,13] ,'goes.png', wdict, width=6.4, ylim=(10**-9,10**-2))

Vous pouvez le tracer comme ça. goes.png Cela ressemble à ceci lorsqu'on les compare côte à côte. compare.png

N'est-ce pas tout à fait reproductible? ?? Comme la police Simplex téléchargée est assez fine, je pense qu'une partie des caractères ne sont pas affichés en raison de l'aliénation et qu'il était possible de reproduire presque tout sauf les informations sur les caractères en haut, en bas, à gauche et à droite. ~~ Il semble que la taille du cadre devra être réglée manuellement. ~~ Ceci termine la copie.

Vous devriez pouvoir dessiner sans problème même si vous modifiez un peu chaque entrée.

plot_goes([2017,9,4], 5 ,[15] ,'goes_extend.png', wdict, width='extend', ylim=(10**-9,10**-2))

goes_extend.png

6. Dessin simple

Par [5. En fait dessin](# 5-En fait dessin), la reproduction de la famille originale est presque terminée. À partir de là, rendons le diagramme un peu plus pratique (simple).

Les deux premiers éléments où le code go_plot () ci-dessus est compliqué sont:

Je pense. Je ne pense pas que vous ayez besoin d'être si précis sur les affiches et les diapositives de présentation, alors je vais essayer de les éliminer et de simplifier le code. De plus, la fonction ci-dessus go_plot () récupérera les données du serveur à chaque exécution, ce qui peut prendre du temps et être trop accessible. Par conséquent, nous réutiliserons les données téléchargées et apporterons quelques modifications afin que les détails de la figure puissent être ajustés. (Bien sûr, il est préférable de télécharger le fichier de données localement et de le charger.)

6.1. Code

plt.rcParams['font.family'] = 'Arial'
plt.rcParams['font.size'] = 11
plt.rcParams['figure.dpi'] = 100

plt.rcParams['axes.linewidth'] = 1

plt.rcParams['lines.antialiased'] = True
plt.rcParams['text.antialiased'] = True
plot_antialiased = True

##Tout en unités de pixels
wdict = dict(
    grid  = 1, ##Largeur de ligne de grille
    spine = 1, ##Largeur de la ligne du cadre
    tick  = 1, ##Échelle de la largeur de la ligne
    line  = 2, ##Largeur de ligne du graphique
)
##Convertir un pixel en point
def px2pt(px):
    dpi = plt.rcParams['figure.dpi']
    return px*72/dpi

def plot_goes_simple(t, trange ,goes ,savename, wdict, width=6.4, ylim=(10**-9,10**-2)):
    '''
    t      :Date de début du tracé(type=datetime.date or list)
             (e.g. datetime.date(2017,9,4) or [2017,9,4]) 
    trange :Période de tracé Quotidien(type=int) (e.g. trange=3 > 3 days plot)
    goes   :Format de liste de numéros de satellite GOES à acquérir(e.g. [15,13])
             index =0 est primaire(1-8A>red, 0.5-4A>blue)
             index =1 est secondaire(1-8A>yellow, 0.5-4A>purple)
    width  :Largeur de tracé par défaut=6.4
             if it is 'extend', extend the width with trange
    ylim   :par défaut de la plage de l'axe y=(10**-9,10**-2)
Probablement pas nécessaire de changer, mais conçu pour pouvoir changer
    '''
    if type(t)==list:
        t = datetime.date(t[0],t[1],t[2])
    elif (type(t)==datetime.date)or(type(t)==datetime.datetime):
        pass
    else:
        print('t should be a list or a datetime object.')
    
    fs_def = plt.rcParams['font.size']
    dpi    = plt.rcParams['figure.dpi']
    y0_fig = plt.rcParams['figure.subplot.bottom']
    y1_fig = plt.rcParams['figure.subplot.top']
    
    ##Réglage de la largeur
    if width=='extend':
        width = (dpi*6.4 + (dpi*6.4 - fs_def*10)/3*(trange-3))/dpi
    elif type(width)==float:
        width = width
    else:
        print('width is \'extend\' or float')

    ##Les marges gauche et droite correspondent à la taille de la police* 10 (Rapport au chiffre)
    margin = fs_def/(100*width)*10

    ##Largeur par jour(Rapport au chiffre)
    w_day  = (1-2*margin)/trange
    ax = []
    
    ##Cadre(axes)Création
    t_s = t
    t_e = t_s + datetime.timedelta(days=trange)
    
    fig = plt.figure(figsize=(width,4.8))
    ##Si vous mettez une légende normale dans le cadre, ajoutez de force_Sans spécifier avec les axes
    ##Fig par défaut.add_Je pense que la sous-intrigue est bien
    #ax = fig.add_axes([margin,y0_fig,1-2*margin,y1_fig-y0_fig],yscale='log', xlim=[t_s,t_e], ylim=ylim)
    ax = fig.add_subplot(111,yscale='log', xlim=[t_s,t_e], ylim=ylim)
    new_xticks = date2num([t_s+datetime.timedelta(days=i) for i in range(trange+1)])

    ax.xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
    ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
    ax.xaxis.set_minor_locator(matplotlib.dates.HourLocator(interval=3))
    ax.yaxis.grid(lw=px2pt(wdict['grid']),color='black')

    ax.tick_params(axis='x', which='major', bottom=True, top=True, direction='in', length=6, width=px2pt(wdict['tick']), pad=fs_def)
    ax.tick_params(axis='x', which='minor', bottom=True, top=True, direction='in', length=3, width=px2pt(wdict['tick']))
    ax.tick_params(axis='y', which='both', right=True, left=True, direction='in', length=6, width=px2pt(wdict['tick']))
    
    for j in ax.spines.keys(): ##Ajustement de la largeur de ligne du cadre
        ax.spines[j].set_linewidth(px2pt(wdict['spine']))

    ## Flare class text
    f_pos = ax.get_position()

    trans = transforms.blended_transform_factory(fig.transFigure,transforms.IdentityTransform())
    X10_y_display   = ax.transData.transform((date2num(t_e),10**-3))[1]
    X_y_display     = ax.transData.transform((date2num(t_e),10**-4))[1]
    w = X10_y_display - X_y_display

    ##Taille relative de la taille de la police par rapport à la figure entière
    ##Utilisé pour autoriser les marges lors du placement du texte de classe afin qu'il ne colle pas au cadre extérieur
    cls_text_size = plt.rcParams['font.size']/(plt.rcParams['figure.figsize'][0])/72
    
    for i,cls in enumerate(['X', 'M', 'C', 'B', 'A']):
        pos = [f_pos.x1,X10_y_display - w/2 - i*w]
        if (pos[1]>=ax.transData.transform((date2num(t_e),ax.get_ylim()[0]))[1])&\
           (pos[1]<=ax.transData.transform((date2num(t_e),ax.get_ylim()[1]))[1]):
            ax.text(pos[0]+cls_text_size/2,pos[1],cls,horizontalalignment='center',verticalalignment='center',transform=trans)

    ##Si vous décommentez ici, vous pouvez mettre la légende en dehors du cadre
    '''
    ## GOES number and wavelength
    ybox_b = TextArea('GOES{} 0.5-4.0 A'.format(goes[0]), textprops=dict(color='#001DFF', size='large' ,rotation=90,ha='left',va='bottom')) #Bleu
    ybox_r = TextArea('GOES{} 1.0-8.0 A'.format(goes[0]), textprops=dict(color='#FF0000', size='large' ,rotation=90,ha='left',va='bottom')) #rouge
    ybox_primary = VPacker(children=[ybox_r, ybox_b],align='bottom', pad=0, sep=2*fs_def)
    if len(goes)==2:
        ybox_p = TextArea('GOES{} 0.5-4.0 A'.format(goes[1]), textprops=dict(color='#5400A8', size='large' ,rotation=90,ha='left',va='bottom')) #violet
        ybox_y = TextArea('GOES{} 1.0-8.0 A'.format(goes[1]), textprops=dict(color='#FFA500', size='large' ,rotation=90,ha='left',va='bottom')) #Jaune
        ybox_secondary = VPacker(children=[ybox_y, ybox_p],align='bottom', pad=0, sep=2*fs_def)
        ybox = HPacker(children=[ybox_primary,ybox_secondary],align='bottom', pad=0, sep=fs_def)
        anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.25, 0.5), 
                                          bbox_transform=fig.transFigure, borderpad=0.)
    else:
        ybox = ybox_primary
        anchored_ybox = AnchoredOffsetbox(loc=5, child=ybox, pad=0., frameon=False, bbox_to_anchor=(1-margin*0.5, 0.5), 
                                          bbox_transform=fig.transFigure, borderpad=0.)
    ax.add_artist(anchored_ybox)
    '''
    
    ##Création d'étiquettes d'axe
    ax.set_xlabel('Universal Time',size='large')
    ax.set_ylabel('Watts m$^{-2}$',size='large')
    ax.set_title('GOES Xray Flux (1-minute data)',size='x-large')

    ax.text(1, 1-cls_text_size/5, 'Begin: {} {} {} 0000 UTC'.format(t.year,t.strftime('%b'),t.day),
            horizontalalignment='right',
            verticalalignment='top',
            transform=fig.transFigure)

    ## X-ray data plot
    color = iter(['#FF0000','#001DFF','#FFA500','#5400A8']) ## red, blue, yellow, purple 
    zo = iter([4,3,2,1])

    for i,g in enumerate(goes):
        data = data_temp[i]
        xray_long  = np.ma.masked_where((data.B_AVG<=ylim[0])|(data.B_AVG>=ylim[1]),data.B_AVG)
        xray_short = np.ma.masked_where((data.A_AVG<=ylim[0])|(data.A_AVG>=ylim[1]),data.A_AVG)
        ax.plot(data.time_tag,xray_long, lw=px2pt(wdict['line']),c=next(color),label='GOES{} 1.0-8.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))
        ax.plot(data.time_tag,xray_short,lw=px2pt(wdict['line']),c=next(color),label='GOES{} 0.5-4.0 A'.format(g),aa=plot_antialiased,clip_on=False,zorder=next(zo))
        
    ## simple legend
    leg = ax.legend(handlelength=0, handletextpad=0, ncol=2)
    for line, text in zip(leg.get_lines(), leg.get_texts()):
        text.set_color(line.get_color())
    
    ## save graph
    fig.savefig(savename)

La notation autour de la variable ʻax a légèrement changé car il n'est plus nécessaire de mettre plusieurs ʻaxes. Veuillez noter la différence avec plot_goes () lors de l'organisation.

## simple legend
leg = ax.legend(handlelength=0, handletextpad=0, ncol=2)
for line, text in zip(leg.get_lines(), leg.get_texts()):
    text.set_color(line.get_color())

J'ai donc rendu le marqueur de légende invisible et changé la couleur du texte de la légende en la couleur correspondant à chaque tracé.

6.2. Dessin

J'en ai essayé quelques-uns, mais les paramètres suivants semblent être parfaits.

plt.rcParams['font.family'] = 'Arial'
plt.rcParams['font.size'] = 11
plt.rcParams['figure.dpi'] = 100

plt.rcParams['axes.linewidth'] = 1

plt.rcParams['lines.antialiased'] = True
plt.rcParams['text.antialiased'] = True
plot_antialiased = True

##Tout en unités de pixels
wdict = dict(
    grid  = 1, ##Largeur de ligne de grille
    spine = 1, ##Largeur de la ligne du cadre
    tick  = 1, ##Échelle de la largeur de la ligne
    line  = 2, ##Largeur de ligne du graphique
)

Stockez les données de radiographie dans data_temp et tracez-les comme suit.

t = [2017,9,4]
trange = 3
goes = [15,13]

data_temp = []
for g in goes:
    data_temp.append(get_goes_csv(g,t,trange))

plot_goes_simple(t, trange, goes, 'goes_simple.png', wdict, width=6.4, ylim=(10**-9,10**-2))

goes_simple.png

Si ça ressemble à ça, je pense que c'est relativement facile à voir même si on le met sur une affiche.

7. Conclusion

Si j'étais particulier à ce sujet, je ne pouvais pas m'arrêter. ʻAxes` J'ai beaucoup appris sur les choses autour de moi. Si vous avez une chance, veuillez l'utiliser pour des articles, des diapositives de présentation, des affiches, etc. Si ça aide, même quand je vous rencontre quelque part, si vous dites "c'était utile", je suis très heureux que vous vous sentiez comme "je l'ai fait".

Si vous avez des questions ou des améliorations, n'hésitez pas à nous contacter. GOES-16 a des données en temps réel publiées au format json, mais vous ne disposez que des données de la semaine dernière. Les données passées seront-elles bientôt publiées? J'aurai des ennuis si je ne le fais pas. ~~ Une fois le format décidé, je voudrais le faire pour qu'il puisse être pris en charge.

Les références

matplotlib en général matplotlib.figure.Figure — Matplotlib 3.1.2 documentation Connaissance de base de matplotlib que je voulais connaître tôt, ou l'histoire d'un artiste qui peut ajuster l'apparence --Qiita Un très résumé de matplotlib-Qiita mémo pull inversé matplotlib (graphique simple) --Qiita mémo de tirage inversé matplotlib (édition cadre) --Qiita

autour de la tique, de la colonne vertébrale Définissez l'échelle de l'axe du graphique des données de séries temporelles avec matplotlib.dates | Analysis Train Échelle et étiquette d'échelle Matplotlib, ligne d'échelle Créez un graphique avec des bordures supprimées avec matplotlib --Qiita

placement du texte et spécification des coordonnées (transformation) Text properties and layout — Matplotlib 3.1.2 documentation Transformations Tutorial — Matplotlib 3.1.2 documentation

Définition d'étiquettes d'axe pour plusieurs sous-tracés (définition d'axes factices) python - pyplot axes labels for subplots - Stack Overflow

Ajustement du marqueur de légende et de la couleur du texte de la légende python - How do you just show the text label in plot legend? (e.g. remove a label's line in the legend) - Stack Overflow Option to set the text color in legend to be same as the line · Issue #10720 · matplotlib/matplotlib · GitHub

Création d'une légende pivotée de 90 degrés (matplotlib.offsetbox) python - Matplotlib: y-axis label with multiple colors - Stack Overflow matplotlib.offsetbox — Matplotlib 3.1.2 documentation

numpy.ma The numpy.ma module — NumPy v1.17 Manual

Recommended Posts

Créez vous-même le tracé familier du flux de rayons X sur 3 jours de NOAA / GOES avec Python
[Python] journalisation dans votre propre module
Créez vos propres commandes Linux en Python
[LLDB] Créez votre propre commande avec Python
Utilisez facilement vos propres fonctions en Python
Obtenez votre propre adresse IP en Python
Créez rapidement votre propre module avec setuptools (python)
Créez un tracé de R semblable à un joyplot avec python
Importez vos propres modules avec le développement Python de Grasshopper
Créez votre propre Big Data en Python pour validation
Créez votre propre stéréogramme aléatoire (RDS) en Python.
Essayez d'améliorer votre propre quiz d'introduction avec Python
Utilisez CASA Toolkit dans votre propre environnement Python
[Road to Intermediate Python] Définissez dans votre propre classe
Essayez de trier vos propres objets avec des files d'attente prioritaires en Python
Ne faites pas test.py en Python!
Créer un bookmarklet en Python
Rendre Opencv disponible en Python
Segfo python en 2 lignes
Tracer des informations géographiques en Python
Simulation de diffraction des rayons X avec Python
Créez votre propre classe de structure graphique et son dessin avec python