Python: Evenly space output data with varying string lengths - python

I am trying to get my output data to look like this:
-------------------------------------------------------
Grade Report for Programs
-------------------------------------------------------
Jacobson, Mark 19.0 <--- 20,17,20
Snurd, Mortimur 16.5 <--- 20,19,18,17,16,15,14,13
Luxemburg, Rosa 15.0 <--- 18,15,20,10,12
Atanasoff, John 20.0 <--- 20,20,20,20,20,20,20
Hopper, Grace 20.0 <--- 20,20,20,20,20,20
-------------------------------------------------------
But I don't know how to deal with the varying name length. My output currently looks like this.
Grade Report for Programs
-------------------------------------------------------
Jacobson, Mark 19.0 <--- 20,17,20
Snurd, Mortimur 16.5 <--- 20,19,18,17,16,15,14,13
Luxemburg, Rosa 15.0 <--- 18,15,20,10,12
Atanasoff, John 20.0 <--- 20,20,20,20,20,20,20
Hopper, Grace 20.0 <--- 20,20,20,20,20,20
-------------------------------------------------------
The program I have written is to take an input file of grade scores and collect the data and neatly print out the average.
The input file looks something like this:
Mark Jacobson,20,17,20
Mortimur Snurd,20,19,18,17,16,15,14,13
Rosa Luxemburg,18,15,20,10,12
John Atanasoff,20,20,20,20,20,20,20
Grace Hopper,20,20,20,20,20,20
And here is my code that collects the name and scores, and prints out the data with last name, first name, average score, then the actual scores that resulted to the average.
file = input("Enter filename: ")
grade_file = open(file, 'r')
print()
print('---------------------------------------------------------')
print('\t\tGrade Report for Programs')
print('---------------------------------------------------------')
for text in grade_file:
end_of_name = text.find(',')
name_seperated = text.find(' ')
first_name = text[0:name_seperated]
last_name = text[name_seperated+1:end_of_name]
name_last_first = last_name + "," + " " + first_name
grades = text[end_of_name+1:]
start = 0
index = 0
sum_n = 0
average= 0
score = 0
count = 0
while index < len(grades):
if grades[index] == ',':
score = int(grades[start:index])
count += 1
sum_n = score + sum_n
start = index + 1
index += 1
count += 1
score = int(grades[start:index])
sum_n = score + sum_n
average = sum_n / count
print(name_last_first, " ", average, "<---", grades)
print('---------------------------------------------------------')
grade_file.close()
I just need to figure out how to have even spaces so it makes an even row and column like the first output. Help is greatly appreciated! Thanks!

One way is to use Python's builtin C-style formatting. Note that a negative field width indicates that the field is to be left-justified:
>>> print("%-30s %4.1f" % ("Jacobson, Mark", 19.0))
Jacobson, Mark 19.0
>>>
Alternatively, you can use the string format method:
>>> print("{:30s} {:4.1f}".format("Jacobson, Mark", 19.0))
Jacobson, Mark 19.0
>>>
You can also use Formatted String Literals (f-strings):
>>> name = "Jacobson, Mark"
>>> average = 19.0
>>> print(f"{name:30s} {average:4.1f}")
Jacobson, Mark 19.0
>>>

Use string formatting with field width specifiers:
print('{:20s} {:4.1f} <--- {}'.format(name_last_first, average, grades))
This uses the str.format() method with the Format String Syntax to slot values into a template.
The first slot formats strings into a field 20 characters wide, the second slots floating point numbers into a field 4 characters wide, using 1 digit after the decimal point (leaving 1 for the decimal point itself plus 2 digits before the point).
If I were you, I'd also look at the csv module to read your data, rather than use string manipulations. You'll get list objects with separate values for each column:
import csv
print('---------------------------------------------------------')
print('\t\tGrade Report for Programs')
print('---------------------------------------------------------')
with open(file, 'r', newline='') as grade_file:
reader = csv.reader(grade_file)
for row in reader:
name = row[0]
name = ' '.join(map(str.strip, reversed(name.split(',')))
grades = [int(g) for g in row[1:])
average = sum(grades) / len(grades)
print('{:20s} {:4.1f} <--- {}'.format(name, average, ','.join(grades)))
print('---------------------------------------------------------')

The answer I find easiest is just using some basic string arithmetic.
For example, say for want the aligned a variable 20 spaces ahead of left-alignment, in your case the "average" variable, you could simply do this
print(name_last_first + (' ' * (20-len(name_last_first))) + average
+ "<----" + grades)
It's just a bit lengthier, but the code is easier to interpret in my opinion.
(Note: this method only works with mono spaced fonts! But most Python output is defaulted to a MS font :-) )

You can use this code :
handle = open('grade.txt')
name= list()
avg = list()
scores = list()
for line in handle:
line = line.strip()
spos = line.find(',')
scores.append(line[spos+1:])
words = line.split(',')
words = words
name.append(words[0])
add = 0
for i in range(1,len(words)):
add = add+int(words[i])
average = add/(len(words)-1)
avg.append(average)
for i in range(len(avg)):
tname = name[i].split()
fname = tname[0]
sname = tname[1]
order = sname+', '+fname
print("%-20s %-3.1f <--- %-30s " %(order,float(avg[i]),scores[i]))
The final line is for displaying it in an organized manner, you're code was lacking it only.

Related

Reading lines from a txt file and create a dictionary where values are list of tuples

student.txt:
Akçam Su Tilsim PSYC 3.9
Aksel Eda POLS 2.78
Alpaydin Dilay ECON 1.2
Atil Turgut Uluç IR 2.1
Deveci Yasemin PSYC 2.9
Erserçe Yasemin POLS 3.0
Gülle Halil POLS 2.7
Gündogdu Ata Alp ECON 4.0
Gungor Muhammed Yasin POLS 3.1
Hammoud Rawan IR 1.7
Has Atakan POLS 1.97
Ince Kemal Kahriman IR 2.0
Kaptan Deniz IR 3.5
Kestir Bengisu IR 3.8
Koca Aysu ECON 2.5
Kolayli Sena Göksu IR 2.8
Kumman Gizem PSYC 2.9
Madenoglu Zeynep PSYC 3.1
Naghiyeva Gulustan IR 3.8
Ok Arda Mert IR 3.2
Var Berna ECON 2.9
Yeltekin Sude PSYC 1.2
Hello, I want to write a function, which reads the information about each student in the file into a dictionary where the keys are the departments, and the values are a list of students in the given department (list of tuples). The information about each student is stored in a tuple
containing (surname, GPA). Students in the file may have more than one name but only the surname and gpa will be stored. The function should return the dictionary. (Surnames are the first words at each line.)
This is what I tried:
def read_student(ifile):
D={}
f1=open(ifile,'r')
for line in f1:
tab=line.find('\t')
space=line.rfind(' ')
rtab=line.rfind('\t')
student_surname=line[0:tab]
gpa=line[space+1:]
department=line[rtab+1:space]
if department not in D:
D[department]=[(student_surname,gpa)]
else:
D[department].append((student_surname,gpa))
f1.close()
return D
print(read_student('student.txt'))
I think the main problem is that there is a sort of disorder because sometimes tab comes after words and sometimes a space comes after words, so I dont know how to use find function properly in this case.
Why mess with rfind and find when you can simply split?
def read_student(ifile):
D = {}
f1 = open(ifile,'r')
for line in f1:
cols = line.split() # Splits at one or more whitespace
surname = cols[0].strip()
department = cols[-2].strip() # Because you know the last-but-one is dept
gpa = float(cols[-1].strip()) # Because you know the last one is GPA
fname = ' '.join(cols[1:-2]).strip()
# cols[1:-2] gives you everything starting at col 1 up to but excluding the second-last.
# Then you join these with spaces.
if department not in D:
D[department] = [(surname, gpa)]
else:
D[department].append((surname, gpa))
f1.close()
return D
If you know that your columns are separated by \t always, you can do cols = line.split('\t') instead. Then you have the students' fname in the second column, the department in the third, and the GPA in the fourth.
A couple of suggestions:
You can use defaultdict to avoid checking if department not in D every time
You can use with to manage reading the file so you don't have to worry about f1.close(). This is the preferred way to read files in Python.
see below - you will have to take care of the surname but rest of the details in the question were handled
from collections import defaultdict
data = defaultdict(list)
with open('data.txt', encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines()]
for line in lines:
first_space_idx = line.rfind(' ')
sec_space_idx = line.rfind(' ', 0,first_space_idx - 1)
grade = line[first_space_idx+1:]
dep = line[sec_space_idx:first_space_idx]
student = line[:sec_space_idx].strip()
data[dep].append((student, grade))
for dep, students in data.items():
print(f'{dep} --> {students}')
output
PSYC --> [('Akçam Su Tilsim', '3.9'), ('Deveci Yasemin', '2.9'), ('Kumman Gizem', '2.9'), ('Madenoglu Zeynep', '3.1'), ('Yeltekin Sude', '1.2')]
POLS --> [('Aksel Eda', '2.78'), ('Erserçe Yasemin', '3.0'), ('Gülle Halil', '2.7'), ('Gungor Muhammed Yasin', '3.1'), ('Has Atakan', '1.97')]
ECON --> [('Alpaydin Dilay', '1.2'), ('Gündogdu Ata Alp', '4.0'), ('Koca Aysu', '2.5'), ('Var Berna', '2.9')]
IR --> [('Atil Turgut Uluç', '2.1'), ('Hammoud Rawan', '1.7'), ('Ince Kemal Kahriman', '2.0'), ('Kaptan Deniz', '3.5'), ('Kestir Bengisu', '3.8'), ('Kolayli Sena Göksu', '2.8'), ('Naghiyeva Gulustan', '3.8'), ('Ok Arda Mert', '3.2')]
You can use split(' ', 1) to extract surname. It gives list with two elements. first one is surname. Then again split the second elements to get the using rsplit(' ', 1). It again gives list with two element first one is name and dept and second one is gpa. Again split second element to get department.
def read_student(ifile):
d = {}
with open(ifile) as fp:
for line in fp:
fname, data = line.strip().split(' ', 1)
data, gpa = data.rsplit(' ', 1)
dept = data.split()[-1]
d.setdefault(dept, []).append((fname, gpa))
return d
print(read_student('student.txt'))
Output:
{'ECON': [('Alpaydin', '1.2'),
('Gündogdu', '4.0'),
('Koca', '2.5'),
('Var', '2.9')],
'IR': [('Atil', '2.1'),
('Hammoud', '1.7'),
('Ince', '2.0'),
('Kaptan', '3.5'),
('Kestir', '3.8'),
('Kolayli', '2.8'),
('Naghiyeva', '3.8'),
('Ok', '3.2')],
'POLS': [('Aksel', '2.78'),
('Erserçe', '3.0'),
('Gülle', '2.7'),
('Gungor', '3.1'),
('Has', '1.97')],
'PSYC': [('Akçam', '3.9'),
('Deveci', '2.9'),
('Kumman', '2.9'),
('Madenoglu', '3.1'),
('Yeltekin', '1.2')]}
This solution makes use of itemgetter to simplify the getting of variables: surname, dept. and gpa
from operator import itemgetter
d = dict()
with open('f0.txt', 'r') as f:
for line in f:
name, dept, gpa = itemgetter(0, -2, -1)(line.split())
d.setdefault(dept, []).append((name, gpa))

How to multiply different numbers from different lines in a text

I have a text file like this:
month /name/ number/ price
1 John 100 120.00
1 Sean 90 125.00
1 Laura 150 100.00
1 Joseph 95 140.00
1 Pam 91 105.00
2 John 110 120.00
2 Sean 98 100.00
2 Laura 100 100.00
2 Joseph 89 150.00
2 Pam 100 100.00
3 John 100 121.00
3 Sean 90 120.00
3 Laura 97 100.00
3 Joseph 120 110.00
3 Pam 101 100.00
I need to get a specific person's (such as Pam) revenue per month and total revenue in 1,2 and 3 months (number*price). I have the code below and the output below. But I have no idea how to get the total revenue, can anyone give to me some advice or idea?
#This is the code I use
f = input('Enter The File Name:')
sales_data = open("sales.txt",'r')
lines = sales_data.readlines()
m = input('Enter the Manager Name:')
print('Monthly Sales Report for' +' ' + m)
for line in lines:
line = line.split()
tr = (float(line[2]) * float(line[3]))
if m in line:
print(line[0] +' ' + line[2] + ' ' + line[3] +' ' + str(tr))
#This is the output I got
Enter the Manager Name: Pam
Monthly Sales Report for Pam
1 91 105.00 9555.0
2 100 100.00 10000.0
3 101 100.00 10100.0
One possible way to solve your issue is to store all monthly values in a dictionary for a particular manager:
file_name = input('Enter The File Name: ')
manager_summary = {'1':0.0, '2':0.0, '3':0.0}
with open (file_name, 'r') as fin:
lines = fin.readlines()
manager = input('Enter the Manager Name: ')
print('Monthly Sales Report for' +' ' + manager)
for line in lines:
line = line.split()
if manager in line:
manager_summary[line[0]] += float(line[-2])*float(line[-1])
manager_total = 0.0
for key, value in manager_summary.items():
manager_total += value
print(manager_total)
The code reads the input file at once, loops through all the lines in search of the target manager and stores cumulative monthly sales for that manager in a dictionary. The total revenue for 3 month period is then computed by adding cumulative values for each month stored in the dictionary.
There were couple changes with respect to your original code worth noting:
In your code you ask the user for a file name but then you have it hardcoded in the next line - here you use that input file name.
Instead of opening the file with open this code uses with open - with open is a context manager that will automatically close the file for your when closing is needed, something that your were missing in your program.
Cumulative data is stored in a dictionary with keys being month numbers. This allows for having more than one monthly entry per manager.
Variable names are more meaningful. It is generally not recommended to use variables like f, m, it makes the program more bug prone and way less readable. The ones used here are longish, you can always come up with something inbetween.
You can solve this using a dictionary - specifically a defaultdict. You can keep track of a dictionary of people's names to revenue.
First import defaultdict and define a dictionary:
from collections import defaultdict
revenue_dictionary = defaultdict(float)
Then just after you've calculated tr, add this to the dictionary:
revenue_dictionary[line[1]] += tr
At the end of the script, you'll have a dictionary which looks like:
{
'John': 37300.0,
'Sean': 31850.0,
'Laura': 34700.0,
'Joseph': 39850.0,
'Pam': 29655.0
}
And you can access any of these using revenue_dictionary['Pam'], or m instead of 'Pam'.
f = input('Enter The File Name: ')
sales_data = open(f,'r')
lines = sales_data.readlines()
m = input('Enter the Manager Name: ')
print('Monthly Sales Report for ' + m)
TOTAL_REVENUE=0
for line in lines:
line = line.split()
if m in line:
tr = (float(line[2]) * float(line[3]))
TOTAL_REVENUE=TOTAL_REVENUE+tr
print(line[0] +' ' + line[2] + ' ' + line[3] +' ' + str(tr))
print("GRAND TOTAL REVENUE: " + str(TOTAL_REVENUE))

Python - Formatting a print to align a specific column

I am trying to format a print statement to align a specific column.
Currently my output is:
0 - Rusty Bucket (40L bucket - quite rusty) = $0.00
1 - Golf Cart (Tesla powered 250 turbo) = $195.00*
2 - Thermomix (TM-31) = $25.50*
3 - AeroPress (Great coffee maker) = $5.00
4 - Guitar (JTV-59) = $12.95
The output I am looking for is:
0 - Rusty Bucket (40L bucket - quite rusty) = $0.00
1 - Golf Cart (Tesla powered 250 turbo) = $195.00*
2 - Thermomix (TM-31) = $25.50*
3 - AeroPress (Great coffee maker) = $5.00
4 - Guitar (JTV-59) = $12.95
Here is the code I am currently using for the print:
def list_items():
count = 0
print("All items on file (* indicates item is currently out):")
for splitline in all_lines:
in_out = splitline[3]
dollar_sign = "= $"
daily_price = "{0:.2f}".format(float(splitline[2]))
if in_out == "out":
in_out = str("*")
else:
in_out = str("")
print(count, "- {} ({}) {}{}{}".format(splitline[0], splitline[1], dollar_sign, daily_price, in_out))
count += 1
I have tried using formatting such as:
print(count, "- {:>5} ({:>5}) {:>5}{}{}".format(splitline[0], splitline[1], dollar_sign, daily_price, in_out))
but have never been able to get just the one column to align. Any help or suggestions would be greatly appreciated! I am also using python 3.x
To note I am using tuples to contain all the information, with all_lines being the master list, as it were. The information is being read from a csv originally. Apologies for the horrible naming conventions, trying to work on functionality first.
Sorry if this has been answered elsewhere; I have tried looking.
EDIT: Here is the code im using for my all_lines
import csv
open_file = open('items.csv', 'r+')
all_lines = []
for line in open_file:
splitline = line.strip().split(',')
all_lines.append((splitline[0], splitline[1], splitline[2], splitline[3]))
And here is the csv file information:
Rusty Bucket,40L bucket - quite rusty,0.0,in
Golf Cart,Tesla powered 250 turbo,195.0,out
Thermomix,TM-31,25.5,out
AeroPress,Great coffee maker,5.0,in
Guitar,JTV-59,12.95,in
You should look at str.ljust(width[, fillchar]):
> '(TM-31)'.ljust(15)
'(TM-31) ' # pad to width
Then extract the variable-length {} ({}) part and padd it to the necessary width.
What you are looking for may be:
[EDIT] I have now also included a tabulate version since you may gain flexibility with this.
import csv
from tabulate import tabulate
open_file = open('items.csv', 'r+')
all_lines = []
for line in open_file:
splitline = line.strip().split(',')
all_lines.append((splitline[0], splitline[1], splitline[2], splitline[3]))
#print all_lines
count = 0
new_lines=[]
for splitline in all_lines:
in_out = splitline[3]
dollar_sign = "= $"
daily_price = "{0:.2f}".format(float(splitline[2]))
if in_out == "out":
in_out = str("*")
else:
in_out = str("")
str2='('+splitline[1]+')'
print count, "- {:<30} {:<30} {}{:<30} {:<10}".format(splitline[0], str2, dollar_sign, daily_price, in_out)
new_lines.append([splitline[0], str2, dollar_sign, daily_price, in_out])
count += 1
print tabulate(new_lines, tablefmt="plain")
print
print tabulate(new_lines, tablefmt="plain", numalign="left")
I do not like the idea of controlling printing format myself.
In this case, I would leverage a tabulating library such as: Tabulate.
The two key points are:
keep data in a table (e.g. list in a list)
select proper printing format with tablefmt param.

Targeting words and integers per line text file

For my code, i need to input a text file where each line will read a Last Name, Wage, and Hours worked. Each separated by a single whitespace character.
Example:
johnson 14 52
doe 12.12 35.5
smith 14.56 42
Further i have to print a report using these values until there are no more lines to take data from.
I want to be able to assign the name to a variable(last), the wage to a variable(rate), and the hours worked to a variable(totalHours) and then use these variables to compute other things.
But i cant figure out how to target each specific name, rate, and hours worked, for each line.
Here's the code i have so far.
f = open('test.txt', 'r')
for line in f:
data = line.split()
for word in data:
last =
rate =
totalHours =
#these are my computations
otHours = 0
if totalHours > 40:
otHours = totalHours - 40
regPay = (totalHours - otHours) * rate
otPay = 1.5 * rate * otHours
gross = regPay + otPay
print("%-21s%-15.2f%-17.1f%-15.1f%-15.2f%-16.2f%3.2f" % \
(last, rate, totalHours, otHours, regPay, otPay, gross))
f.close
Any help is appreciated.
You can unpack the data list, like below:
last,rate,totalHours = data
Or,
last = data[0]
rate=data[1]
totalHours=data[2]
You can simply use
for line in f:
last, rate, totalHours = line.split()
But there's another problem with your code. All these items will be read as strings. You'll have to convert rate and totalHours to floats before doing any calculations with them.

Return the average mark for all student in that Section

I know it was asked already but the answers the super unclear
The first requirement is to open a file (sadly I have no idea how to do that)
The second requirement is a section of code that does the following:
Each line represents a single student and consists of a student number, a name, a section code and a midterm grade, all separated by whitespace
So I don't think i can target that element due to it being separate by whitespace?
Here is an excerpt of the file, showing line structure
987654322 Xu Carolyn L0101 19.5
233432555 Jones Billy Andrew L5101 16.0
555432345 Patel Amrit L0101 13.5
888332441 Fletcher Bobby L0201 18
777998713 Van Ryan Sarah Jane L5101 20
877633234 Zhang Peter L0102 9.5
543444555 Martin Joseph L0101 15
876543222 Abdolhosseini Mohammad Mazen L0102 18.5
I was provided the following hints:
Notice that the number of names per student varies.
Use rstrip() to get rid of extraneous whitespace at the end of the lines.
I don't understand the second hint.
This is what I have so far:
counter = 0
elements = -1
for sets in the_file
elements = elements + 1
if elements = 3
I know it has something to do with readlines() and the targeting the section code.
marks = [float(line.strip().split()[-1]) for line in open('path/to/input/file')]
average = sum(marks)/len(marks)
Hope this helps
Open and writing to files
strip method
Something like this?
data = {}
with open(filename) as f:#open a file
for line in f.readlines():#proceed through file lines
#next row is to split data using spaces and them skip empty using strip
stData = [x.strip() for x in line.split() if x.strip()]
#assign to variables
studentN, studentName, sectionCode, midtermGrade = stData
if sectionCode not in data:
data[sectionCode] = []
#building dict, key is a section code, value is a tuple with student info
data[sectionCode].append([studentN, studentName, float(midtermGrade)]
#make calculations
for k,v in data.iteritems():#iteritems returns you (key, value) pair on each iteration
print 'Section:' + k + ' Grade:' + str(sum(x[2] for x in v['grade']))
more or less:
infile = open('grade_file.txt', 'r')
score = 0
n = 0
for line in infile.readlines():
score += float(line.rstrip().split()[-1])
n += 1
avg = score / n

Categories

Resources