** 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.
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. ** **
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.
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.
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
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.
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)
Je présenterai chaque partie. Si vous souhaitez pouvoir l'utiliser pour le moment, veuillez l'ignorer.
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.
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.
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é.
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)).
##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.
##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.
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é.
## 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
,
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 ...
## 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».
##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.
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.
## 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.
** 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. Cela ressemble à ceci lorsqu'on les compare côte à côte.
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))
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.)
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é.
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))
Si ça ressemble à ça, je pense que c'est relativement facile à voir même si on le met sur une affiche.
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.
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