Lorsque je travaille sur l'analyse des données, je pense qu'en plus du suivi numérique des données, il arrive souvent que les caractéristiques des données soient confirmées visuellement à l'aide de graphiques et autres.
Il existe de nombreuses tâches de ce type, et j'ai créé un outil appelé "Glance" qui peut afficher des graphiques immédiatement tout en changeant librement la combinaison de données.
J'ai utilisé la bibliothèque tkinter
de Python pour créer l'interface graphique.
Le fichier peut être trouvé à ici. ** * Je l'ai fait pour une utilisation dans un environnement Windows, il doit donc probablement être modifié séparément dans un environnement Mac ... **
C'est un outil simple qui sélectionne simplement les données lues et les affiche sous forme de graphique. Trois opérations majeures ont été mises en œuvre.
Lisez les données de la sélection de fichiers et affichez la colonne dans une liste
Sélectionnez les données et affichez le graphique
J'ai utilisé tkinter pour la première fois cette fois, mais l'une des choses qui est restée bloquée est "** Je ne sais pas comment recevoir la valeur de retour de la fonction affectée au widget **". Pour cela, il a semblé nécessaire de définir une classe pour contenir les données et de les transmettre en donnant à chaque objet des données.
Par exemple, dans le cas de cet outil, il existe une fonction appelée fileselect
qui lit le fichier, mais la trame de données lue n'est pas retournée par retour, mais est donnée à l'objet dans le processus.
def fileselect():
root = tkinter.Tk()
root.withdraw()
fTyp = [("", "*")]
iDir = os.path.abspath(os.path.dirname(__file__))
model.path = tkinter.filedialog.askopenfilename(filetypes=fTyp,
initialdir=iDir)
suffix = model.path.rsplit('.', 1)[1]
if suffix == 'csv' or suffix == 'pkl':
model.set_data(suffix) #J'ai les données ici.
graph.config(state='active')
Entry1.insert(tk.END, model.path)
line_size = columns_list.size()
columns_list.delete(0, line_size)
for column in enumerate(model.data.columns):
columns_list.insert(tk.END, column)
else:
tkinter.messagebox.showinfo(model.path, 'Sélectionnez un fichier csv ou pickle.')
Lors de l'utilisation du widget tkinter avec une fonction Vous définirez la fonction d'opération à l'intérieur de la fonction sur la portée de la variable.
Cette fois, nous définissons également new_window
pour ouvrir le graphique dans une fenêtre séparée, donc
La structure de la fonction «redessiner» qui met à jour le graphique est la suivante.
def main():
#En traitement
def new_window():
def redraw():
#En traitement
scale = tk.Scale(command=redraw) #Barre coulissante
Cela entraînera l'exécution de la fonction de rafraîchissement à chaque fois que vous manipulerez l'échelle.
L'intérieur de la fonction de redraw est gratuit, mais après avoir changé le contenu exprimé par matplotlib
Veuillez noter que si canvas.draw ()
n'est pas à la fin, il ne sera pas reflété sur l'interface graphique.
C'est un problème non résolu, mais dans mon style d'écriture, c'est dans main () Je ne pouvais pas sortir en tant que fonction ou fichier séparé parce que j'ai défini toutes les autres fonctions. (Mais je ne sais comment faire fonctionner le widget qu'avec une fonction ...)
En conséquence, main () devient long et difficile à lire.
import os
import tkinter as tk
import tkinter.filedialog
import tkinter.messagebox
import japanize_matplotlib
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import numpy as np
import pandas as pd
def main():
class model_data:
def __init__(self):
self.data = pd.DataFrame()
self.path = ''
self.columns = []
def set_path(self, file_path):
self.path = file_path
def set_data(self, suffix):
if suffix == 'csv':
self.data = pd.read_csv(self.path)
elif suffix == 'pkl':
self.data = pd.read_pickle(self.path)
def new_window(event):
if any(model.data):
def _quit_sub():
sub_win.destroy()
def _redraw(event):
min_lim = min_scale.get()
max_lim = max_scale.get()
val = []
if min_lim >= max_lim:
return
else:
t = target.iloc[min_lim:max_lim, :]
for index in selection:
label = target.columns[index]
val.append(t[label].max())
val.append(t[label].min())
max_val = max(val)
min_val = min(val)
ax.set_xlim(min_lim, max_lim)
ax.set_ylim(min_val, max_val)
canvas.draw()
selection = columns_list.curselection()
sub_win = tk.Toplevel(root)
sub_win.configure(bg='white')
sub_win.wm_title("Embedding in Tk")
target = model.data.copy()
target.reset_index(drop=True, inplace=True)
fig = Figure(figsize=(10, 4), dpi=100)
ax = fig.add_subplot(111)
if norm_bln.get():
for index in selection:
label = target.columns[index]
max_num = target[label].max()
min_num = target[label].min()
target[label] = (target[label] - min_num) / (max_num - min_num)
ax.plot(target.index,
target[label],
label=label)
else:
for index in selection:
label = target.columns[index]
ax.plot(target.index,
target[label],
label=label)
canvas_frame = tk.Frame(master=sub_win, bg='white')
canvas_frame.pack(side=tk.LEFT)
canvas = FigureCanvasTkAgg(fig,
master=canvas_frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP,
fill=tk.BOTH,
expand=1)
control_frame = tk.Frame(master=sub_win)
control_frame.pack(side=tk.RIGHT)
toolbar = NavigationToolbar2Tk(canvas,
canvas_frame)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP,
fill=tk.BOTH,
expand=True)
min_scale = tk.Scale(control_frame,
label='Min',
orient='h',
from_=0,
bg='light blue',
resolution=1,
to=model.data.shape[0]-1,
command=_redraw)
min_scale.pack(anchor=tk.NW)
max_scale = tk.Scale(control_frame,
label='Max',
orient='h',
from_=1,
bg='tan1',
resolution=1,
to=model.data.shape[0],
command=_redraw)
max_scale.set(target.shape[0])
max_scale.pack(anchor=tk.NW)
button = tkinter.Button(master=canvas_frame,
text="Quit",
command=_quit_sub)
button.pack(side=tkinter.BOTTOM)
else:
tkinter.messagebox.showinfo('Glance.py', 'Veuillez sélectionner les données à lire en premier')
def fileselect():
root = tkinter.Tk()
root.withdraw()
fTyp = [("", "*")]
iDir = os.path.abspath(os.path.dirname(__file__))
model.path = tkinter.filedialog.askopenfilename(filetypes=fTyp,
initialdir=iDir)
suffix = model.path.rsplit('.', 1)[1]
if suffix == 'csv' or suffix == 'pkl':
model.set_data(suffix)
graph.config(state='active')
Entry1.insert(tk.END, model.path)
line_size = columns_list.size()
columns_list.delete(0, line_size)
for column in enumerate(model.data.columns):
columns_list.insert(tk.END, column)
else:
tkinter.messagebox.showinfo(model.path, 'Sélectionnez un fichier csv ou pickle.')
def _quit(event):
root.quit()
root.destroy()
root = tk.Tk()
model = model_data()
root.title('Glance')
root.geometry('680x300')
frame1 = tk.Frame(bd=3, relief=tk.SUNKEN, pady=5)
frame1.pack(padx=5, pady=5, ipadx=10, ipady=10, side=tk.LEFT)
Static1 = tk.Label(frame1, text='File Path')
Static1.pack()
Entry1 = tk.Entry(frame1, width=80)
Entry1.insert(tk.END, model.path)
Entry1.pack()
Static1 = tk.Label(frame1, text='Column Names')
Static1.pack()
var = tk.StringVar(value=[])
columns_list = tk.Listbox(frame1,
listvariable=var,
width=80,
selectmode='multiple')
scrollbar = tk.Scrollbar(frame1,
command=columns_list.yview)
columns_list.yscrollcommand = scrollbar.set
scrollbar.pack(fill=tk.BOTH,
side=tk.RIGHT)
columns_list.pack()
Button1 = tk.Button(frame1,
text='SelectFile',
width=10,
command=fileselect)
Button1.pack(pady=5)
frame2 = tk.Frame()
frame2.pack(padx=5, pady=5, ipadx=10, ipady=10, fill=tk.BOTH)
norm_bln = tk.BooleanVar()
norm_bln.set('False')
norm = tkinter.Checkbutton(frame2,
variable=norm_bln,
text='Norm')
norm.pack()
graph = tk.Button(frame2,
text='Graph',
width=10,
state='disable')
graph.bind("<Button-1>", new_window)
graph.pack()
frame3 = tk.Frame()
frame3.pack(padx=5, pady=5, ipadx=10, ipady=10, side=tk.BOTTOM)
Button2 = tk.Button(frame3, text='Exit', width=5)
Button2.bind("<Button-1>", _quit)
Button2.pack()
root.mainloop()
if __name__ == "__main__":
main()
Comment afficher le graphique et le modifier dynamiquement À propos du placement des widgets tkinter
Recommended Posts