きれいなグラフを簡単に作成できるアプリを作ってみた【環境不要】【体裁調整可能】

データ処理を行う方はcsvファイルを元にグラフを作成する方が多いと思います。グラフ作成アプリ自体は多くありますが、自分の好みに応じて体裁を設定可能なものがなかったので作成してみました。Windowsの方は、exeファイルを用いれば追加の環境設定は不要です。

実行ファイル

実行ファイル(.exe)をzip化したものになります。exe化には「cx_Freeze」を用いました。exeファイルはGoogle driveのリンクを貼ります。buildファイルがzip圧縮されたものなので、お使いのWindows環境で展開してください。(環境によっては上手く動作しない場合もあります。その場合はpythonのexe化の記事を参考にしてアプリを作成してみてください。)

(作成したアプリへのリンクはこちら)

テストデータとして用いた「tabledata.csv」も載せておきます。

操作例

Excelよりも簡単にグラフ作成可能

Excelファイルを開いてデータ列を選ぶだけ

csvデータ
使用したtabledata.csv

上のような表形式のExcelデータ(拡張子.csv)を例にグラフ作成テストを行いました。1行目がヘッダ(見出し)で、2行目以降が各データになります。

操作画面説明

動画にあるように、ファイルを選択するとデータが読み込まれるので(①)、X軸・Y軸にするデータを選択して(②)、「Scatter Plot」を選択すれば散布図の出来上がりです(③)。

「Scatter Plot」を繰り返し押すと重ね描きされていくので、前のプロットを消したい場合にはリセットボタンを押します(④)。グラフの体裁を調整したい場合は左下のオプションを調整します(⑤)。

(グラフの体裁オプションを変更してプロットした後で、再び体裁を変更しても反映されない場合があります。その場合は体裁オプションを修正した後で一度グラフ初期化(④)を行います。)

Data1,Data2があるのは、フォーマットが違うファイルからプロットできるようにするためです。(Data1でも同じ形式であれば複数ファイルを選択できますが、形式が違うとエラーが生じます。)

実現したかったこと

私が本プログラムを作成したのは、Excelでグラフを作成して体裁を整えるのが面倒くさいと思ったことがきっかけです。

Excelマクロを使ったりPythonなどの言語でプログラムを書くことはできるのですが、

・ちょっとX軸・Y軸データからグラフを書きたい、

・でも発表スライドに載せられるくらいの体裁は整えたい

と思うことが大学・会社で度々ありました。

もちろん世の中にはExcelで扱うcsvファイルからグラフを作成するツールがいくつも存在していますが、

・確認用には使えるものの体裁が整っていないグラフを作成するツール

・体裁の整ったグラフは作成できるものの設定する条件の多いツール

などが多いと感じました。

大学の研究室ではIgorという有料ソフトを用いており、マクロ機能を使って複雑なグラフも作成可能でしたが、多くの方はそこまで多くの機能がなくても、きれいなグラフを簡単にかけるフリーソフトがあれば十分だと思いました。

Python環境がある方は以前のカラープロットの記事のようにコードを書くことでグラフをかけますが、毎回似たようなグラフを描くならexeファイルのほうが断然簡単だと思います。

手法: PythonのTkinter

機械学習などで近年多く使用されているPythonというプログラミング言語でGUIを作成するツール「Tkinter」を用いました。視覚的に操作できるようにすることで、どなたでも使えるようになると良いと思いました。

ソースコード

今回用いたソースコードを以下に載せます。

#Copyright (C) 2022 てるみかん.  All rights reserved.
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.filedialog as tkfd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
import matplotlib.colors as clr
import matplotlib.markers as mkr


import pandas as pd

import numpy as np



class Application(tk.Frame):
    

    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.title('2D_plot')
        self.pack()
        self.color_list      = clr.cnames
        self.color_list_pickup={ 'black': '#000000', 'blue': '#0000FF',  'brown': '#A52A2A',  'cyan': '#00FFFF',  'gold': '#FFD700',  'gray': '#808080', 'green': '#008000',   'magenta': '#FF00FF',   'navy': '#000080',  'orange': '#FFA500', 'pink': '#FFC0CB',  'purple': '#800080',  'red': '#FF0000',  'white': '#FFFFFF','yellow': '#FFFF00'}
        self.marker_list     = mkr.MarkerStyle().markers   
        self.create_widgets()
        self.dict_data_frame = {}
        self.selected_data   = []
        self.init_status_bar()
        self.master.bind('<Control-v>', self.scatter_plot(self.entry1,self.num1))
        self.master.bind('<Control-V>', self.scatter_plot(self.entry1,self.num1))
        self.master.bind('<Control-c>', self.scatter_plot(self.entry1,self.num1))
        self.master.bind('<Control-C>', self.scatter_plot(self.entry1,self.num1))
   
    def click_x1_set_button(self):
        self.x1_data_box.delete(0, tk.END)
        if not self.selected_data:
            self.status_bar_str.set('Select data from Data List')
            return
        self.group_data_name_str_x1 = ','.join(self.selected_data[-1])
        self.x1_data_box.insert(tk.END, self.group_data_name_str_x1)

    def click_y1_set_button(self):
        self.y1_data_box.delete(0, tk.END)
        if not self.selected_data:
            self.status_bar_str.set('Select data from Data List')
            return
        self.group_data_name_str_y1 = ','.join(self.selected_data[-1])
        self.y1_data_box.insert(tk.END, self.group_data_name_str_y1)
        
    def click_x2_set_button(self):
        self.x2_data_box.delete(0, tk.END)
        if not self.selected_data:
            self.status_bar_str.set('Select data from Data List')
            return
        self.group_data_name_str_x2 = ','.join(self.selected_data[-1])
        self.x2_data_box.insert(tk.END, self.group_data_name_str_x2)

    
    def click_y2_set_button(self):
        self.y2_data_box.delete(0, tk.END)
        if not self.selected_data:
            self.status_bar_str.set('Select data from Data List')
            return
        self.group_data_name_str_y2 = ','.join(self.selected_data[-1])
        self.y2_data_box.insert(tk.END, self.group_data_name_str_y2)
       
    def init_status_bar(self):
        self.status_bar_str = tk.StringVar()
        self.status_bar = tk.Label(self.master,
                                   textvariable=self.status_bar_str,
                                   bd=1, relief=tk.SUNKEN,
                                   anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        self.status_bar_str.set('')
    
    def init_data_list(self):
        self.data_list_label = tk.Label(text='Data List')
        self.data_list_label.pack()
        container = ttk.Frame()
        container.pack(fill=tk.BOTH, expand=True)
        data_header = ['Data group', 'Data name', 'Max', 'Min', 'Mean', 'Sigma']
        self.data_list = ttk.Treeview(columns=data_header, show='headings')
        self.data_list.column(2, width=100)
        self.data_list.column(3, width=100)
        self.data_list.column(4, width=100)
        self.data_list.column(5, width=100)
        v_scrollbar = ttk.Scrollbar(orient='vertical', command=self.data_list.yview)
        h_scrollbar = ttk.Scrollbar(orient='horizontal', command=self.data_list.xview)
        self.data_list.configure(xscrollcommand=h_scrollbar.set,
                                 yscrollcommand=v_scrollbar.set)
        self.data_list.grid(column=0, row=0, sticky='nsew', in_=container)
        v_scrollbar.grid(column=1, row=0, sticky='ns', in_=container)
        h_scrollbar.grid(column=0, row=1, sticky='ew', in_=container)
        container.grid_columnconfigure(0, weight=3)
        container.grid_rowconfigure(0, weight=1)
        for column in data_header:
            self.data_list.heading(column, text=column.title())

    def insert_data_frame(self, df, path):
        group_name = path.split('/')[-1]
        if not self.dict_data_frame.keys():
            self.dict_data_frame[group_name] = df
        else:
            last_group_name = list(self.dict_data_frame.keys())[-1]
            last_df = self.dict_data_frame[last_group_name]
            last_df_columns = last_df.columns.values.tolist()
            add_df_columns  = df.columns.values.tolist()
            if last_df_columns == add_df_columns:
                self.dict_data_frame[last_group_name] = pd.concat([self.dict_data_frame[last_group_name], df])
            else:
                self.dict_data_frame[group_name] = df 
    
    def insert_data_list(self):
        for group_name in list(self.dict_data_frame.keys()):
            single_df = self.dict_data_frame[group_name]
            single_df_columns = single_df.columns.values.tolist()
            for column in single_df_columns:
                # calculate max, min, ave and sigma of each value
                data = single_df[column]
                max_value   = round(float(np.max(data)), 2)
                min_value   = round(float(np.min(data)), 2)
                mean_value  = round(float(np.mean(data)), 2)
                sigma_value = round(float(np.std(data)), 2)
                self.data_list.insert('', 'end', values=(group_name, column, max_value, 
                                      min_value, mean_value, sigma_value))
        # binding event
        self.data_list.bind('<<TreeviewSelect>>', self.select_data_from_list)
        
    def select_data_from_list(self, event):
        widget = event.widget
        self.selected_data = []
        for index in widget.selection():
            data_group_name = widget.item(index)['values']
            self.selected_data.append(data_group_name)
        self.status_bar_str.set('')
    

    def create_widgets(self):
        
        #----------------------plot1-------------------------------------------------------------------------
        self.Frame1 = tk.Frame(self.master)
        self.Frame1.pack(side=tk.TOP)
        self.num1=1
        self.Button11 = tk.Button(self.Frame1, text="Data1", command=lambda: [self.read_csv_file(self.entry1)])
        self.Button11.pack(side=tk.LEFT) 
        self.entry1 = tk.Entry(self.Frame1, width=100)
        self.entry1.pack(side=tk.LEFT)        
        self.Button12 = tk.Button(self.Frame1, text="Scatter Plot", command=lambda: self.scatter_plot(self.entry1,self.num1))
        self.Button12.pack(side=tk.LEFT)
        self.Button13 = tk.Button(self.Frame1, text="Line Plot", command=lambda: self.line_plot(self.entry1,self.num1))
        self.Button13.pack(side=tk.LEFT)
        
        #----------------------plot2-------------------------------------------------------------------------
        self.Frame2 = tk.Frame(self.master)
        self.Frame2.pack(side=tk.TOP)
        self.Button21 = tk.Button(self.Frame2, text="Data2", command=lambda: [self.read_csv_file(self.entry2)])
        self.Button21.pack(side=tk.LEFT)
        self.entry2 = tk.Entry(self.Frame2, width=100)
        self.entry2.pack(side=tk.LEFT)
        self.Button22 = tk.Button(self.Frame2, text="Scatter Plot", command=lambda: self.scatter_plot(self.entry2,self.num2))
        self.Button22.pack(side=tk.LEFT)
        self.Button23 = tk.Button(self.Frame2, text="Line Plot", command=lambda: self.line_plot(self.entry2,self.num2))
        self.Button23.pack(side=tk.LEFT)

        #---------------------- Frame3 -------------------------------------------------------------------------
        self.Frame3 = tk.Frame(self.master)
        self.Frame3.pack(side=tk.TOP)
        # x data set
        self.x1_data_button = tk.Button(self.Frame3,text='Data1 X', command=self.click_x1_set_button)
        self.x1_data_button.grid(row=0, column=0)        
        self.x1_data_box = tk.Entry(self.Frame3,width=22)
        self.x1_data_box.grid(row=0, column=1)
        # y data set
        self.y1_data_button = tk.Button(self.Frame3,text='Data1 Y', command=self.click_y1_set_button)
        self.y1_data_button.grid(row=0, column=2)
        self.y1_data_box = tk.Entry(self.Frame3,width=22)
        self.y1_data_box.grid(row=0, column=3)
        # x2 data set
        self.x2_data_button = tk.Button(self.Frame3,text='Data2 X', command=self.click_x2_set_button)
        self.x2_data_button.grid(row=0, column=4)
        self.x2_data_box = tk.Entry(self.Frame3,width=22)
        self.x2_data_box.grid(row=0, column=5)
        # y data set
        self.y2_data_button = tk.Button(self.Frame3,text='Data2 Y', command=self.click_y2_set_button)
        self.y2_data_button.grid(row=0, column=6)
        self.y2_data_box = tk.Entry(self.Frame3,width=22)
        self.y2_data_box.grid(row=0, column=7)
        # ボタンをフレームに配置
        self.btn_open_folder = tk.Button(self.Frame3, text="Graph Reset",command = lambda:self.clear_graph())#, image = self.ico_open_file)
        self.btn_open_folder.grid(column=8, row=0) # 左側だけ隙間を空ける
 
        #----------------------Data_List-------------------------------------------------------------------------
        self.Frame5 = tk.Frame(self.master)
        self.Frame5.pack(expand=True,side=tk.TOP,fill=tk.BOTH)
        self.data_list_label = tk.Label(self.Frame5,text='Data List')
        container = ttk.Frame(self.Frame5)
        container.pack(fill=tk.BOTH)
        data_header = ['Data group', 'Data name', 'Max', 'Min', 'Mean', 'Sigma']
        self.data_list = ttk.Treeview(self.Frame5,columns=data_header, show='headings')
        self.data_list.column(2, width=60)
        self.data_list.column(3, width=60)
        self.data_list.column(4, width=60)
        self.data_list.column(5, width=60)
        v_scrollbar = ttk.Scrollbar(self.Frame5,orient='vertical', command=self.data_list.yview)
        h_scrollbar = ttk.Scrollbar(self.Frame5,orient='horizontal', command=self.data_list.xview)
        self.data_list.configure(xscrollcommand=h_scrollbar.set,
                                 yscrollcommand=v_scrollbar.set)
        self.data_list.grid(column=0, row=0, sticky='nsew', in_=container)
        v_scrollbar.grid(column=1, row=0, sticky='ns', in_=container)
        h_scrollbar.grid(column=0, row=1, sticky='ew', in_=container)
        container.grid_columnconfigure(0, weight=3)
        container.grid_rowconfigure(0, weight=1)
        for column in data_header:
            self.data_list.heading(column, text=column.title())
 
        #----------------------Data_List-------------------------------------------------------------------------
        self.Frame6 = tk.Frame(self.master)
        self.Frame6.pack(expand=True,side=tk.TOP)
        
        # graph title
        self.graph_title_label = tk.Label(self.Frame6,text='Graph Title')
        self.graph_title_label.grid(column=0, row=0)
        self.graph_title = tk.Entry(self.Frame6,width=12)
        self.graph_title.grid(column=1, row=0)
        self.graph_title.insert(tk.END,"Title")
        
        # x_axis title
        self.x_axis_title_label = tk.Label(self.Frame6,text='X_axis Title')
        self.x_axis_title_label.grid(column=2, row=0)
        
        self.x_axis_title = tk.Entry(self.Frame6,width=12)
        self.x_axis_title.grid(column=3, row=0)
        self.x_axis_title.insert(tk.END,"X")
        
        # y_axis title
        self.y_axis_title_label = tk.Label(self.Frame6,text='Y_axis Title')
        self.y_axis_title_label.grid(column=4, row=0)
        self.y_axis_title = tk.Entry(self.Frame6,width=12)
        self.y_axis_title.grid(column=5, row=0)
        self.y_axis_title.insert(tk.END,"Y")
        
        # figure size
        self.fig_size_label = tk.Label(self.Frame6,text='Figure Size(width,height)')
        self.fig_size_label.grid(column=6, row=0)
        self.fig_size = tk.Entry(self.Frame6,width=8)
        self.fig_size.grid(column=7, row=0)
        self.fig_size.insert(tk.END,"5,5")
        
        # X range
        self.X_Range_label = tk.Label(self.Frame6,text='X Range(min,max)')
        self.X_Range_label.grid(column=0, row=1)
        self.X_Range = tk.Entry(self.Frame6,width=8)
        self.X_Range.grid(column=1, row=1)
        #self.X_Range.insert(tk.END,"test")
        
        # Y range
        self.Y_Range_label = tk.Label(self.Frame6,text='Y Range(min,max)')
        self.Y_Range_label.grid(column=2, row=1)
        self.Y_Range = tk.Entry(self.Frame6,width=8)
        self.Y_Range.grid(column=3, row=1)
        
        # font size
        self.font_size_label = tk.Label(self.Frame6,text='Font Size')
        self.font_size_label.grid(column=4, row=1)
        self.font_size = tk.Entry(self.Frame6,width=8)
        self.font_size.grid(column=5, row=1)
        self.font_size.insert(tk.END,"16")
        
        # font family
        self.font_family_label = tk.Label(self.Frame6,text='Font Family')
        self.font_family_label.grid(column=6, row=1)
        self.font_family = tk.Entry(self.Frame6,width=8)
        self.font_family.grid(column=7, row=1)
        self.font_family.insert(tk.END,"Arial")

        # plot1 color pattern
        self.color1_list_label = tk.Label(self.Frame6,text='Plot1 Color')
        self.color1_list_label.grid(column=0, row=2)
        self.color1_list_cmbbox = ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)#,command=lambda: [self.open_command()])
        self.color1_list_cmbbox['values'] = list(self.color_list_pickup.keys())
        self.color1_list_cmbbox.current(0)
        self.color1_list_cmbbox.grid(column=1, row=2)

        # plot2 color pattern
        self.color2_list_label = tk.Label(self.Frame6,text='Plot2 Color')
        self.color2_list_label.grid(column=0, row=3)
        self.color2_list_cmbbox = ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)#,command=lambda: [self.open_command()])
        self.color2_list_cmbbox['values'] = list(self.color_list_pickup.keys())
        self.color2_list_cmbbox.current(12)
        self.color2_list_cmbbox.grid(column=1, row=3)
        def open_command(self):
            self.color_name=str(self.color_list_cmbbox.get())
            self.color_list_cmbbox.config(bg=str(self.color_list_cmbbox.get()))

        # plot1 marker pattern
        self.marker1_list_label = tk.Label(self.Frame6,text='Marker Pattern1')
        self.marker1_list_label.grid(column=2, row=2)
        self.marker1_list_cmbbox = ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)
        self.marker1_list_cmbbox['values'] = list(self.marker_list.keys())
        self.marker1_list_cmbbox.current(0)
        self.marker1_list_cmbbox.grid(column=3, row=2)
          
        # plot2 marker pattern
        self.marker2_list_label = tk.Label(self.Frame6,text='Marker Pattern2')
        self.marker2_list_label.grid(column=2, row=3)
        self.marker2_list_cmbbox = ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)
        self.marker2_list_cmbbox['values'] = list(self.marker_list.keys())
        self.marker2_list_cmbbox.current(12)
        self.marker2_list_cmbbox.grid(column=3, row=3)
        
        
        # plot1 marker size
        self.marker_size1_label = tk.Label(self.Frame6,text='Marker Size1')
        self.marker_size1_label.grid(column=4, row=2)
        self.marker_size1 = tk.Entry(self.Frame6,width=8)
        self.marker_size1.grid(column=5, row=2)
        self.marker_size1.insert(tk.END,"10")
        
        
        # plot2 marker pattern
        self.marker_size2_label = tk.Label(self.Frame6,text='Marker Size2')
        self.marker_size2_label.grid(column=4, row=3)
        self.marker_size2 = tk.Entry(self.Frame6,width=8)
        self.marker_size2.grid(column=5, row=3)
        self.marker_size2.insert(tk.END,"10")
        
        # plot1 legend
        self.legend1_label = tk.Label(self.Frame6,text='Legend1')
        self.legend1_label.grid(column=6, row=2)
        
        self.legend1 = tk.Entry(self.Frame6,width=12)
        self.legend1.grid(column=7, row=2)
        #self.legend1.insert(tk.END,"data1")
        
        # plot2 legend
        self.legend2_label = tk.Label(self.Frame6,text='Legend2')
        self.legend2_label.grid(column=6, row=3)
        
        self.legend2 = tk.Entry(self.Frame6,width=12)
        self.legend2.grid(column=7, row=3)
        #self.legend2.insert(tk.END,"data2")
        
        
        ####### row=4 #################################################################
        # set grid
        self.var_grid=tk.BooleanVar()#True or False
        self.set_grid_check = tk.Checkbutton(self.Frame6,text="set grid",variable=self.var_grid)
        self.set_grid_check.grid(column=0, row=4)
        self.var_grid.set(False)
        
        # grid color pattern
        self.grid_color_label = tk.Label(self.Frame6,text='Grid Color')
        self.grid_color_label.grid(column=1, row=4)
        self.grid_color = ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)#,command=lambda: [self.open_command()])
        self.grid_color['values'] = list(self.color_list_pickup.keys())
        self.grid_color.current(0)
        self.grid_color.grid(column=2, row=4)
        
        # grid xy
        self.grid_xy_label = tk.Label(self.Frame6,text='Grid XY')
        self.grid_xy_label.grid(column=3, row=4)
        self.grid_xy= ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)#,command=lambda: [self.open_command()])
        self.grid_xy['values'] = ['both','x','y']
        self.grid_xy.current(0)
        self.grid_xy.grid(column=4, row=4)
        
        # grid linestyle
        self.grid_linestyle_label = tk.Label(self.Frame6,text='Grid Linestyle')
        self.grid_linestyle_label.grid(column=5, row=4)
        self.grid_linestyle= ttk.Combobox(self.Frame6, state='readonly', justify='center', width=12)#,command=lambda: [self.open_command()])
        self.grid_linestyle['values'] = ['dotted','solid','dashed','dashdot']
        self.grid_linestyle.current(0)
        self.grid_linestyle.grid(column=6, row=4)
        
        plt.rcParams['font.family'] =self.font_family.get()#使用するフォント
        plt.rcParams['xtick.direction'] = "in"#x軸の目盛線が内向き('in')か外向き('out')か双方向か('inout')
        plt.rcParams['ytick.direction'] = "in"#y軸の目盛線が内向き('in')か外向き('out')か双方向か('inout')
        plt.rcParams['xtick.major.width'] = 2.0#x軸主目盛り線の線幅
        plt.rcParams['ytick.major.width'] = 2.0#y軸主目盛り線の線幅
        plt.rcParams['font.size'] = self.font_size.get() #フォントの大きさ
        plt.rcParams['axes.linewidth'] = 2.0# 軸の線幅edge linewidth。囲みの太さ

        
        #----------------------figure-------------------------------------------------------------------------
        self.Frame4 = tk.Frame(self.master)
        self.Frame4.pack(after=self.Frame3,side=tk.RIGHT)
        self.figsize_tuple=tuple(self.fig_size.get())
        self.fig = plt.Figure(figsize=(int(self.figsize_tuple[0]),int(self.figsize_tuple[2])))  
        self.ax = self.fig.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.fig, self.Frame4)
        self.canvas.draw() 
        self.toolbar = NavigationToolbar2Tk(self.canvas, self.Frame4)
        self.toolbar.update()   
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.ax.set_title(self.graph_title.get())#(self.acquisited_graph_title)#Application.graph_title)
     self.ax.set_xlabel(self.x_axis_title.get())
        self.ax.set_ylabel(self.y_axis_title.get())
        self.fig.tight_layout()   
        self.ax.legend( loc="best")#"best","upper right","upper left","lower left","lower right","right","center right","lower center","upper center","center"

        #----------------------end-------------------------------------------------------------------------

    def load_data(self, entry):
        file_path = tk.filedialog.askopenfilename()
        entry.insert(tk.END, file_path)
        
    def scatter_plot(self,entry,num):
        filename = entry.get()
        try:
            df = pd.read_csv(filename, header=0)
            if num==1:
                x1_split=self.group_data_name_str_x1.split(",")
                y1_split=self.group_data_name_str_y1.split(",")
                x_org = df.loc[:,x1_split[1]]#2列目がheader
                y_org = df.loc[:,y1_split[1]]
                self.ax.scatter(x_org,y_org,label=self.legend1.get(), c=self.color1_list_cmbbox.get(),s=int(self.marker_size1.get()))#,marker=str(self.marker1_list_cmbbox))
                
            else:
                x2_split=self.group_data_name_str_x2.split(",")
                y2_split=self.group_data_name_str_y2.split(",")
                x_org = df.loc[:,x2_split[1]]#2列目がheader
                y_org = df.loc[:,y2_split[1]]
                self.ax.scatter(x_org,y_org, label=self.legend2.get(), c=self.color2_list_cmbbox.get(),s=int(self.marker_size2.get()))#,marker=str(self.marker2_list_cmbbox))
        except:
            x_org = []
            y_org = []
        self.ax.set_title(self.graph_title.get())
        self.X_Range_split=self.X_Range.get().split(',')
        
        if len(self.X_Range_split)>1:
            self.ax.set_xlim(int(self.X_Range_split[0]),int(self.X_Range_split[1]))
   
        self.Y_Range_split=self.Y_Range.get().split(',')
        if len(self.Y_Range_split)>1:
            self.ax.set_ylim(int(self.Y_Range_split[0]),int(self.Y_Range_split[1]))
        if self.var_grid.get()==True:
            self.ax.grid(color=self.grid_color.get(),axis=self.grid_xy.get(),linestyle=self.grid_linestyle.get(),linewidth=1)#グリッド線を追加。
        else:
            plt.grid(b=None)
        self.fig.tight_layout()       
        self.ax.legend( loc="best")#"best","upper right","upper left","lower left","lower right","right","center right","lower center","upper center","center")
        self.canvas.draw()
        
        
    def line_plot(self,entry,num):
        filename = entry.get()
        try:
            df = pd.read_csv(filename, header=0)
            if num==1:
                x1_split=self.group_data_name_str_x1.split(",")
                y1_split=self.group_data_name_str_y1.split(",")
                x_org = df.loc[:,x1_split[1]]#2列目がheader
                y_org = df.loc[:,y1_split[1]]
                self.ax.plot(x_org,y_org,label=self.legend1.get(), c=self.color1_list_cmbbox.get(),s=int(self.marker_size1.get()))#,marker=str(self.marker1_list_cmbbox))
                
            else:
                x2_split=self.group_data_name_str_x2.split(",")
                y2_split=self.group_data_name_str_y2.split(",")
                x_org = df.loc[:,x2_split[1]]#2列目がheader
                y_org = df.loc[:,y2_split[1]]
                self.ax.plot(x_org,y_org, label=self.legend2.get(), c=self.color2_list_cmbbox.get(),s=int(self.marker_size2.get()))#,marker=str(self.marker2_list_cmbbox))
        except:
            x_org = []
            y_org = []
        self.ax.set_title(self.graph_title.get())
        self.X_Range_split=self.X_Range.get().split(',')
        
        if len(self.X_Range_split)>1:
            self.ax.set_xlim(int(self.X_Range_split[0]),int(self.X_Range_split[1]))
   
        self.Y_Range_split=self.Y_Range.get().split(',')
        if len(self.Y_Range_split)>1:
            self.ax.set_ylim(int(self.Y_Range_split[0]),int(self.Y_Range_split[1]))
        if self.var_grid.get()==True:
            self.ax.grid(color=self.grid_color.get(),axis=self.grid_xy.get(),linestyle=self.grid_linestyle.get(),linewidth=1)#グリッド線を追加。
        else:
            plt.grid(b=None)
        self.fig.tight_layout()       
        self.ax.legend( loc="best")#"best","upper right","upper left","lower left","lower right","right","center right","lower center","upper center","center")
        self.canvas.draw()
        
    
    def read_csv_file(self,entry):
        self.data_list.delete(*self.data_list.get_children())
        self.status_bar_str.set('')
        self.status_bar_str.set('Reading csv files...')
        fType = [('CSV', '*.csv')]
        csv_file_paths = tkfd.askopenfilenames(title='Select csv files',
                                              filetypes=fType)
        entry.delete(0, tk.END)
        entry.insert(tk.END, csv_file_paths)
        if not csv_file_paths:
            self.status_bar_str.set('Select csv files')
            return
        self.dict_data_frame = {}
        for path in csv_file_paths:
            df = pd.read_csv(path)   
            self.insert_data_frame(df, path)
        self.insert_data_list()
        
        self.status_bar_str.set('Finished!!')
    def clear_graph(self):
        self.canvas.get_tk_widget().destroy()
        self.fig = plt.Figure(figsize=(int(self.figsize_tuple[0]),int(self.figsize_tuple[2])))
        self.ax = self.fig.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.fig, self.Frame4)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # ボタンをフレームに配置
        self.toolbar.destroy()
        self.toolbar = NavigationToolbar2Tk(self.canvas, self.Frame4)
        self.toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
   
        self.ax.set_title(self.graph_title.get())
        self.var_grid.set(False)
        if self.var_grid.get()==True:
            self.ax.grid(color=self.grid_color.get(),axis=self.grid_xy.get(),linestyle=self.grid_linestyle.get(),linewidth=1)#グリッド線を追加。
        else:
            plt.grid(b=None)
        self.ax.set_xlabel(self.x_axis_title.get())
        self.ax.set_ylabel(self.y_axis_title.get())

        self.fig.tight_layout()    
        
root = tk.Tk()
app = Application(master=root)
root.minsize(width=1300, height=400)
app.mainloop()

参考にしたgitコードは以下になります。

GitHub - ShisatoYano/CsvDataAnalyzer: This is a visualization ans analysis GUI tool for csv file.
This is a visualization ans analysis GUI tool for csv file. - ShisatoYano/CsvDataAnalyzer

用いたライブラリはnumpy,matplotlib,pandas(いずれもBSDライセンス)です。

最後に

散布図を簡単に描画できるアプリの作成に挑戦してみましたが、想像以上に難しいと感じました。体裁をもう少し整えたかったのですが、体裁を直すと動作しなくなるバグが生じてしまったこともあって詰めきれませんでした。

機能追加したバージョンや、コードの詳細についても今後記事にできれば良いと思っています。

コメント

タイトルとURLをコピーしました