def mainButtons(self): row = 0 col = 0 self.buttons = [] mb = Menubutton(self.root, text="Select Application") mb.menu = Menu(mb, tearoff=0) mb["menu"] = mb.menu for app in APPLICATIONS: mb.menu.add_radiobutton(label=app, variable=self.appVar, command=self.onSelectApp) mb.grid(row=row, column=col, sticky="NEW") col += 1 self.buttons.append(mb) otherButtons = ( ("Run %12s" % self.appVar.get(), self.runApp), ("load Config", self.loadCfg), ("Save Config", self.saveCfg), ("Save Config as", self.saveCfgAs), ("Quit", self.root.quit), ) for text, command in otherButtons: self.buttons.append(Button(self.root, text=text, command=command)) self.buttons[-1].grid(row=row, column=col, sticky="NEW") col += 1 return len(self.buttons)
def _init_general(self): frame_general = Frame(self) self.notebook.add(frame_general, text=_("General")) # --- Language Label(frame_general, text=_("Language")).grid(row=0, column=0, padx=8, pady=4, sticky="e") menu_lang = Menu(frame_general, tearoff=False, background=self._bg) mb = Menubutton(frame_general, menu=menu_lang, textvariable=self.lang) mb.grid(row=0, column=1, padx=8, pady=4, sticky="w") for lang in LANGUAGES: language = LANGUAGES[lang] menu_lang.add_radiobutton(label=language, value=language, variable=self.lang, command=self.translate) # --- gui toolkit Label(frame_general, text=_("GUI Toolkit for the system tray icon")).grid(row=2, column=0, padx=8, pady=4, sticky="e") menu_gui = Menu(frame_general, tearoff=False, background=self._bg) Menubutton(frame_general, menu=menu_gui, width=9, textvariable=self.gui).grid(row=2, column=1, padx=8, pady=4, sticky="w") for toolkit, b in TOOLKITS.items(): if b: menu_gui.add_radiobutton(label=toolkit.capitalize(), value=toolkit.capitalize(), variable=self.gui, command=self.change_gui) # --- Update delay Label(frame_general, text=_("Feed update delay (min)")).grid(row=4, column=0, padx=8, pady=4, sticky="e") self.entry_delay = Entry(frame_general, width=10, justify='center', validate='key', validatecommand=(self._validate, '%P')) self.entry_delay.grid(row=4, column=1, padx=8, pady=4, sticky='w') self.entry_delay.insert( 0, CONFIG.getint('General', 'update_delay') // 60000) # --- image loading timeout Label(frame_general, text=_("Image loading timeout (s)")).grid(row=5, column=0, padx=8, pady=4, sticky="e") self.entry_timeout = Entry(frame_general, width=10, justify='center', validate='key', validatecommand=(self._validate, '%P')) self.entry_timeout.grid(row=5, column=1, padx=8, pady=4, sticky='w') self.entry_timeout.insert( 0, CONFIG.getint('General', 'img_timeout', fallback=10)) # --- Notifications self.notifications = Checkbutton(frame_general, text=_("Activate notifications")) self.notifications.grid(row=6, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'notifications', fallback=True): self.notifications.state(('selected', '!alternate')) else: self.notifications.state(('!selected', '!alternate')) # --- Confirm remove feed self.confirm_feed_rem = Checkbutton( frame_general, text=_("Show confirmation dialog before removing feed")) self.confirm_feed_rem.grid(row=7, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'confirm_feed_remove', fallback=True): self.confirm_feed_rem.state(('selected', '!alternate')) else: self.confirm_feed_rem.state(('!selected', '!alternate')) # --- Confirm remove cat self.confirm_cat_rem = Checkbutton( frame_general, text=_("Show confirmation dialog before removing category")) self.confirm_cat_rem.grid(row=8, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'confirm_cat_remove', fallback=True): self.confirm_cat_rem.state(('selected', '!alternate')) else: self.confirm_cat_rem.state(('!selected', '!alternate')) # --- Confirm update self.confirm_update = Checkbutton( frame_general, text=_("Check for updates on start-up")) self.confirm_update.grid(row=9, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'check_update', fallback=True): self.confirm_update.state(('selected', '!alternate')) else: self.confirm_update.state(('!selected', '!alternate')) # --- Splash supported self.splash_support = Checkbutton( frame_general, text=_("Check this box if the widgets disappear when you click")) self.splash_support.grid(row=10, column=0, padx=8, pady=4, columnspan=2, sticky='w') if not CONFIG.getboolean('General', 'splash_supported', fallback=True): self.splash_support.state(('selected', '!alternate')) else: self.splash_support.state(('!selected', '!alternate'))
class MainWindow(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.grid() self.master.title("Simple Data Modeling") self.createWidgets() self.model_loaded = False self.header_bool = IntVar() # define model types (classification/regression) self.clas_models = { 'Decision Tree Classifier': sklearn.tree.DecisionTreeClassifier, 'Random Forest Classifier': ensemble.RandomForestClassifier, 'Gradient Boosting Classifier': sklearn.ensemble.GradientBoostingClassifier } self.reg_models = { 'Linear Model': sklearn.linear_model.LinearRegression, 'k-Nearest Neighbors Regression': sklearn.neighbors.KNeighborsRegressor, 'Decision Tree Regression': sklearn.tree.DecisionTreeRegressor, 'Gaussian Process Regression': gaussian_process.GaussianProcessRegressor, 'Neural Network': neural_network.MLPRegressor, 'Support Vector Regression': sklearn.svm.SVR, 'XGB Regressor': xgb.XGBRegressor, } # define metric types for evaluation (classification/regression) self.clas_metrics = { "Accuracy Classification Score": metrics.accuracy_score, "Balanced Accuracy": metrics.balanced_accuracy_score, "F1 Score": metrics.f1_score, "Precision": metrics.precision_score, "Recall": metrics.recall_score } self.reg_metrics = { "Mean Absolute Error": metrics.mean_absolute_error, "Mean Squared Error": metrics.mean_squared_error, "R\u00B2 (Coefficient of Determination)": metrics.r2_score } def createWidgets(self): top = self.winfo_toplevel() self.menuBar = Menu(top) top["menu"] = self.menuBar self.subMenu = Menu(self.menuBar, tearoff=0) self.menuBar.add_cascade(label="File", menu=self.subMenu) self.subMenu.add_command(label="New", command=self.readData_createWindow) self.subMenu.add_command(label="Load Model", command=self.laod_model) self.subMenu.add_separator() self.subMenu.add_command(label="Help", command=self.help) self.subMenu.add_command(label="About", command=self.about) self.menuBar.add_command(label="Quit", command=self.master.destroy) def readData_createWindow(self): try: filename = filedialog.askopenfilename() f = open(filename, "rb") self.set_header(self.header_bool) # f = 'http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data' header = self.header_bool.get() == 1 self.data = pd.read_csv(f, header=0 if header else None, sep=',') if not header: self.data.columns = [ 'var' + str(i) for i in range(len(self.data.columns)) ] except AttributeError: pass except Exception as e: return showerror("Error", "Data could not be loaded! Make sure the data is " \ + "formated in a similar way as the sample data: {}.".format(e)) self.cols = self.data.columns self.setUpData() self.bools = [ ] # variables for columns: first axis: which col; second axis: what type height = min(5, self.data.shape[0]) width = len(self.cols) # data frame data_frame = LabelFrame(self.master, text="Data Summary", relief=RIDGE) data_frame.grid(row=0, column=0, columnspan=5, sticky="EWNS", padx=15, pady=7.5) data_size = "Data size is {} kilobytes. " \ .format(np.round(self.data.memory_usage(deep=False).sum() / 1000, 2)) cols_rows = "The number of columns is {} and the number of rows is {}. " \ .format(self.data.shape[1], self.data.shape[0]) miss_data = "The data does have missing values." if self.data.isnull().values.any() \ else "The data does not have missing values." Message(data_frame, text=data_size + cols_rows + miss_data, width=335) \ .grid(row=0, column=0, columnspan=width-1, rowspan=3, sticky='NW') Button(data_frame, text="Data", width=13, command=self.show_data) \ .grid(row=0, column=4, columnspan=1, padx=4, pady=4) Button(data_frame, text="Description", width=13, command=self.show_description) \ .grid(row=1, column=4, columnspan=1, padx=4, pady=0) Button(data_frame, text="Pair Plot", width=13, command=self.pair_plot) \ .grid(row=2, column=4, columnspan=1, padx=4, pady=4) # head frame head_frame = LabelFrame(self.master, text="Head of data", relief=RIDGE) head_frame.grid(row=5, column=0, columnspan=5, sticky="EWNS", padx=15, pady=7.5) for i in range(len(self.cols)): self.bools.append( self.initBools(5, trues=[0, 3] if i < width - 1 else [1, 3])) table = CustomTable(main_window=self, parent=head_frame, dataframe=self.data.head(), editable=False, width=1, height=120) config.apply_options({'fontsize': 10}, table) table.show() # modeling frame model_frame = LabelFrame(self.master, text="Modeling", relief=RIDGE) model_frame.grid(row=height + 11, column=0, columnspan=5, sticky="EWNS", padx=15, pady=7.5) # train / test ratio self.train_test_ratio = 1 / 3 self.train_test_ratio_str = StringVar( self.master, value=str(np.round(self.train_test_ratio, 2))) Button(model_frame, text="Shuffle Data", width=13, command=self.shuffle_data) \ .grid(row=0, column=0, columnspan=1) Button(model_frame, text="TrainTest Ratio", width=13, command=self.set_traintest) \ .grid(row=0, column=2, columnspan=1) Label(model_frame, textvariable=self.train_test_ratio_str) \ .grid(row=0, column=3, columnspan=1, sticky="E") # model selection ttk.Style().configure("TMenubutton", background="#E1E1E1") self.model_btn = Menubutton(model_frame, text="Model", width=9) self.set_model(self.model_btn) self.model_btn.grid(row=1, column=0, columnspan=1, padx=0, pady=4) Button(model_frame, text="Parameters", width=13, command=self.set_model_parameters) \ .grid(row=1, column=1, columnspan=1, padx=0, pady=4) self.metric_btn = Menubutton(model_frame, text="Metric", width=9) self.set_metric(self.metric_btn) self.metric_btn.grid(row=1, column=2, columnspan=1, padx=0, pady=4) # model training self.score = -1 self.score_str = StringVar(self.master, value="") Button(model_frame, text="Train Model", width=13, command=self.startModel) \ .grid(row=1, column=3, columnspan=width-1) Label(model_frame, textvariable=self.score_str) \ .grid(row=3, columnspan=width, sticky='W') # Export Frame export_frame = LabelFrame(self.master, text="Save", relief=RIDGE) export_frame.grid(row=height + 15, column=0, columnspan=5, sticky="EWNS", padx=15, pady=7.5) Button(export_frame, text="Export Data", width=13, command=self.export_data) \ .grid(row=0, column=0, columnspan=1, padx=4, pady=4) Button(export_frame, text="Export Model", width=13, command=self.export_model) \ .grid(row=0, column=1, columnspan=1, padx=0, pady=4) def set_header(self, header_var): """ User selection if data has a header as first row. """ class popupWindow(object): def __init__(self, master): top = self.top = Toplevel(master) top.title('Header') top.attributes("-topmost", True) top.geometry("300x65") top.grid() top.resizable(0, 0) self.e = Checkbutton(top, text="Data contains header", variable=header_var, onvalue=1, offvalue=0) self.e.grid(row=0, column=1, columnspan=1) Button(top, text=' Ok ', command=self.cleanup) \ .grid(row=0, column=2, columnspan=1) top.bind('<Return>', self.cleanup) def cleanup(self, event=None): self.top.destroy() popup = popupWindow(self.master) self.master.wait_window(popup.top) def setUpData(self): self.input_cols = list(np.arange(len(self.cols) - 1)) self.output_cols = [len(self.cols) - 1] self.ignore_cols = [] self.numeric_cols = list(np.arange(len(self.cols))) self.categorical_cols = [] def setColSelection(self, new_btn, col): new_menu = Menu(new_btn, tearoff=0) new_btn["menu"] = new_menu new_menu.add_checkbutton(label="Input", command=self.setIn(col), variable=self.bools[col][0], onvalue=1, offvalue=0) #, #state='disabled' if col < len(self.cols)-1 else 'active') new_menu.add_checkbutton(label="Output", command=self.setOut(col), variable=self.bools[col][1]) new_menu.add_checkbutton(label="Ignore", command=self.setIgnore(col), variable=self.bools[col][2]) new_menu.add_separator() new_menu.add_checkbutton( label="Numeric", command=self.setNum(col), variable=self.bools[col][3]) #, state='disabled') new_menu.add_checkbutton(label="Categorical", command=self.setCat(col), variable=self.bools[col][4]) return new_menu def initBools(self, n, trues=[0, 3]): return [ BooleanVar(self.master, value=True if i in trues else False) for i in range(n) ] ### functions for feature settings ### def setIn(self, col): def _setIn(): if col in self.input_cols: pass else: self.input_cols.append(col) self.input_cols = sorted(self.input_cols) try: del self.output_cols[self.output_cols.index(col)] except: pass try: del self.ignore_cols[self.ignore_cols.index(col)] except: pass self.bools[col][0].set(value=True) self.bools[col][1].set(value=False) self.bools[col][2].set(value=False) return _setIn def setOut(self, col): def _setOut(): if col in self.output_cols: pass else: try: del self.input_cols[self.input_cols.index(col)] except: pass try: del self.ignore_cols[self.ignore_cols.index(col)] except: pass if len(self.output_cols) == 1: self.input_cols.append(self.output_cols[0]) self.bools[self.output_cols[0]][0].set(value=True) self.bools[self.output_cols[0]][1].set(value=False) self.output_cols = [col] self.bools[col][0].set(value=False) self.bools[col][1].set(value=True) self.bools[col][2].set(value=False) return _setOut def setIgnore(self, col): def _setOut(): if col in self.ignore_cols: pass else: self.ignore_cols.append(col) self.ignore_cols = sorted(self.ignore_cols) try: del self.input_cols[self.input_cols.index(col)] except: pass try: del self.output_cols[self.output_cols.index(col)] except: pass self.bools[col][0].set(value=False) self.bools[col][1].set(value=False) self.bools[col][2].set(value=True) return _setOut def setNum(self, col): def _setNum(): if col in self.numeric_cols: pass else: self.numeric_cols.append(col) self.numeric_cols = sorted(self.numeric_cols) try: del self.categorical_cols[self.categorical_cols.index(col)] except: pass self.bools[col][3].set(value=True) self.bools[col][4].set(value=False) return _setNum def setCat(self, col): def _setCat(): # showinfo("Information", "Categorical variables not supported yet!") if col in self.categorical_cols: pass else: self.categorical_cols.append(col) self.categorical_cols = sorted(self.categorical_cols) try: del self.numeric_cols[self.numeric_cols.index(col)] except: pass self.bools[col][3].set(value=False) self.bools[col][4].set(value=True) return _setCat ### ------------------------------ ### def properties(self): pass def help(self): instructions = "Instructions\n" \ + "1) Load your data which is formated as csv file.\n" \ + "2) Make sure loading worked by checking the displayed ´Head of Data´\n" \ + " or pressing the ´Data´ button to see the whole dataset.\n" \ + "3) In the ´Head of Data´ press the column names to change their type" \ + " and to select the output feature.\n" \ + "4) Select your appropriate model and change its parameters if needed.\n" \ + "5) Save your predictions and model.\n" showinfo("Information", instructions) def about(self): showinfo("About", "This Application is for small data modeling tasks.\n\n" \ + "Author\t{}\nVersion\t{}".format(__author__, __version__)) def set_traintest(self): train_test_ratio = self.train_test_ratio_str.get() class popupWindow(object): def __init__(self, master): top = self.top = Toplevel(master) top.title('Train-Test Ratio') top.attributes("-topmost", True) top.geometry("350x80") top.grid() top.resizable(0, 0) Label(top, text="Enter the ratio of training set " \ + "size to test set size:").grid(row=0, columnspan=6, sticky='nesw', padx=15, pady=7.5) self.e = Entry(top) self.e.insert(END, str(train_test_ratio)) self.e.grid(row=1, column=0, columnspan=5, sticky='w', padx=15) Button(top, text=' Ok ', command=self.cleanup) \ .grid(row=1, column=5, columnspan=1, sticky='e', padx=15) top.bind('<Return>', self.cleanup) def cleanup(self, event=None): self.value = self.e.get() self.top.destroy() popup = popupWindow(self.master) self.master.wait_window(popup.top) try: train_test_ratio = float(popup.value) if train_test_ratio > 1 or train_test_ratio <= 0: showerror("Error", "Train-Test ratio must be in (0,1]!") else: self.train_test_ratio = train_test_ratio self.train_test_ratio_str.set( str(np.round(self.train_test_ratio, 2))) except AttributeError: pass except: showerror("Error", "Train-Test ratio must be a value in (0,1]!") def set_model(self, btn): new_menu = Menu(btn, tearoff=0) btn["menu"] = new_menu self.model_selection_bool = self.initBools(len(self.reg_models) + len(self.clas_models), trues=[]) for i, model in enumerate(self.reg_models.keys()): new_menu.add_checkbutton(label=model, command=self.setModelType(i), variable=self.model_selection_bool[i]) new_menu.add_separator() for i, model in enumerate(self.clas_models.keys()): j = len(self.reg_models) + i new_menu.add_checkbutton(label=model, command=self.setModelType(j), variable=self.model_selection_bool[j]) return new_menu def set_metric(self, btn): new_menu = Menu(btn, tearoff=0) btn["menu"] = new_menu self.metric_selection_bool = self.initBools(len(self.reg_metrics) + len(self.clas_metrics), trues=[]) for i, metric in enumerate(self.reg_metrics.keys()): new_menu.add_checkbutton(label=metric, command=self.setMetricType(i), variable=self.metric_selection_bool[i]) new_menu.add_separator() for i, metric in enumerate(self.clas_metrics.keys()): new_menu.add_checkbutton( label=metric, command=self.setMetricType(len(self.reg_metrics) + i), variable=self.metric_selection_bool[len(self.reg_metrics) + i]) return new_menu def setModelType(self, model_index, default_params=True): def _setModelType(): if len(self.output_cols) == 0: showerror("Error", "No output variable selected!") if self.output_cols[0] in self.numeric_cols: if model_index >= len(self.reg_models): # regression for j in range(len(self.model_selection_bool)): self.model_selection_bool[j].set(value=False) return showerror("Error", "This model is for classification!") elif self.output_cols[0] in self.categorical_cols: if model_index < len(self.reg_models): for j in range(len(self.model_selection_bool)): self.model_selection_bool[j].set(value=False) return showerror("Error", "This model is for regression!") for j in range(len(self.model_selection_bool)): self.model_selection_bool[j].set(value=False) self.model_selection_bool[model_index].set(value=True) # get selected model type for i, b in enumerate(self.model_selection_bool): if b.get(): self.model_int = i try: if self.output_cols[0] in self.numeric_cols: # regression model = self.reg_models[list( self.reg_models.keys())[self.model_int]] model_name = list(self.reg_models.keys())[self.model_int] elif self.output_cols[ 0] in self.categorical_cols: # classification model_int = self.model_int - len(self.reg_models) model = self.clas_models[list( self.clas_models.keys())[model_int]] model_name = list(self.clas_models.keys())[model_int] except Exception as e: return showerror( "Error", "An appropriate model has to be selected: {}".format(e)) self.model_name = model_name self.model = model # get and set default model parameters if default_params: signature = inspect.signature(model) self.model_dict = { k: v.default for k, v in signature.parameters.items() if v.default is not inspect.Parameter.empty } # transformation for regression tasks if self.output_cols[0] in self.numeric_cols: def transformedTargetRegressor(**kwargs): return TransformedTargetRegressor( regressor=model(**kwargs), transformer=MinMaxScaler()) model = transformedTargetRegressor return _setModelType def set_model_parameters(self): """ """ try: model_name, model, model_dict = self.model_name, self.model, self.model_dict except Exception as e: return showerror("Error", "An model has to be selected first: {}".format(e)) class popupWindow(object): def __init__(self, master): top = self.top = Toplevel(master) top.title("Parameters") top.attributes("-topmost", True) width = str(26 * (len(model_dict) + 4)) top.geometry("310x" + width) top.grid() top.resizable(0, 0) frame = LabelFrame(top, text=model_name, relief=RIDGE, width=260) frame.grid(row=0, column=0, columnspan=2, sticky="EWNS", padx=15, pady=15) self.values = list() line = 1 for k, v in model_dict.items(): Label(frame, text=k, width=20) \ .grid(row=line, column=0, columnspan=1, sticky='W', padx=4, pady=1) e = Entry(frame, width=10) e.insert(END, str(v)) e.grid(row=line, column=1, columnspan=1, sticky='E', padx=6, pady=1) self.values.append(e) line += 1 Button(frame, text=' Help ', command=self.help) \ .grid(row=line+2, column=0, columnspan=1, sticky='W', padx=4, pady=4) Button(frame, text=' Ok ', command=self.cleanup) \ .grid(row=line+2, column=1, columnspan=1, sticky='E', padx=4, pady=4) top.bind('<Return>', self.cleanup) def cleanup(self, event=None): for i, k in enumerate(model_dict.keys()): param_type = type(model_dict[k]) try: value = self.values[i].get() if param_type in (type(tuple()), type(list())): model_dict[k] = eval(value) elif param_type is not type(None): model_dict[k] = param_type(value) elif value == 'True' or value == 'False': model_dict[k] = bool(value) else: # default might be NoneType which we don't want try: model_dict[k] = float(value) except: try: model_dict[k] = int(value) except: model_dict[k] = None self.model_dict = model_dict except Exception as e: showerror("Error", "The parameter type of {} is wrong: {}." \ .format(k, type(e))) self.top.destroy() def help(self, event=None): model_class = str(model) start = model_class.find('\'') + 1 end = model_class.rfind('\'') keyword = model_class[start:end] keyword = keyword.replace('.', ' ').replace('_', ' ').replace(' ', ' ') webbrowser.open('https://google.com/search?q=' + keyword) popup = popupWindow(self.master) self.master.wait_window(popup.top) try: self.model_dict = popup.model_dict except AttributeError: pass def setMetricType(self, metric_index): def _setMetricType(): if len(self.output_cols) == 0: showerror("Error", "No output variable selected!") if self.output_cols[0] in self.numeric_cols: if metric_index >= len(self.reg_metrics): # regression for j in range(len(self.metric_selection_bool)): self.metric_selection_bool[j].set(value=False) return showerror("Error", "This metric is for classification!") elif self.output_cols[0] in self.categorical_cols: if metric_index < len(self.reg_metrics): for j in range(len(self.metric_selection_bool)): self.metric_selection_bool[j].set(value=False) return showerror("Error", "This metric is for regression!") for j in range(len(self.metric_selection_bool)): self.metric_selection_bool[j].set(value=False) self.metric_selection_bool[metric_index].set(value=True) # get selected metric type for i, b in enumerate(self.metric_selection_bool): if b.get(): self.metric_int = i # set name and metric itself if self.output_cols[0] in self.numeric_cols: self.metric_name = list( self.reg_metrics.keys())[self.metric_int] self.metric = self.reg_metrics[self.metric_name] elif self.output_cols[0] in self.categorical_cols: metric_int = self.metric_int - len(self.reg_metrics) self.metric_name = list(self.clas_metrics.keys())[metric_int] self.metric = self.clas_metrics[self.metric_name] return _setMetricType def show_data(self): """ Opens a new window showing the data. """ data = self.data class DataFrame(Frame): def __init__(self, master=None): top = self.top = Toplevel(master) top.attributes("-topmost", True) top.geometry('600x720') top.title('Data') self.table = pt = Table(top, dataframe=data, editable=False, enable_menus=False) pt.show() return popup = DataFrame(self.master) self.master.wait_window(popup.top) def show_description(self): """ Opens a new window showing the a description of the data. """ data = self.data.describe() data.reset_index(level=0, inplace=True) data.rename(columns={"index": ""}, inplace=True) class DataFrame(Frame): def __init__(self, master=None): top = self.top = Toplevel(master) top.attributes("-topmost", True) top.geometry('550x220') top.title('Data Description') self.table = pt = Table(top, dataframe=data, editable=False, enable_menus=False) pt.show() return popup = DataFrame(self.master) self.master.wait_window(popup.top) def pair_plot(self, limit: int = 50): """ Draws a seaborn pairplot of the current data selection. Arguments --------- limit: int, maximum number of data points to plot. Returns ------- - """ if len(self.input_cols) + len(self.output_cols) <= 1: return showerror("Error", "No features selected to plot.") if len(self.output_cols) == 1: hue = self.cols[self.output_cols[0]] if \ self.bools[self.output_cols[0]][4].get() else None else: hue = None if limit < self.data.shape[0]: showinfo("Info", "Showing only the first {} entries.".format(limit)) sns.pairplot(self.data[self.cols[self.input_cols + self.output_cols]][:limit], hue=hue, height=1.5, aspect=1.) plt.gcf().canvas.set_window_title('Pair Plot of Data') plt.get_current_fig_manager().window.setWindowIcon( QtGui.QIcon(os.path.join(os.path.dirname(__file__), 'icon.ico'))) plt.show() def shuffle_data(self): """ Shuffle the rows of the data. """ self.data = self.data.sample(frac=1) class Notification(Frame): def __init__(self, master=None): top = self.top = Toplevel(master) top.attributes("-topmost", True) window_height = 75 window_width = 225 screen_width = top.winfo_screenwidth() screen_height = top.winfo_screenheight() x_cordinate = int((screen_width / 2) - (window_width / 2)) y_cordinate = int((screen_height / 2) - (window_height / 2)) top.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate)) top.title('') Label(top, text="\nShuffeld\n", anchor=CENTER).pack() return popup = Notification(self.master) self.after(500, lambda: popup.top.destroy()) def startModel(self): """ Main action for trainig the selected model on the data. """ error_msg = "" if len(self.output_cols) != 1: error_msg += " * There must be exactly one dependent variable.\n" if len(self.input_cols) < 1: error_msg += " * There must be at least one independent variable.\n" if not hasattr(self, 'model'): error_msg += " * A model has to be selected.\n" if not hasattr(self, 'metric_int'): error_msg += " * A metric has to be selected.\n" if len(error_msg) > 0: return showerror("Error", 'Model training failed!\n' + error_msg) # split data X_train, X_test, y_train, y_test = train_test_split( self.data[self.cols[self.input_cols]], self.data[self.cols[self.output_cols]], test_size=self.train_test_ratio) numeric_transformer = Pipeline( steps=[('imputer', SimpleImputer( strategy='median')), ('scaler', StandardScaler())]) categorical_transformer = Pipeline( steps=[('imputer', SimpleImputer(strategy='constant', fill_value='missing') ), ('onehot', OneHotEncoder(handle_unknown='ignore'))]) preprocessor = ColumnTransformer( transformers=[('num', numeric_transformer, self.cols[list( set(self.numeric_cols) - set(self.ignore_cols + self.output_cols))]), ('cat', categorical_transformer, self.cols[list( set(self.categorical_cols) - set(self.ignore_cols + self.output_cols))])]) model = Pipeline(steps=[('preprocessor', preprocessor), ('model', Model(self.master, self.model, self.model_name, self.model_dict))]) try: model.fit(X_train, y_train) except ValueError as e: return showerror( "Error", "Could not train the model (ValueError): {}".format(e)) except Exception as e: return showerror("Error", "Could not train the model: {}.".format(e)) # make prediction try: y_pred = model.predict(X_test) self.data["predictions"] = model.predict( self.data[self.cols[self.input_cols]]) except Exception as e: return showerror("Error", "Failed to calculate predictions: {}".format(e)) # evaluate model try: self.score = np.round(self.metric(y_test, y_pred), 4) showinfo("Result", "The model {} scored a {} of {} on the test data." \ .format(self.model_name, self.metric_name, self.score)) self.score_str.set( "The current model scores {} on the test data.".format( str(np.round(self.score, 4))) if self.score != -1 else "") except Exception as e: return showerror("Error", "Failed to evaluate the predictions with " \ + "the specified metric: {}.".format(e)) def export_data(self): """ Save preprocessed data and predictions (if made). """ try: export_file_path = filedialog.asksaveasfilename( filetypes=[("default", "*.csv")], defaultextension='.csv') self.data.to_csv(export_file_path, index=False, header=True) except FileNotFoundError: pass except Exception as e: showerror("Error", "Data could not be saved: {}.".format(e)) def export_model(self): """ Save the model on disk. """ try: export_file_path = filedialog.asksaveasfilename( filetypes=[("default", "*.pkl")], defaultextension='.pkl') saving = (self.model_name, self.model_dict, self.model, self.model_int, self.metric_int) pickle.dump(saving, open(export_file_path, "wb")) except (AttributeError, FileNotFoundError): pass except Exception as e: showerror("Error", "Failed to save model: {}.".format(e)) def laod_model(self): try: filename = filedialog.askopenfilename(filetypes=[("default", "*.pkl")], defaultextension='.pkl') self.model_name, self.model_dict, self.model, self.model_int, \ self.metric_int = pickle.load(open(filename, 'rb')) self.model_loaded = True self.setModelType(self.model_int, default_params=False)() self.setMetricType(self.metric_int)() except FileNotFoundError: pass except Exception as e: showerror("Error", "Data could not be saved: {}.".format(e))
class ExpCheckGUI(Tk): def __init__(self, data_manager): super().__init__() self.data_manager = data_manager self.searching = False self.good_chars = printable[:-5] + '\x08' self.add_gui = AddFoodGUI(self) self.init_gui() def init_gui(self): """ Initializes all widgets for the main screen and calls grid_widgets() to place them """ self.title("exp_check") self.config(bg='black') style = Style() style.configure("TMenubutton", foreground="white", background="black") # setup widgets self.add_button = Button(self, text="Add Food", **widget_options, command=self.add_food) self.delete_button = Button(self, text="Delete Food", **widget_options, command=self.delete_food) self.search_box = Entry(self, **widget_options, insertbackground='white') self.search_box.bind("<Key>", self.search) self.data_display = DataDisplay(widget_options) vals = self.data_manager.get_keylist() vals.insert(0, 'name') menu1 = Menu(tearoff=0) menu1.add('radiobutton', label=vals[0], command=lambda: self.update_data_display(sort_key=vals[0])) menu1.add('radiobutton', label=vals[1], command=lambda: self.update_data_display(sort_key=vals[1])) menu1.add('radiobutton', label=vals[2], command=lambda: self.update_data_display(sort_key=vals[2])) self.sort_menu = Menubutton(self, text='sort by', menu=menu1, style="TMenubutton") search_icon = self.generate_icon_object("./data/search_icon.png", (20, 20)) self.search_label = Label(image=search_icon, **widget_options) self.search_label.image = search_icon # setup message box dialog self.message = Label(self, text='', **widget_options) self.okay_button = Button( self, text=" OK ", **widget_options, command=lambda: self.okay_button_press(self.cur_instance)) self.grid_widgets() def grid_widgets(self): """ Places initialized widgets on screen """ self.title("exp_check") self.cur_instance = self self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.rowconfigure(2, weight=1) grid_options = {"padx": 3, "pady": 3} self.add_button.grid(row=0, column=0, **grid_options) self.delete_button.grid(row=0, column=1, **grid_options) self.data_display.grid(row=2, column=0, **grid_options, columnspan=6, sticky=N + S + E + W) self.search_label.grid(row=0, column=3) self.search_box.grid(row=0, column=4, **grid_options) self.sort_menu.grid(row=0, column=5, **grid_options) self.update_data_display() def generate_icon_object(self, path, size): """ Initializes the an icon with @param path so it is ready to be placed inside a label widget to be placed on screen """ image = Image.open(path) image.convert("RGBA") image.thumbnail(size, Image.ANTIALIAS) return ImageTk.PhotoImage(image) def update_data_display(self, event=None, sort_key='name', key_list=None, search_string=None): """ Synchronizes the data display with the database from the data manager """ if key_list is None: key_list = self.data_manager.get_keylist() self.data_display.delete(0, END) for item in self.data_manager.get_database(key_list=key_list, sort_key=sort_key): item_str = "{} {} {}".format(*item) if search_string is None or\ search_string in item_str.lower(): item[1] = format_string.format(item[1]) item[2] = format_string.format(item[2]) self.data_display.insert(END, item) def search(self, event=None): """ Updates the data display with whatever is in the search box. Will run any time a key is pressed while focused on the search box. """ search_string = self.search_box.get() if event is None or event.char not in self.good_chars: newchar = "" elif event.char == '\x08': search_string = search_string[:-1] newchar = "" else: newchar = event.char search_string += newchar search_string = search_string.lower() self.update_data_display(search_string=search_string) def un_grid_widgets(self, event=None): """ Removes all the current widgets """ self.add_button.grid_forget() self.delete_button.grid_forget() self.data_display.grid_forget() self.search_label.grid_forget() self.search_box.grid_forget() self.sort_menu.grid_forget() def add_food(self, event=None): """ Removes all the current widgets and draws draws the add_food_gui widgets on the window. """ self.un_grid_widgets() self.add_gui.grid_widgets() def delete_food(self, event=None): """ Selects and deletes a food from the database by calling the data manager """ selection = self.data_display.get(ACTIVE).split() if not selection: return name = ' '.join( selection[0:-2]) if len(selection) > 3 else selection[0] msg = self.data_manager.delete_entry(name) if msg is not None: self.msg_box_init(self, msg) return self.update_data_display() def msg_box_init(self, instance, msg='a message'): """ Creates a message box after deleting all removing all widgets from the screen """ instance.un_grid_widgets() self.message['text'] = msg self.message.grid(row=0, column=0) self.okay_button.grid(row=1, column=0) def okay_button_press(self, instance, event=None): """ On pressing the okay button, replaces all changed widgets """ self.message.grid_forget() self.okay_button.grid_forget() instance.grid_widgets()