Better time complexity for following task - python

I was solving following problem today (unfortunately I didnt achieve required better time complexity).
Given a list of student test scores, find the best average grade. Each student may have more than one test score in the list, and the best average grade is the average of all test scores for that student.
Complete the bestAverageGrade function in the editor below.
It has one parameter, scores, which is an array of student test scores. Each element in the array is a two-element array of the form [student name, test score] e.g. [ "Bobby", "87"].
More points will be awarded for solutions that can handle larger inputs within a set period of time i.e. code with a faster run-time complexity.
Input Format
The input parameter scores is an array of arrays, where each sub-array contains two strings: the student name followed by a test score as a string. You must also include the number of entries and the size of each entry (this will always be 2). See below for specific examples. Test scores may be positive or negative integers.
Output Format
Your function must return a single integer representing the best average grade. If you end up with an average grade that is not an integer, you should use a floor function to return the largest integer less than or equal to the average.
Return 0 for an empty input.
Sample Input 0
[ [ "Bobby", "87" ],
[ "Charles", "100" ],
[ "Eric", "64" ],
[ "Charles", "22" ] ]
entered as
4
2
Bobby 87
Charles 100
Eric 64
Charles 22
Sample Output 0
87
and here is me bestAverageGrade method:
def bestAverageGrade(scores):
list_of_students = set([x[0] for x in scores])
averages = []
for student in list_of_students:
results = [float(x[1]) for x in scores if x[0] == student]
averages.append(sum(results_of_student)/len(results))
return math.floor(max(averages))
How could I achieve better time complexity? I know that now list of lists is iterated two times.

You iterate over the list several times: one time for each student. If there are multiple students, then the number of loops can be rather large, so the time complexity can be - worst case - O(n2).
We can use an approach where we for instance using a dictionary. We can define a dictionary grades that maps every student name on a 2-tuple (a numerator and denominator). In that case the code looks like:
Vanilla Python
def bestAverageGrade(scores):
grades = {}
for student, grade in scores:
grade = float(grade)
current = grades.get(student)
if current is None:
grades[student] = grade, 1
else:
num, denom = current
grades[student] = num + grade, denom + 1
return math.floor(max(num/denom for num, denom in grades.values()))
Pandas
We can also boost performance by using Pandas. For example:
import pandas as pd
def bestAverageGrade(scores):
df = pd.DataFrame([[name, float(score)] for name, score in scores],
columns=['student', 'score'])
return math.floor(df.groupby('student')['score'].mean().max())
So here we first group by students and take the mean as aggregate for the 'score' column. Then we take the maximum over all these students.

Using Javascript.
function bestAverageGrade(scores) {
if(!Array.isArray(scores) || scores.length === 0) return 0;
let duplicateFrequency = {};
let sumFrequency = {};
scores.forEach(item => {
duplicateFrequency[item[0]] = duplicateFrequency[item[0]] ? duplicateFrequency[item[0]]+1 : 1;
sumFrequency[item[0]] = sumFrequency[item[0]] ? sumFrequency[item[0]]+Number(item[1]) : Number(item[1]);
})
for( let props in duplicateFrequency) {
sumFrequency[props] = Math.floor(sumFrequency[props] / duplicateFrequency[props])
}
return Math.max(...Object.values(sumFrequency))
}
Time Complexity here is O(n)
Approach: Here we are using two hashMaps one for storing the duplicates the other for storing the total marks for each student.
Then we are just reflecting over one of the hashMaps and dividing the total sum with the duplicates. Flooring the result to avoid decimals.
Finally, just use Math.max to get the maximum value.

Related

Function problems

So I have two lists:
typeList — list of strings of transaction types (addition, subtraction)
valueList — List of values either added or subtracted.
as both value and type are appended to the list at the same index, I need to make a function to calculate the sum of the total value added and the total value subtracted, but I've been really stuck on it for ages trying to get my head around just how to do it.
desiredType is just the type of transaction that is being looked for. As you'll see, I called my function twice with each type.
I understand that the index values need to be obtained and used between the two lists but not sure how to sum the values up.
def showInfo(typeList, valueList, desiredType):
for i in range(len(valueList)):
if typeList[i] == desiredType:
total = total + valueList[i]
return (total)
Call to the function:
if choice == "I": #Prints transaction information
print (showInfo(transactionTypes,transactionAmounts, "Addition"))
print (showInfo(transactionTypes,transactionAmounts, "Subtraction"))
Any help would be appreciated as well as an explanation on how it's done.
You can zip together those 2 lists, then you don't have to keep track of the index you're looking at:
def transaction_total(types, amounts, desired_type):
total = 0
for transaction_type, amount in zip(types, amounts):
if transaction_type == desired_type:
total += amount
return total
However, you're really just filtering values and summing them. Python makes this cleaner using generator expressions and the sum function:
def transaction_total(types, amounts, desired_type):
return sum(amount for transaction_type, amount in zip(types, amounts)
if transaction_type == desired_type)
If you need to keep your data unmodified, you can just make your function more efficient (just one call) :
def showInfo(typeList, valueList):
for i in range(len(valueList)):
if typeList[i] == "Addition":
total_add = total_add + valueList[i]
else:
total_sub = total_sub - valueList[i]
return (total_add, total_sub)

Python- Function which returns the average mark

I am trying to create a function which returns the average of a student's module. The student's data is stored in a list which contains the following, so Dave has: ('Dave', 0, 'none', 'M106', ['50'])
and then Ollie has: ('Ollie', 'M104', 0, 'none', ['60']). I can't get my head around how to get the average from the two averages.
def moduleAverage(self):
if student.getAverage is > 0:
return self.moduleAverage
Ok first I have to say: Correct your indentation.
So I'll make a little example now:
studentDetails = []
studentDetails.append(('Peter', ['40']))
studentDetails.append(('Frank', ['100']))
studentDetails.append(('Ernest', ['40']))
def moduleAverage(inList):
total = 0.0
for i in xrange(0, len(inList)):
total += float(inList[i][1][0]) # the score is at the index 1 in the tuple (('Peter', ['40']))
return (total / float(len(inList)))
print moduleAverage(studentDetails)
Pay attention: You have the single mark ['40'] as a string, so you have to convert it to float.
Also remain the same order in the student tuple.
I don't know your whole structure so I just made a simple example of the algorithm with tuples as you mentioned in your question.

Python Data Structure Selection

Let's say I have a list of soccer players. For now, I only have four players. [Messi, Iniesta, Xavi, Neymar]. More players will be added later on. I want to keep track of the number of times these soccer players pass to each other during the course of a game. To keep track of the passes, I believe I'll need a data structure similar to this
Messi = {Iniesta: 4, Xavi: 5 , Neymar: 8}
Iniesta = {Messi: 4, Xavi: 10 , Neymar: 5}
Xavi = {Messi: 5, Iniesta: 10 , Neymar: 6}
Neymar = {Messi: 8, Iniesta: 5 , Xavi: 6}
Am I right to use a dictionary? If not, what data structure would be better suited? If yes, how do I approach this using a dictionary though? How do I address the issue of new players being included from time to time, and creating a dictionary for them as well.
As an example, If I get the first element in the list, List(i) in the first iteration is Messi, how do i use the value stored in it to create a dictionary with the name Messi. That is how do i get the line below.
Messi = [Iniesta: 4, Xavi: 5 , Neymar: 8]
It was suggested I try something like this
my_dynamic_vars = dict()
string = 'someString'
my_dynamic_vars.update({string: dict()})
Python and programming newbie here. Learning with experience as I go along. Thanks in advance for any help.
This is a fun question, and perhaps a good situation where something like a graph might be useful. You could implement a graph in python by simply using a dictionary whose keys are the names of the players and whose values are lists players that have been passed the ball.
passes = {
'Messi' : ['Iniesta', 'Xavi','Neymar', 'Xavi', 'Xavi'],
'Iniesta' : ['Messi','Xavi', 'Neymar','Messi', 'Xavi'],
'Xavi' : ['Messi','Neymar','Messi','Neymar'],
'Neymar' : ['Iniesta', 'Xavi','Iniesta', 'Xavi'],
}
To get the number of passes by any one player:
len(passes['Messi'])
To add a new pass to a particular player:
passes['Messi'].append('Xavi')
To count the number of times Messi passed to Xavi
passes['Messi'].count('Xavi')
To add a new player, just add him the first time he makes a pass
passes['Pele'] = ['Messi']
Now, he's also ready to have more passes 'appended' to him
passes['Pele'].append['Xavi']
What's great about this graph-like data structure is that not only do you have the number of passes preserved, but you also have information about each pass preserved (from Messi to Iniesta)
And here is a super bare-bones implementation of some functions which capture this behavior (I think a beginner should be able to grasp this stuff, let me know if anything below is a bit too confusing)
passes = {}
def new_pass(player1, player2):
# if p1 has no passes, create a new entry in the dict, else append to existing
if player1 not in passes:
passes[player1] = [player2]
else:
passes[player1].append(player2)
def total_passes(player1):
# if p1 has any passes, return the total number; otherewise return 0
total = len(passes[player1]) if player1 in passes else 0
return total
def total_passes_from_p1_to_p2(player1, player2):
# if p1 has any passes, count number of passes to player 2; otherwise return 0
total = passes[player1].count(player2) if player1 in passes else 0
return total
Ideally, you would be saving passes in some database that you could continuously update, but even without a database, you can add the following code and run it to get the idea:
# add some new passes!
new_pass('Messi', 'Xavi')
new_pass('Xavi', 'Iniesta')
new_pass('Iniesta', 'Messi')
new_pass('Messi', 'Iniesta')
new_pass('Iniesta', 'Messi')
# let's see where we currently stand
print total_passes('Messi')
print total_passes('Iniesta')
print total_passes_from_p1_to_p2('Messi', 'Xavi')
Hopefully you find this helpful; here's some more on python implementation of graphs from the python docs (this was a fun answer to write up, thanks!)
I suggest you construct a two dimensional square array. The array should have dimensions N x N. Each index represents a player. So the value at passes[i][j] is the number of times the player i passed to player j. The value passes[i][i] is always zero because a player can't pass to themselves
Here is an example.
players = ['Charles','Meow','Rebecca']
players = dict( zip(players,range(len(players)) ) )
rplayers = dict(zip(range(len(players)),players.keys()))
passes = []
for i in range(len(players)):
passes.append([ 0 for i in range(len(players))])
def pass_to(f,t):
passes[players[f]][players[t]] += 1
pass_to('Charles','Rebecca')
pass_to('Rebecca','Meow')
pass_to('Charles','Rebecca')
def showPasses():
for i in range(len(players)):
for j in range(len(players)):
print("%s passed to %s %d times" % ( rplayers[i],rplayers[j],passes[i][j],))
showPasses()

Making a statistics program

I am trying to write a Python program that computes and prints the following :
the average score from a list of scores
the highest score from a list of scores
the name of the student who got the highest score.
The program starts by asking the user to enter the number of cases. For EACH case, the program should ask the user to enter the number of students. For each student the program asks the user to enter the student's name and marks. For EACH case the program reports the average marks, the highest marks and the name of the student who got the highest marks.
Also
If there are more than one person with the highest score in a CASE, the program should report the first occurrence only.
The average score and the highest score should have exactly 2 decimal places.
The output should be as in the sample program output.
What I have been trying so far is the following:
grade=[]
name_list=[]
cases=int(input('Enter number of cases: '))
for case in range(1,cases+1):
print('case',case)
number=int(input('Enter number of students: '))
for number in range (1,number+1):
name=str(input('Enter name of student: '))
name_list.append(name)
mark=float(input('Enter mark of student:'))
grade.append(mark)
highest= max (grade)
average=(sum(grade)/number)
high_name=grade.index(max(grade))
print('average',average)
print('Highest',highest)
print (high_name)
This is what i have deciphered so far. my biggest problem now is getting the name of the individual with the high score. Any thoughts and feedback is much appreciated. As with respect to the answer posted below, i am afraid the only thing i do not understand is the dictionary function but otherwise the rest does make sense to me.
This resembles an assignment, it is too specific on details.
Anyways, the official docs are a great place to get started learning Python.
They are quite legible and there's a whole bunch of helpful information, e.g.
range(start, end): If the start argument is omitted, it defaults to0
The section about lists should give you a head start.
numcases = int(input("How many cases are there? "))
cases = list()
for _ in range(numcases):
# the _ is used to signify we don't care about the number we're on
# and range(3) == [0,1,2] so we'll get the same number of items we put in
case = dict() # instantiate a dict
for _ in range(int(input("How many students in this case? "))):
# same as we did before, but skipping one step
name = input("Student name: ")
score = input("Student score: ")
case[name] = score # tie the score to the name
# at this point in execution, all data for this case should be
# saved as keys in the dictionary `case`, so...
cases.append(case) # we tack that into our list of cases!
# once we get here, we've done that for EVERY case, so now `cases` is
# a list of every case we have.
for case in cases:
max_score = 0
max_score_student = None # we WILL need this later
total_score = 0 # we don't actually need this, but it's easier to explain
num_entries = 0 # we don't actually need this, but it's easier to explain
for student in case:
score = case[student]
if score > max_score:
max_score = score
max_score_student = student
total_score += score
num_entries += 1
# again, we don't need these, but it helps to demonstrate!!
# when we leave this for loop, we'll know the max score and its student
# we'll also have the total saved in `total_score` and the length in `num_entries`
# so now we need to do.....
average = total_score/max_entries
# then to print we use string formatting
print("The highest score was {max_score} recorded by {max_score_student}".format(
max_score=max_score, max_score_student=max_score_student))
print("The average score is: {average}".format(average=average))

Python Min-Max Function - List as argument to return min and max element

Question: write a program which first defines functions minFromList(list) and maxFromList(list). Program should initialize an empty list and then prompt user for an integer and keep prompting for integers, adding each integer to the list, until the user enters a single period character. Program should than call minFromList and maxFromList with the list of integers as an argument and print the results returned by the function calls.
I can't figure out how to get the min and max returned from each function separately. And now I've added extra code so I'm totally lost. Anything helps! Thanks!
What I have so far:
def minFromList(list)
texts = []
while (text != -1):
texts.append(text)
high = max(texts)
return texts
def maxFromList(list)
texts []
while (text != -1):
texts.append(text)
low = min(texts)
return texts
text = raw_input("Enter an integer (period to end): ")
list = []
while text != '.':
textInt = int(text)
list.append(textInt)
text = raw_input("Enter an integer (period to end): ")
print "The lowest number entered was: " , minFromList(list)
print "The highest number entered was: " , maxFromList(list)
I think the part of the assignment that might have confused you was about initializing an empty list and where to do it. Your main body that collects data is good and does what it should. But you ended up doing too much with your max and min functions. Again a misleading part was that assignment is that it suggested you write a custom routine for these functions even though max() and min() exist in python and return exactly what you need.
Its another story if you are required to write your own max and min, and are not permitted to use the built in functions. At that point you would need to loop over each value in the list and track the biggest or smallest. Then return the final value.
Without directly giving you too much of the specific answer, here are some individual examples of the parts you may need...
# looping over the items in a list
value = 1
for item in aList:
if item == value:
print "value is 1!"
# basic function with arguments and a return value
def aFunc(start):
end = start + 1
return end
print aFunc(1)
# result: 2
# some useful comparison operators
print 1 > 2 # False
print 2 > 1 # True
That should hopefully be enough general information for you to piece together your custom min and max functions. While there are some more advanced and efficient ways to do min and max, I think to start out, a simple for loop over the list would be easiest.

Categories

Resources