Grade Mapping Using Bisect - python-3.x

from bisect import bisect
grades = "FEDCBA"
breakpoints = [30, 44, 66, 75, 85]
def grade(total):
return grades[bisect(breakpoints, total)]
print(grade(66))
print(list(map(grade, [33, 99, 77, 44, 12, 88])))
'''
C
['E', 'A', 'B', 'D', 'F', 'A']
[Program finished]'''
Not my program. Imported from enki.
Bisect module provides support for maintaining a list in sorted order without having to sort the list after each insertion.
So, when we call grade(66). It passes 66 to the grade function which returns C, How?
The second print statement is even more confusing.
It is mapping function grade with a List.
If I try to print, print (grades[bisect(breakpoints, grades)]),
I get Err,
TypeError: '<' not supported between instances of 'str' and 'int'

Your code produced the correct results for the data you fed it!
The breakpoint list [30, 44, 66, 75, 85] bisects the letter string "FEDCBA" as follows:
If grade < 30 then F
If 30 <= grade < 44 then E
If 44 <= grade < 66 then D
If 66 <= grade < 75 then C
If 75 <= grade < 85 then B
If 85 <= grade then A
Therefore print(grade(66)) correctly resulted in an output of C.
Similarly, your print(list(map(grade, [33, 99, 77, 44, 12, 88]))) correctly resulted in an output of ['E', 'A', 'B', 'D', 'F', 'A'].
Now regarding your getting TypeError because of print (grades[bisect(breakpoints, grades)]), it just looks like you meant to do grades[bisect(breakpoints, total)] instead. Notice total instead of grades as the second argument to bisect().
Here's another version of the working code which puts all the variables at the top so you can change them easier for testing:
data_list = [33, 99, 77, 44, 12, 88]
grade_string = 'FEDCBA'
breakpoint_list = [30, 44, 66, 75, 85]
def grade(total, breakpoints=breakpoint_list, grades=grade_string):
i = bisect(breakpoints, total)
return grades[i]
print([grade(total) for total in data_list])
The output is:
['E', 'A', 'B', 'D', 'F', 'A']

from bisect import bisect
grades = "FEDCBA"
breakpoints = [95, 44, 66, 75, 85]
def grade(total):
i = bisect(breakpoints, total)
return grades[i]
print("Original Data:", [grade(total) for total in breakpoints ])
print("Data within print statement:",list(map(grade, [33, 99, 77, 44, 12, 88])))
Thanks Scott,
Original Data: ['A', 'D', 'C', 'B', 'A']
Data within print statement: ['F', 'A', 'B', 'D', 'F', 'A']
I was able to produce the output I wanted for learning purpose

Related

How to divide one element to another element in a list in python

There is a couple of lists and I want to write a code thats input as DIVIDE,(fruit name) and the output is that fruits datas(5th lement/3rd element) from its list.For example one of the list is ['apple', 'a', 147, 457, 66, 119, 21, 6, 8] and the output is 0.26!
fruits = [['apple', 'a', 147, 457, 66, 119, 21, 6, 8], ['banana', 'b', 131, 454, 53, 108, 19, 0, 20], ['orange', 'o', 124, 454, 54, 110, 20, 2, 29], 21]
command, name = input().split()
def count_machine(command, name):
if command == "DIVIDE" :
for fru in fruits:
if name == fru[0]:
result = [fru[5] / fru[3]]
return result
result = count_machine(command, name)
if type(result) == float:
print("%.2f" % result)
Where am i doing wrong,isn't it possible to divide [fru[5] / fru[3]] two elements like this?
Remove [] from result
if name == fru[0]:
result = [fru[5] / fru[3]]
Should be
if name == fru[0]:
result = fru[5] / fru[3]
As your code make result a list not a float
The result returned is a list. So, instead of checking for float type, do this to capture the float value-
if type(result) == list:
print("%.2f" % result[0])

How to get key from the value of dictionary

I have the following set of rules for grading system
if 25 < score <= 30, grade = A.
if 20 < score <= 25, grade = B.
if 15 < score <= 20, grade = C.
if 10 < score <= 15, grade = D.
if 5 < score <= 10, grade = E.
if 0 <= score <= 5, grade = F.
so I have to write a function which takes score as parameter and returns letter grade. So I can do this using selections(if, else). But I want to do it in different manner.
for instance I want to declare a dictionary like below:
gradeDict = {
'A': [26, 27, 28, 29, 30],
'B': [21, 22, 23, 24, 25],
'C': [16, 17, 18, 19, 20],
'D': [11, 12, 13, 14, 15],
'E': [6, 7, 8, 9, 10],
'F': [0, 1, 2, 3, 4, 5]
}
so while checking the score with values I want to return the key
In python I've learned something like dict.get(term, 'otherwise') but it will give you the values. Is there any other mechanism that does the opposite, ie: if we can pass the value in the get method it will return the key?
The bisect standard library offers an elegant solution to problems like this one. In fact, grading is one of the examples shown in the docs.. Here is an adaption of the example modeled on OP's grading curve:
Example:
from bisect import bisect_left
def grade(score, breakpoints=[5, 10, 15, 20, 25], grades='FEDCBA'):
i = bisect_left(breakpoints, score)
return grades[i]
[grade(score) for score in [1, 5, 8, 10, 11, 15, 17, 20, 22, 25, 26]]
Output:
['F', 'F', 'E', 'E', 'D', 'D', 'C', 'C', 'B', 'B', 'A']
Funny thing is that you don't even need a dictionary for this, just an array. Ofc you can do it in a dictionary way style by declaring the following dict:
gradeDict = {
1:'F',
2:'E',
3:'D',
4:'C',
5:'B',
6:'A'
}
This dict seems to be useless since it's just an ordered list of indexes 1,2,3...
You can transform it: grates_arr = ['F', 'E', 'D', 'C', 'B', 'A']
But how can I get the letter that I need? you may ask. Simple, divide the score by 5. 21 // 5 means 4. grates_arr[21//5] is 'B'.
2 more particular cases:
when the score divides 5 means you have to subtract 1 because for example 25 // 5 means 5 but grates_arr[5] is A not B.
when score is 0 do not subtract.

Cannot map an item in a list to a list in list of lists

I have two lists like so,
list1 = ['a','b','c','d']
list2 = [[20,30,15], [23,32,62,234, 234], [34,345,5345], [12]]
How can I map them so it outputs:
a 20
a 30
a 15
b 23
b 32
b 62
.
.
.
d 12
I tried this
list1 = ['a', 'b', 'c', 'd']
list2 = [[20, 30, 15], [23, 32, 62, 234, 234], [34, 345, 5345], [12]]
for item in list2:
for al, it, in zip(list1, item):
print(al, it)
which gives
a 20
b 30
c 15
a 23
b 32
c 62
d 234
a 34
b 345
c 5345
a 12
Using enumerate():
list1 = ['a', 'b', 'c', 'd']
list2 = [[20, 30, 15], [23, 32, 62, 234, 234], [34, 345, 5345], [12]]
for index, alpha in enumerate(list1):
for number in list2[index]:
print(alpha, number)

Loop over columns and calculate relative change within groups

I have a dataset (df) and want to achieve df_goal. That is to create a new variable that captures the relative change within groups from value1 and value2. In my real dataset I have a lot of columns, so I want to find a solution that loops over columns and add new ones along the way.
I have tried versions of the snippet below but it doesn't work. Any suggestions?
for col in df.columns:
df[col + 'REL_CGH'] = df.groupby(['GROUP']).apply((df.col / dfcol[0]) * 100)
import pandas as pd
df = pd.DataFrame({'GROUP': ['A', 'A', 'A', 'B', 'B', 'B'],
'VALUE1': [5, 6, 7, 3, 5, 8],
'VALUE2': [11, 16, 21, 321, 401, 423]})
df_goal = pd.DataFrame({'GROUP': ['A', 'A', 'A', 'B', 'B', 'B'],
'VALUE1': [5, 6, 7, 3, 5, 8],
'VALUE2': [11, 16, 21, 321, 401, 423],
'VALUE1_REL_CHG': [100, 120, 140, 100, 167, 267],
'VALUE2_REL_CHG' :[100, 145, 191, 100, 174, 183]})
You can use GroupBy.transform with GroupBy.first for first value per groups of all columns defined in list cols, divide by DataFrame.div, round and convert to integers, use DataFrame.add_suffix and last append to original:
cols = ['VALUE1','VALUE2']
df = (df.join(df[cols].div(df.groupby(['GROUP'])[cols].transform('first'))
.mul(100)
.round()
.astype(int)
.add_suffix('_REL_CGH')))
print (df)
GROUP VALUE1 VALUE2 VALUE1_REL_CGH VALUE2_REL_CGH
0 A 5 11 100 100
1 A 6 16 120 145
2 A 7 21 140 191
3 B 3 321 100 100
4 B 5 401 167 125
5 B 8 423 267 132
Your solution should be changed with lambda function, but is slowier if large DataFrame:
for col in cols:
df[col + 'REL_CGH'] = df.groupby(['GROUP'])[col].apply(lambda x: (x / x.iloc[0]) * 100)

python3 bisect sequence association

I'm struggling to understand how Python makes the association between the breakpoints sequence and the grades sequence.
def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
i = bisect.bisect(breakpoints, score)
return grades[i]
print([grade(score) for score in [33, 59, 99, 77, 70, 89, 90, 100]])
Result = ['F', 'F', 'A', 'C', 'C', 'B', 'A', 'A']
How does python know that a score below 60 == F, score between 60-70 is D, 70-80 is C etc ?
This answer says it all. It's an advanced concept so it takes some time to wrap your head around it.

Resources