Hello I always get the error "Error: float() argument must be a string or a number, not 'dict_keys'" when running this code. I thought i ran it before without any problem but it seems like now it does not work anymore. Is that possible? And if so, what can I do to get it working again? My problem is within the very last part where I want to graph the time it took to do calculations. But I guess maybe one of the earlier variables isnt defined correctly or something? Appreaciate your help!
Thank you!
from sklearn.linear_model import LinearRegression
import numpy as np
from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt #biblioteca de gráficos a partir de listas o arreglos
import time
import threading #para hacer subprocesos
import gc #gc.collect va borrando lo de la memoria
#Definicion de filas y columnas (Parte de arriba)
np.random.seed(42)
n_rows = 100 #número fijo de filas
cols_min = 100 #comienzo numero de columnas
cols_max = 12_800 #máximo de columnas para que salga del loop
timeout = 90 # segundos maximos para correr la iteracion, se puede ir modificando para testeo
#Generaremos funciones para ir llamando
def mult_range(start, end): #Finalidad es que irá duplicando las columnas
val = start
while val <= end:
yield val
val *= 2
#gen_dataset irá generando un conjunto de datos, partiendo de 100 columnas que son las fijadas en la parte de arriba
#la gracia es tener por separado x e y para luego hacer en una linea los gráficos
def gen_dataset(n_rows, n_cols):
y = np.random.rand(n_rows) #crea la matriz y la rellena con el valor de n_rows // las n filas
x = np.random.rand(n_rows, n_cols) #para la matriz n filas m columnas
return x, y
#cromometrará el tiempo de cálculo
def timeit(timeout, n_cols, result, fn, *args, **kwargs):
try:# función que puede llegar a levantar una excepción
#link: para info https://uniwebsidad.com/libros/algoritmos-python/capitulo-12/excepciones
t1 = datetime.now() #valor actual
fn(*args, **kwargs)
t2 = datetime.now() #valor nuevo
delta = t2 - t1 #diferencia
delta_microsecs = delta.seconds * 1_000_000 + delta.microseconds #tiempo que tardará la ejecución
if (delta_microsecs / 1_000_000) < (timeout + 500_000): #condición para que imprima lo que tarda
#irá imprimiendo instantaneamente en la pantalla para saber el tiempo que tarda por cantidad
print(f"for {n_cols} columns it took {delta_microsecs / 1_000_000} seconds")
#imprime para n columnas tarda delta_microsecs de tiempo
result[n_cols] = delta_microsecs #
#else:
# print(f"for {n_cols} columns it took {delta_microsecs / 1_000_000} seconds")
except MemoryError: #cuando se genera la excepción
#en caso de que utilice muchos recursos del pc
print(f"data doesn't fit in memory for {n_cols} columns")
#regresion con sklearn para las x e y
def sklearn_reg(x, y):
reg = LinearRegression()
reg.fit(x, y) #toma los valor x e y ya definidos
#regresion con numpy para las x e y
def np_reg(x, y): #toma los valor x e y ya definidos
# agregar columna de unos
x_ = np.hstack((np.ones((x.shape[0], 1)), x))
# estimador (x'x)^-1 * (x'y)
return np.matmul(np.linalg.inv(np.matmul(x_.T, x_)),np.matmul(x_.T, y))
#regresion con timeout segundos m columnas de inicio, m++ columnas finales
def time_reg(timeout, cols_min, cols_max, reg_fn, square=False): #square es para poner cuando sea X2
timing = {}
threads = []
for n_cols in mult_range(cols_min, cols_max):
try:
gc.collect()
x, y = gen_dataset(n_rows, n_cols)
if square:
x = np.square(x)
thread = threading.Thread(target=timeit, args=(timeout, n_cols, timing, reg_fn, x, y))
thread.start()
for i in range(timeout):
time.sleep(1)
if not thread.is_alive():
break
else:
print(f"for {n_cols} columns it took more than {timeout} seconds")
except MemoryError:
print(f"data doesn't fit in memory for {n_cols} columns")
return timing
def plot_time(timing):
fig = plt.figure(figsize=(10, 10))
plt.plot(timing.keys(), timing.values())
#plt.axis((0, 1_000_000, 0, 1_000_000))
plt.xlabel('time (μs)')
plt.ylabel('columns')
plt.show()
plot_time(time_reg(timeout, cols_min, cols_max, sklearn_reg))
#generar gráfico con el tiempo que demora según cantidad de columnas
plot_time(time_reg(timeout, cols_min, cols_max, np_reg))
#generar gráfico con el tiempo que demora según cantidad de columnas
``
This is my code:
import pandas as pd
import os
import glob as g
archivos = g.glob('C:\Users\Desktop\*.csv')
for archiv in archivos:
nombre = os.path.splitext(archiv)[0]
df = pd.read_csv(archiv, sep=",")
d = pd.to_datetime(df['DATA_LEITURA'], format="%Y%m%d")
df['FECHA_LECTURA'] = d.dt.date
del df['DATA_LEITURA']
df['CONSUMO']=""
df['DIAS']=""
df["SUMDIAS"]=""
df["SUMCONS"]=""
df["CONSANUAL"] = ""
ordenado = df.sort_values(['NR_CPE','FECHA_LECTURA', 'HORA_LEITURA'], ascending=True)
##Agrupamos por el CPE
agrupado = ordenado.groupby('NR_CPE')
for name, group in agrupado: #Recorremos el grupo
indice = group.index.values
inicio = indice[0]
fin = indice[-1]
#Llenamos la primeras lectura de cada CPE, con esa lectura (porque no hay una lectura anterior)
ordenado.CONSUMO.loc[inicio] = 0
ordenado.DIAS.loc[inicio] = 0
cont=0
for i in indice: #Recorremos lo que hay dentro de los grupos, dentro de los CPES(lecturas)
if i > inicio and i <= fin :
cont=cont+1
consumo = ordenado.VALOR_LEITURA[indice[cont]] - ordenado.VALOR_LEITURA[indice[cont-1]]
dias = (ordenado.FECHA_LECTURA[indice[cont]] - ordenado.FECHA_LECTURA[indice[cont-1]]).days
ordenado.CONSUMO.loc[i] = consumo
ordenado.DIAS.loc[i] = dias
# Hago las sumatorias, el resultado es un objeto DataFrame
dias = agrupado['DIAS'].sum()
consu = agrupado['CONSUMO'].sum()
canu = (consu/dias) * 365
#Contador con el numero de courrencias de los campos A,B y C
conta=0
contb=0
contc=0
#Como es un DF, para recorrerlo tengo que iterar sobre ellos para hacer la comparacion
print "Grupos:"
for ind, sumdias in dias.iteritems():
if sumdias <= 180:
grupo = "A"
conta=conta+1
elif sumdias > 180 and sumdias <= 365:
grupo = "B"
contb=contb+1
elif sumdias > 365:
grupo = "C"
contc=contc+1
print "grupo A: " , conta
print "grupo B: " , contb
print "grupo C: " , contc
#Formateamos los campos para no mostrar todos los decimales
Fdias = dias.map('{:.0f}'.format)
Fcanu = canu.map('{:.2f}'.format)
frames = [Fdias, consu, Fcanu]
concat = pd.concat(frames,axis=1).replace(['inf','nan'],[0,0])
with open('C:\Users\Documents\RPE_PORTUGAL\Datos.csv','a') as f:
concat.to_csv(f,header=False,columns=['CPE','DIAS','CONSUMO','CONSUMO_ANUAL'])
try:
ordenado.to_excel(nombre+'.xls', columns=["NOME_DISTRITO",
"NR_CPE","MARCA_EQUIPAMENTO","NR_EQUIPAMENTO","VALOR_LEITURA","REGISTADOR","TIPO_REGISTADOR",
"TIPO_DADOS_RECOLHIDOS","FACTOR_MULTIPLICATIVO_FINAL","NR_DIGITOS_INTEIRO","UNIDADE_MEDIDA",
"TIPO_LEITURA","MOTIVO_LEITURA","ESTADO_LEITURA","HORA_LEITURA","FECHA_LECTURA","CONSUMO","DIAS"],
index=False)
print (archiv)
print ("===============================================")
print ("*****Se ha creado el archivo correctamente*****")
print ("===============================================")
except IOError:
print ("===================================================")
print ("¡¡¡¡¡Hubo un error en la escritura del archivo!!!!!")
print ("===================================================")
This takes a file where I have lectures of energy consumption from different dates for every light meter('NR_CPE') and do some calculations:
Calculate the energy consumption for every 'NR_CPE' by substracting the previous reading with the next one and the result put in a new column named 'CONSUMO'.
Calculate the number of days where I'v got a reading and sum up the number of days
Add the consumption for every 'NR_CPE' and calculate the anual consumption.
Finally I want to classify by number of days that every light meter('NR_CPE') has a lecture. A if it has less than 180 days, B between 180 and 1 year and C more than a year.
And finally write this result in two differents files.
Any idea of how should I re-code this to have the same output and be faster?
Thank you all.
BTW this is my dataset:
,NOME_DISTRITO,NR_CPE,MARCA_EQUIPAMENTO,NR_EQUIPAMENTO,VALOR_LEITURA,REGISTADOR,TIPO_REGISTADOR,TIPO_DADOS_RECOLHIDOS,FACTOR_MULTIPLICATIVO_FINAL,NR_DIGITOS_INTEIRO,UNIDADE_MEDIDA,TIPO_LEITURA,MOTIVO_LEITURA,ESTADO_LEITURA,DATA_LEITURA,HORA_LEITURA
0,GUARDA,A002000642VW,101,1865411,4834,001,S,1,1,4,kWh,1,1,A,20150629,205600
1,GUARDA,A002000642VW,101,1865411,4834,001,S,1,1,4,kWh,2,2,A,20160218,123300
2,GUARDA,A002000642VJ,122,204534,25083,001,S,1,1,5,kWh,1,1,A,20150629,205700
3,GUARDA,A002000642VJ,122,204534,27536,001,S,1,1,5,kWh,2,2,A,20160218,123200
4,GUARDA,A002000642HR,101,1383899,11734,001,S,1,1,5,kWh,1,1,A,20150629,205600
5,GUARDA,A002000642HR,101,1383899,11800,001,S,1,1,5,kWh,2,2,A,20160218,123000
6,GUARDA,A002000995VM,101,97706436,12158,001,S,1,1,5,kWh,1,3,A,20150713,155300
7,GUARDA,A002000995VM,101,97706436,12163,001,S,1,1,5,kWh,2,2,A,20160129,162300
8,GUARDA,A002000995VM,101,97706436,12163,001,S,1,1,5,kWh,2,2,A,20160202,195800
9,GUARDA,A2000995VM,101,97706436,12163,001,S,1,1,5,kWh,1,3,A,20160404,145200
10,GUARDA,A002000996LV,168,5011703276,3567,001,V,1,1,6,kWh,1,1,A,20150528,205900
11,GUARDA,A02000996LV,168,5011703276,3697,001,V,1,1,6,kWh,2,2,A,20150929,163500
12,GUARDA,A02000996LV,168,5011703276,1287,002,P,1,1,6,kWh,1,1,A,20150528,205900
Generally you want to avoid for loops in pandas.
For example, the first loop where you calculate total consumption and days could be rewritten as a groupby apply something like:
def last_minus_first(df):
columns_of_interest = df[['VALOR_LEITURA', 'days']]
diff = columns_of_interest.iloc[-1] - columns_of_interest.iloc[0]
return diff
df['date'] = pd.to_datetime(df['DATA_LEITURA'], format="%Y%m%d")
df['days'] = (df['date'] - pd.datetime(1970,1,1)).dt.days # create days column
df.groupby('NR_CPE').apply(last_minus_first)
(btw I don't understand why you are subtracting each entry from the previous, surely for meter readings this is the same as last-first?)
Then given the result of the above as consumption, you can replace your second for loop (for ind, sumdias in dias.iteritems()) with something like:
pd.cut(consumption.days, [-1, 180, 365, np.inf], labels=['a', 'b', 'c']).value_counts()
I'm trying to call these 3 class variables from another script, by importing the calls onto the new script. This is how it's suppose to work, when the logIn.py runs if you typed the correct user and password, they're saved in user and password variables. if you fail, 1 is added to the fail variable. At 1st I had the like this fail = 0, user = "", password = "" cause I was getting undefined errors. Everything worked, but when I create an object of that class, everything was again initialized to fail = 0, user = "", password = "". So I thought "maybe if I use StringVar and IntVar that'll fix the problem". But instead I get this error.
Traceback (most recent call last):
File "C:\Users\devilboy 4\Documents\Visual Studio 2013\Projects\mainPage3\mainPage3\logIn_screen.py", line 9, in <module>
class checkValidation:
File "C:\Users\devilboy 4\Documents\Visual Studio 2013\Projects\mainPage3\mainPage3\logIn_screen.py", line 11, in checkValidation
fail = IntVar()
File "C:\Python31\lib\tkinter\__init__.py", line 265, in __init__
Variable.__init__(self, master, value, name)
File "C:\Python31\lib\tkinter\__init__.py", line 174, in __init__
self._tk = master.tk
AttributeError: 'NoneType' object has no attribute 'tk'
I'm pretty sure it's related to the String,IntVar. If this works properly, on mainPage3 I'll generate a report and these 3 variables are some the ones I'll use.
logIn_screen.py:
from tkinter import *
import os
import tkinter.messagebox
#check si lo entrado es correcto
class checkValidation:
fail = IntVar()
user = StringVar()
password = StringVar()
#valida el user y el pass
def fullVali(self, name, passwd):
if name == "" or name == " ":
tkinter.messagebox.showinfo( "Error","Dejo el usuario en blanco")
self.fail+= 1
elif name != "UmetSeg":
tkinter.messagebox.showinfo( "Error","Usuario incorrecto")
self.fail+= 1
else :
self.user = name
tkinter.messagebox.showinfo( "ok","dude" + name)
if passwd == "" or passwd == " ":
tkinter.messagebox.showinfo( "Error","Dejo la password en blanco")
self.fail+= 1
elif passwd != "SegUmet":
tkinter.messagebox.showinfo( "Error","Password incorrecto")
self.fail+= 1
else :
self.password = passwd
tkinter.messagebox.showinfo( "ok","dude" + passwd)
form.destroy()
#open another py script
os.system("mainPage3.py 1")
return
# no deja pasar parametros por command en el boton a menos que se por lambda, so corre este metodo para
#correr el metodo de validar
def callVali(self):
user = usrIn.get()
self.fullVali(usrIn.get(), passIn.get())
return
def getUser(self):
return self.user
def getPass(self):
return self.password
#este if es para que corra esto si se corre el programa y no si se importa la clase
if __name__ == "__main__":
vali = checkValidation()
form = Tk()
form.title("LogIn")
form.geometry("300x320+300+200")
#User txtBox
usrIn = Entry(form, textvariable = None, width = 30)
usrIn.place(x = 60, y = 140)
user = usrIn.get()
#Passwd txtBox - show es para que se vea eso pero el texto sea lo que se escribe
passIn = Entry(form, show = "*", textvariable = None, width = 30)
passIn.place(x = 60, y = 200)
#Username Label
usrLblVal = StringVar()
usrLblVal.set("User name")
usrLbl = Label(form, textvariable = usrLblVal )
usrLbl.place(x = 120, y = 115)
#Passwrd label
passLblVal = StringVar()
passLblVal.set("Password")
passLbl = Label(form, textvariable = passLblVal )
passLbl.place(x = 120, y = 175)
#Login btn
btn = Button(form, text = "Entrar", width = 10, command = vali.callVali)
btn.place(x = 110, y = 250)
form.mainloop()
mainPage3.py(where I import class from logIn_screen.py):
from tkinter import *
from logIn_screen import checkValidation
import os
import time
import tkinter.messagebox
#stuff to do:
#seguir el formato hechoen writeFile.py
#bregar con lo de procesar imagenes
#cambiar los botones de help, reporte y logout por imagenes enves de texto
#ponerle un background image a todo para que se vea mas bonito
#Reporte shit:
#se genera un reporte cadavez que se hace un fail attempt a entrar en el login, cadavez que se abre esta pagina(con hora fecha y dia),
#cada vez que se presiona editar(sale el valor), cadavez que se captura y se presiona Si(sale el resultado) o No y cadavez que se presione logOut(sale fecha y hora) .
#seguir con el reporte.
#tiene todos los metodos que bregan con agregar widgets y quitarlos
class formManipulation:
# total default de parkings
tot = 200
# 0 = no 1 = yes
totChanged = 0
cv = checkValidation()
# 0 = no 1 = yes
imgAceptada = 1
#tiempo con formato 12
t1 = time.strftime("%I:%M%p")
#fecha
d1 = time.strftime("%d-%m-%Y")
# tiempo cuando se undio logOut
#t2 = ""
##fecha al undirse logOut
#d2 = ""
#corre cuando se unde actualizar y cambia el valor de parking en total
def changeTotal(self, txt):
self.totChanged = 1
if txt == "" or txt == " ":
tkinter.messagebox.showinfo( "Error","Dejo el total de estacionamientos en blanco")
else:
try:
self.tot = int(txt)
except:
tkinter.messagebox.showinfo( "Error","El valor debe ser numerico")
if self.tot < 1:
tkinter.messagebox.showinfo( "Error","El valor debe ser mayor a cero")
else:
self.tot = str(self.tot)
#se usa para llamar changeTotal(self, txt) por que esta mierda no se le pueden pasar parametros por el command del buton
def callChange(self):
self.changeTotal(txtBoxPark.get())
#el form peque~o(no puedo usar e~e ni que sale un error...) que sale despues de presionar capturar
def askForm(self):
self.imgAceptada = 1
#display new form
form2 = Tk()
form2.title("Cotego de imagen")
form2.geometry("300x100")
newBox = LabelFrame(form2, text = "La imagen es correcta?", height = "50", width = "250")
newBox.place(x = 30, y = 17)
#btn Si
btnY = Button(newBox, text = "Si", width = 3, command = self.changeDisTxt)
btnY.place( x = 50)
#btn No
btnN = Button(newBox, text = "No", width = 3, command = self.killImg)
btnN.place(x = 150)
#display la cantidad sobrante de parkings de acuerdo al total en el txtBox de estacionamientos
def changeDisTxt(self):
#puse esto aqui envez de en la classe de reporte por que pasar parametros
#por widgets de tkinter es una jodienda.
with open(self.d1 + ".txt", "a+") as f:
f.write("--Imagen capturada-- \r\n\r\n"+
"Correcta: Si\r\n\r\n" +
"Path de la imagen: \r\n\r\n" +
"Disponibilidad: 50/"+ self.tot + "\r\n\r\n" +
self.d1 + " " + self.t1 + "\n" +
"--------------------------------\r\n\r\n")
if self.imgAceptada > 0:
txtBoxDisp.insert(0, "50/" + self.tot)
txtBoxDisp.configure(state = "readonly")
else:
tkinter.messagebox.showinfo( "Error","Debe capturar una nueva imagen")
#desaprace la foto
def killImg(self):
#puse esto aqui envez de en la classe de reporte por que pasar parametros
#por widgets de tkinter es una jodienda.
with open(self.d1 + ".txt", "a+") as f:
f.write("--Imagen capturada-- \r\n\r\n"+
"Correcta: No\r\n\r\n" +
"Path de la imagen: \r\n\r\n" +
self.d1 + " " + self.t1 + "\n" +
"--------------------------------\r\n\r\n")
self.imgAceptada = 0
lblImg.pack_forget()
#display la foto
def displayImg(self):
lblImg.pack()
self.askForm()
#llama al script que desplega el howTo
def openHelp(self):
os.system("howTo.py 1")
#grava la fecha y tiempo que se presiono logOut y abre el script de logIn
def openLogIn(self):
#tiempo con formato 12
t2 = time.strftime("%I:%M%p")
#fecha
d2 = time.strftime("%d-%m-%Y")
failStr = str(self.cv.fail)
totStr = str(self.tot)
with open(self.d1 +".txt", "a") as f:
f.write("--Reporte Final-- \r\n\r\n"+
"Entrada: "+ self.d1 + " " + self.t1 +" \r\n" +
"Intentos fallados: " + failStr + " \r\n" +
"Usuario: " + self.cv.user + " \r\n" +
"Total de estacionamientos: " + totStr + " \r\n" +
"Promedio de estacionamientos libres: 50% \r\n" +
"Salida: " + d2 + " " + t2 +"\r\n" +
"--------------------\r\n\r\n"
)
form.destroy()
os.system("logIn_screen.py 1")
# clase que brega con todo lo que tenga que ver con el reporte
class ReportGen():
fm = formManipulation()
cv = checkValidation()
#mainLog = ""
#desplega el form del reporte y lee el file de reportes
def repForm(self):
#form
form3 = Tk()
form3.title("Parkaider")
form3.geometry("500x500")
with open(fm.d1 + ".txt") as f:
out = f.read()
repBox = LabelFrame(form3, text = "Reporte generado", height = 420, width = 260)
repBox.place( x = 30, y = 10)
scroll = Scrollbar(repBox)
scroll.pack( side = RIGHT, fill = Y)
txt = Text(repBox, text = None, yscrollcommand = scroll.set)
txt.insert(END, out)
txt.config( width = 50, height = 28)
txt.pack()
txt.config( state = DISABLED)
scroll.config(command = txt.yview)
#genera reporte cuando se actualiza el valor de estacionamientos
def totUpdateLog(self):
fm.callChange()
with open(fm.d1 + ".txt", "a+") as f:
f.write("--Total de parking actualizado-- \r\n\r\n"+
"Total de estacionamientos : "+ fm.tot +"\r\n\r\n" +
fm.d1 + " " + fm.t1 + "\n" +
"--------------------------------\r\n\r\n")
#llama al metodo de logout y genera el reporte final
def finRep():
fm.openLogIn()
#class PicProcessing:
#main form
fm = formManipulation()
rg = ReportGen()
global form
form = Tk()
form.title("Main Page")
form.geometry("600x700+100+0")
#box imagen capturada
#esta aqui por que sino sale undefined para lblImg
imgBox = LabelFrame(form, text = "Imagen capturada", height = "300", width = "260")
#path de foto
#esta aqui por que sino sale undefined para lblImg
img = PhotoImage(file = "parking.gif", height = "300", width = "260")
#foto
#es global para que la pueda reconocer la clase cuando corra killImg()
global lblImg
lblImg = Label(imgBox, image = img)
#big lbl PArkaider
lblTitleVal = StringVar()
lblTitleVal.set("Parkaider")
lblTitle = Label(form, textvariable = lblTitleVal, font=("Times", 30))
lblTitle.place( x = 220, y = 20)
#total de parking. Se convierte en string para usarse en el txtBox
strinTot = StringVar()
#conversion de totPark(int) a string
fm.tot = str(fm.tot)
#txtBox con el total de parking.
txtBoxPark = Entry(form, textvariable = None, width = 10)
txtBoxPark.insert(0,fm.tot)
txtBoxPark.place( x = 264, y = 135)
#lbl total de estacionamientos
lblParkingTotVal = StringVar()
lblParkingTotVal.set("Total de estacionamientos")
lblParkingTot = Label(form, textvariable = lblParkingTotVal, font = ("Times"))
lblParkingTot.place( x = 220, y = 100)
#btn edit,se usa si se fuera hacer update al algoritmo con el total nuevo de parking que no sea el default
btnEditTot = Button(form, text = "Actualizar", width = 8, command = rg.totUpdateLog)
btnEditTot.place( x = 263 , y = 170 )
#show el box de imagen capturada
imgBox.place(x = 170, y = 220)
#txtBox con la cantidad total despues que confirma que la imagen es correcta. Se supo que ahi se haga el algoritmo de object detection
txtBoxDisp = Entry(form, textvariable = None, width = 30)
txtBoxDisp.place( x = 210, y = 650)
#btn que captura una imagen con la camara
btnCap = Button(form, text = "Capturar" , width = 7, command = fm.displayImg)
btnCap.place( x = 270, y = 550)
#lbl disponibilidad
lblDisVal = StringVar()
lblDisVal.set("Disponibilidad")
lblDis = Label(form, textvariable = lblDisVal, font = "Times")
lblDis.place( x = 255, y = 620)
btnHelp = Button(form, text = "help", width = 4, command = fm.openHelp)
btnHelp.place(x = 20, y = 20)
btnLogout = Button(form, text = "Logout", width = 5, command = fm.openLogIn)
btnLogout.place(x = 540, y = 20)
btnRep = Button(form, text = "Reporte", width = 6, command = rg.repForm)
btnRep.place( x = 70, y = 20 )
#se necesita para que todo funcione en windows, en linux o mac, esto no hace falta
form.mainloop()
Ignore all of the Spanish comments and the Spanglish variables lol.
Thanks
You have to create a root window before you can create instances of StringVar or IntVar
Put form = Tk() befor class declaration and before imported class
global form
form = Tk()
from logIn_screen import checkValidation
class formManipulation:
# rest of code