Sorting objects using CSV data - python

Before I start I am noob
So, I made objects using data from a CSV file which was in the form 'doe,john,m,20', which can be seen below:
class FitClinic:
def __init__(self, lname, fname, gender, age):
self.__lname = lname
self.__fname = fname
self.__gender = gender
self.__age = age
def __del__(self):
print("Customer has been deleted")
def set_lname(self):
pass
def get_lname(self):
return self.__lname
def set_fname(self):
pass
def get_fname(self):
return self.__fname
def set_gender(self):
pass
def get_gender(self):
return self.__gender
def set_age(self):
pass
def get_age(self):
return self.__age
fh=open('fit_clinic_20.csv', 'r')
fh.seek(3)
listofcustomers=[]
for row in fh:
c = row.split(",")
listofcustomers.append(FitClinic(c[0], c[1], c[2], c[3:]))
What I need to do is sort these objects by the fname attribute which I have no idea how to do, please help, thanks.

You can use sorted with key
sorted_list=sorted(listofcustomers,key=lambda x: x.get_fname())
Refer to Sorting HOW TO
To check the result, you can just print the information with the method you have implemented:
for x in sorted_list:
print(x.get_fname())
In a more complicated situation, advised by #Maurice Reeves, you can also add __str__ and __repr__ methods.
def __str__(self):
str_to_print=f'lname:{self.__lname},'
str_to_print+=f'fname:{self.__fname},'
str_to_print+=f'gender:{self.__gender},'
str_to_print+=f'age:{self.__age}'
return str_to_print
__repr__=__str__
Then you can print by:
for x in sorted_list:
print(x)
Refer to Python doc.
BTW, you can use pandas to load csv file conveniently.
import pandas
csv_pd=pandas.read_csv('fit_clinic_20.csv')
csv_pd.sort_values(by=['fname']) # If fname is the head of your csv file. If not, just add it.
Refer to pandas.DataFrame.sort_values

Related

How to delete a row from dataframe as python code?

I'm having the function delete_employee(self, new_employee) which gets a csv file path and an employee credentials which I want to remove, and suppose to delete him from the file.
My function doesn't delete it and also shows this error:
FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison return op(a, b).
Can you please help me understand why it goes wrong?
class Employee(object):
def __init__(self, employee_id, full_name, phone, age):
self.employee_id = employee_id
self.full_name = full_name
self.phone = phone
self.age = age
class EmployeesList(object):
def __init__(self, list_of_employees):
self.list_of_employees = list_of_employees
def open_and_read_file(self, list_of_employees):
employees_csv_data = pd.read_csv(list_of_employees)
return employees_csv_data
def delete_employee(self, new_employee):
employees_csv_data = self.open_and_read_file(self.list_of_employees)
for row in employees_csv_data.values:
if new_employee.employee_id == str(row[0]):
employees_csv_data = employees_csv_data[employees_csv_data.employee_id != new_employee.employee_id]
print(employees_csv_data)

Method as an argument with self parameter

Disregard the naming conventions as to what class is part of which it is just a test run.
I need some help with the OOP inheritance i have created a class of Students, Teachers, and the principal. My goal is for the principal to be able to add employees. The problem is i just want to use a for loop to get the names and then pass that method as an attribute for the principal object. i was able to do it with the class Input without the self parameter. can someone tell me
what is going on here and how can i fix this with self. i removed input from names so that my question wont get shut down
class Input:
def count():
cnt = []
for i in range(4):
name = ('Enter name here: ')
cnt.append(name)
return cnt
class Student:
def __init__(self,name,lastname):
self.name = name
self.lastname = lastname
class StudentCouncil(Student):
def __init__(self, name, lastname, tenure):
super().__init__(name,lastname)
self.tenure = tenure
class Principal(StudentCouncil):
def __init__(self, name, lastname, tenure,employees=None):
super().__init__(name,lastname,tenure)
if employees is None:
self.employees = []
else:
self.employees = employees
def display(self):
for names in self.employees:
print(names,end=' ')
count = Input.count()
tij = Principal('Mike','Thoma','3',count)
tij.display()
If the method takes a self parameter, you need to create an instance of the class. So it would be:
class Input:
def count(self):
cnt = []
for i in range(4):
name = input('Enter name here: ')
cnt.append(name)
return cnt
and then you would do:
myinput = Input()
count = myinput.count()
Your count() method doesn't use any attributes of self, so it doesn't currently need to be written this way. But you might want to redefine it like this:
class Input:
def __init__(self, howmany):
self.howmany = howman
def count(self):
return [input('Enter name here: ') for _ in range(self.howmany)]
myinput = Input(4)
count = myinput.count()
If count is all you want from Input, just make it a function:
def input_names():
cnt = []
for i in range(4):
name = ('Enter name here: ')
cnt.append(name)
return cnt
If you want a configurable Input type of some sort, then you want to run count on an instance of that, you need self:
class Input:
def count(self):
cnt = []
for i in range(self.num_names): # if we need some configuration
name = ('Enter name here: ')
cnt.append(name)
return cnt
Else, the kosher way to do this is to use the staticmethod decorator:
class Input:
#staticmethod
def count():
cnt = []
for i in range(4):
name = ('Enter name here: ')
cnt.append(name)
return cnt
Your current code will work as you use it currently, Input.count(), but if you instantiated an input, Input().count() would throw an exception. The staticmethod decorator ensures that this method is safe to call on either the class directly or on an instance of that class.

Searching for an ID in a list of books

I've made a simple library system which stores books with an ID, name and cost. My question is rather simple but my limited knowledge of python has let me down.
I've created a class that stores books in the library, they are created like this;
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
I have to make a function that searches for a book by its ID, by making a function in my library class. I tried to make it like in the code below, but it didn't work. basically, I want to give my function an ID, and it should return the name and cost of that particular book, but only if the ID is present in the list.
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
#def show_id(self, ID):
# if ID in lib:
# return self.books
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
I think the simplest idea if you need ID indexing is to use a dictionary:
class Library:
def __init__(self):
self.books = dict()
def add_book(self, Book):
self.books[Book.ID] = Book
def remove_book(self, Book):
del self.books[Book.ID]
def get_book(self, ID):
return self.books[ID]
def show_id(self, ID):
self.get_book(ID).show()
def show_all(self):
for Book in self.books.values():
Book.show()
You could even rename get_book to __getitem__, this second name is special in python, it's called a dunder method (or magic method). Implementing it will allow you to write lib[id] instead of lib.show_id(id) (I'm not saying that you should, but that's an option). There are many other dunder methods that you can try using for fun, you can find some of them in the python data model.
I think that you should post your code on codereview as you may use broader advices on your code.
As noted above, there might be better implementations, but using your current code, you can do the below where I have adapted your show_id function according to requirements. It relies on a list comprehension to identify the correct IDs.
Hope this helps!
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
def show_id(self, ID):
# simple list comprehension to filter; note that you might need to make sure that e.g. all IDs are int, or str
matching_ids = [book for book in self.books if book.ID == ID]
# return name, price tuple for all matching books as requested -- note that this will return a list of all matches
# if the IDs are truly unique, you can return e.g. a single tuple or Book object here
# if nothing found, will return empty list
return [(book.name, book.price) for book in matching_ids]
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
print(lib.show_id(1))
Even if this method is not really optimized, you can go through them with a for loop.
for book in lib.books:
if book.id == searched_id:
searched_book = book
break

Convert a multi-column table to a dict of dicts

I have custom class to simulate a row in table (in database concept), each column is a string.
class Row:
def __init__(self, filename, message, version):
self.filename = filename
self.message = message
self.version = version
And I use a list to store them.
Assume I don't know the range of each column, and I want to transfer this 'table' to a dict of dicts,
such that it would be easier to query for all rows that filename = OOO and version = XXXX.
What would be a better way to do it? Right now I could iterate through all rows and build the range for particular column but it's kind of spaghetti code.
Easiest is probably something like this. If you know your rows are immutable, you could provide a hash method though - that might look a little nicer.
#!/usr/local/cpython-3.3/bin/python
class Row:
def __init__(self, filename, message, version):
self.filename = filename
self.message = message
self.version = version
def __str__(self):
return '{} {} {}'.format(self.filename, self.message, self.version)
__repr__ = __str__
def main():
list_ = [
Row('abc', 'message1', 'version1'),
Row('def', 'message2', 'version2'),
Row('ghi', 'message3', 'version3'),
Row('jkl', 'message4', 'version4'),
Row('mno', 'message5', 'version5'),
]
dict_ = {}
for row in list_:
tuple_ = (row.filename, row.version)
dict_[tuple_] = row
sought = ('def', 'version2')
print(dict_[sought])
main()

creating a list from a text file in python

I have a text file that looks something like this where the first column is a student's name, the second column is the number of credits, and the third is the number of points (grade times hours).
john 5 15
bill 9 30
ted 7 22
I want to create a class that extracts the relevant information and calculates gpa.
class Student:
def __init__(self, name, hours, qpoints):
self.name = name
self.hours = float(hours)
self.qpoints = float(qpoints)
def getName(self):
return self.name
def getHours(self):
return self.hours
def getQPoints(self):
return self.qpoints
def gps(self):
return self.qpoints/self.hours
used to make extract the data (based on the fact that there is a tab between each piece of information)
def makeStudent(info):
name, hours, qpoints = info.split("\t")
return Student(name, hours, qpoints)
here I use a for loop to create a list based on the data in the text file by appending the relevant information from each line to the list
def readStudents(filename):
infile = open(filename, 'r')
students = []
for line in infile:
students.append(makeStudent(line))
infile.close()
return students
the problem is that I get this error:
[<__main__.Student object at 0x01FA4AD0>, <__main__.Student object at 0x01FA4AF0>,
<__main__.Student object at 0x01FA4B10>, <__main__.Student object at 0x01FA4B50>,
<__main__.Student object at 0x01FA4B30>]
Any ideas on why this is happening?
This is not an error. It is regular output. You should override the __str__ and __repr__ methods of the Student class, to tell python how to print Student objects.
Some help on your code, this is much better:
def readStudents(filename):
with open(filename) as student_file:
return [Student(*line.split()) for line in student_file]
Like everyone says, not an error. Just implement __str__, __repr__ or __unicode__ on your Student class.
However, I have one minor suggestion. You should use the csv module to read your file.
Your readStudents function can also be re-written like this:
def readStudents(filename):
students = []
with open(filename) as infile:
for line in csv.reader(infile, delimiter='\t'): # or excel-tab if you want.
students.append(Student(**line))
return students
Isn't that pretty?
Don't forget to put a import csv at the beginning of your python file!
I strongly suggest using the csv module (import csv): it will do most of the work for you, be more flexible, more readable and less prone to bugs.
Also, to be a little more strict: what is your actual question? I just see a blurb of code and some output you do not understand. That is not an error message, by the way, it is a list of five instances of the Student class. The code seems to work as intended: you parse the file, create students, and then..? What do you want to do with them? The parsing is done, you want to move on to the handling of the data.
You don't get an error, but a list of student objects. What you need to do is implement a __str__ or a __repr__ method: Special method names
Getter and setter methods are usually frowned on unless they are doing something active (over and above just retrieving or setting a value).
import csv
class Student(object):
def __init__(self, name, hours, qpoints):
super(Student,self).__init__()
self.name = str(name)
self.hours = float(hours)
self.qpoints = float(qpoints)
#property
def gpa(self):
return self.qpoints/self.hours
#gpa.setter
def gpa(self, value):
raise SyntaxError('gpa property is read-only; update .qpoints or .hours instead')
def __str__(self):
return "{name:20} {hours:>6.2f} {qpoints:>6.2f} {gpa:>6.2f}".format(name=self.name, hours=self.hours, qpoints=self.qpoints, gpa=self.gpa)
def loadStudents(fname, *args, **kwargs):
with open(fname) as inf:
return [Student(*line) for line in csv.reader(inf, *args, **kwargs)]
def main():
students = loadStudents('students.csv', delimiter='\t')
for s in students:
print s
if __name__=="__main__":
main()
results in
john 5.00 15.00 3.00
bill 9.00 30.00 3.33
ted 7.00 22.00 3.14

Categories

Resources