How to understand complex lists in python - python

Sorry this will be a very basic question, I am learning python.
I went through a coding exercise to calculate bmi and went for a straightforward way:
def bmi(weight, height):
bmi = weight / height ** 2
if bmi <= 18.5:
return "Underweight"
elif bmi <= 25:
return "Normal"
elif bmi <= 30:
return "Overweight"
else:
return "Obese"
However, in the exercise solutions I also see this one:
def bmi(weight, height):
b = weight / height ** 2
return ['Underweight', 'Normal', 'Overweight', 'Obese'][(b > 30) + (b > 25) + (b > 18.5)]
I want to understand what this double/back-to-back list is where they've got [items][conditions] but I can't find the name of it to learn about it - what is the name for this? Is it a part of list comprehensions?

observe this line carefully
['Underweight', 'Normal', 'Overweight', 'Obese'][(b > 30) + (b > 25) + (b > 18.5)]
Above is line is actually list indexing [(b > 30) + (b > 25) + (b > 18.5)] this gives the index of the list ['Underweight', 'Normal', 'Overweight', 'Obese']. Let us say b > 30 then it satisfies all the three conditions (b > 30) + (b > 25) + (b > 18.5) the equivalent boolean value of each condition is 1 making the sum 3 and returns index 3 which is Obese. Similarly it works for other conditions.

Related

Is this colidity challenge (Zinc 2018) test case wrong?

I just randomly picked up this challenge...
The question and report can be found here: https://app.codility.com/demo/results/training3NRM6P-HSG/
For test case N = 100,000, all performances are different., it says: got 166661666700000 expected 665533373
For N = 100,000 all different performance should not it be: C(100000, 3) = int(len(A) * (len(A) - 1) * (len(A) - 2) / 3 / 2), how is the 665533373 calculated?
Paste my solution here for reading convenience:
def solution(A):
# write your code in Python 3.6
if not A or len(A) < 3:
return 0
if len(set(A)) == len(A):
return int(len(A) * (len(A) - 1) * (len(A) - 2) / 3 / 2)
check = {}
def bt(path, nxt):
if len(path) == 3:
t = tuple(path)
if t not in check:
check[t] = None
return
if len(path) > 3:
return
for i in range(nxt, len(A)):
if i > nxt and A[i] == A[i-1]:
continue
path.append(A[i])
bt(path, i + 1)
path.pop()
bt([], 0)
return len(check)
Look closer at the question! It clearly says that, "since the answer can be very large, provide it modulo 10^9 + 7 (1,000,000,007)".
Your answer, 166661666700000 % (10^9 + 7) = 665533373, which is the expected result.
So all you need to do theoretically is edit the last line of your code like so:
return len(check) % (10**9 + 7)

Code simplification. Between positive and negative conditions

I have a question, how to simplify this code? I have the impression that it can be done in 3 conditional instructions and not in 6 ...
if (PID > 10 and self.last_pid > 0):
if (PID >= self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
if (PID < -10 and self.last_pid < 0):
if (PID <= self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
(Which might be simplified to:)
if A > 10 and B > 0:
if A >= B:
# do block A
else:
# do block B
if A < -10 and B < 0:
if A <= B):
# do block A
else:
# do block B
This should be equivalent to your two cases for positive and negative values:
if abs(A) > 10 and A * B > 0:
if abs(A) >= abs(B):
# do block A
else:
# do block B
Explanation:
abs(A) corresponds to A > 10 and A < -10 respectively
A * B > 0 means that both have the same sign and B != 0
abs(A) >= abs(B) means A <= B if both are < 0 and A => B if both are > 0
Now that's shorter and less repetitive, but whether it's easier to understand is for you to decide. In any case, you should add a comment explaining the code and that it is supposed to do.
With your original variables and procedures, this would be:
if abs(PID) > 10 and PID * self.last_pid > 0:
if abs(PID) >= abs(self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
Some more points that I just noticed:
your line self.increase_val + (percent_last / 100 * self.increase_val) does not do anything. I guess the + should be = or +=?
it is kind of pointless to first * 100 to get percent just to then / 100 again
it's odd how in one case you add increase_val to KP before increasing it, but after increasing it in the other case; is this intentional?
In fact, I think that this could be further simplified to this, provided that the inner if is used to cap the increase to the increase_val; not sure whether it should be added to Kp before or after being increased itself, though, or if that should actually depend on the case.
if abs(PID) > 10 and PID * self.last_pid > 0:
self.setKp(self.Kp + self.increase_val)
self.increase_val *= 1 + min(PID/self.last_pid, 1)
It's a bit long, but it takes less lines:
if (A > 10 and B > 10 and A>=B) or (A < -10 and B < 0 and A<= B):
#do block a
else:
#do block b
If you don't like it being so long, I would recommend turning each side of the or on the first line into a boolean variable and then using said variable in the if statement. Like so:
condA = A > 10 and B > 10 and A>=B
condB = A < -10 and B < 0 and A<= B
if condA or condB:
#do block a
else:
#do block b

How to use series. apply() to create conditional pandas series?

I am trying to create a new column in my df using numerical data from another column. I attempted using a for loop and a series of if statements to categorize the numerical data into strings that I want to now use to create the new column. The following data is from the WNBA 2010-2011 dataset about the players.
def clean(col):
for xp in col:
if xp < 1:
print('Rookie')
elif ((xp >= 1) and (xp <= 3)):
print('Little experience')
elif ((xp >= 4) and (xp <= 5)):
print('Experienced')
elif ((xp > 5) and (xp < 10)):
print('Very experienced')
elif (xp > 10):
print("Veteran")
I tried using series.apply() and series.map() but both of these return a new column called XP as follows
XP = df.Experience.apply(clean)
df['XP'] = XP
However, when I checked the dtypes it says that the newly created column is a NONETYPE object. Is this because I am using the print function in the for loop as opposed to manipulating the actual value? If so what should I do to return the string values specified?
Thanks in advance for the help.
df = pd.DataFrame({'xp':[0,2,4,6,20,'4']})
Put in a string because you had the type error.
def clean(str_xp):
xp = int(str_xp)
if xp < 1:
return('Rookie')
elif ((xp >= 1) and (xp <= 3)):
return('Little experience')
elif ((xp >= 4) and (xp <= 5)):
return('Experienced')
elif ((xp > 5) and (xp < 10)):
return('Very experienced')
elif (xp > 10):
return ("Veteran")
df['rank'] = df['xp'].apply(clean)
df returns:
xp rank
0 0 Rookie
1 2 Little experience
2 4 Experienced
3 6 Very experienced
4 20 Veteran
5 4 Experienced
That's because your function doesn't return anything (so returns None by default). You need to replace those print statements with return.
Also, you don't need to loop over the column in your function - apply does that for you in a vectorized way. Try this:
def clean(xp):
if xp < 1:
return 'Rookie'
elif ((xp >= 1) and (xp <= 3)):
return 'Little experience'
elif ((xp >= 4) and (xp <= 5)):
return 'Experienced'
elif ((xp > 5) and (xp < 10)):
return 'Very experienced')
elif (xp > 10):
return "Veteran"
df['XP'] = df.Experience.apply(clean)
Bear in mind also that the way your equalities are currently written, your function will return None if xp == 10.

Project 2 Human Pyramid Calculations

For simplicity we will assume that everyone in the pyramid weighs exactly 200 pounds. Person
A at the top of the pyramid has no weight on her back. People B and C are each carrying half of
person A's weight. That means that each of them is shouldering 100 pounds.
Now, let's look at the people in the third row. Let’s begin by focusing on person E. How much
weight is she supporting? Well, she’s directly supporting half the weight of person B (100
pounds) and half the weight of person E (100 pounds), so she’s supporting at least 200 pounds.
On top of this, she’s feeling some of the weight that people B and C are carrying. Half of the
weight that person B is shouldering (50 pounds) gets transmitted down onto person E and half
the weight that person C is shouldering (50 pounds) similarly gets sent down to person E, so
person E ends up feeling an extra 100 pounds. That means she’s supporting a net total of 300
pounds.
Write a recursive function (using no loops), weightOn(r,c), which returns the weight
on the back of the person in row r and and column c. Rows and columns are 0-based,
so the top position is (0,0), for example, and person H is in position (3,1). The following
also hold:
weightOn(0,0) == 0.00
weightOn(3,1) == 425.00
Weights should be floating-point numbers.
I have already tried a lot. I will include my most recent code below.
t = 0.0
x = 0.0
def weightOn(r, c):
global t
if r < 0:
print('Not valid')
elif r == 0 and c == 0:
return t
elif r > 0 and c == 0:
t += 200 / (2 ** r)
return weightOn(r - 1, 0)
elif r > 0 and c == r:
t += 200 / (2 ** r)
return weightOn(r - 1, 0)
elif r > c > 0:
mid(r, c)
return t
def mid(r, c):
global x
x = weightOn(r - 1, c - 1) + weightOn(r - 1, c)
'''I have also tried: x = (((weightOn(r - 1, c - 1) + 200) / 2) + ((weightOn(r - 1, c) + 200) / 2))'''
return x
r = int(input('r: '))
c = int(input('c: '))
weightOn(r, c)
if r > c > 0:
print(x)
else:
print(t)
It always brings up the wrong output. I can correctly pull up all of the edges (when c == 0 or c == r). But other than that it won't work.
Ex. Input (3, 1) outputs 500
(3, 2) outputs 550
Using global variables suggests that you haven't considered this recursively.
Each person shoulders half the weight of the persons on each shoulder. The effective weight of each person is what they shoulder, plus 200 pounds. If a person is on the edge, then the "person" on the other shoulder has 0 weight.
So ...
def weight(r, c):
# Code base cases
if r < 0: # Past the pyramid top; no such person
return 0
if c < 0 or c > r: # Off the edge; no such person
return 0
return 200 + (weight(r - 1, c - 1) + weight(r - 1, c)) / 2
Then weightOn is simply the above routine without the 200 +.
That's your outline; can you take it from there?
def weight_on (r,c):
second_person = 200 #finds out if there is a second person on top or not
if c - 1 < 0 or c > r - 1 :
second_person = 0
if c < 0 or c > r:
return 0
elif r <= 0:
return 0
else:
return (second_person + 200 + weight_on (r - 1,c - 1) + weight_on (r - 1,c))/2

python checking surrounding points list of lists index out of range

I'm working on a school project and i'm trying to go through list of lists containing numbers. I'm trying to check all 8 surrounding "block" but i'm getting index out of range exception
filling the list:
for i in range(0, self.sizeX):
temp = []
for j in range(0, self.sizeY):
temp.append(random.randint(0, 100))
self.map.append(temp)
checking the surrounding
def check(self, i, j):
count = 0
if j-1 >= 0 & self.map[i][j-1] > 50:
count += 1
if (j+1) < len(self.map[i]) & self.map[i][j+1] > 50:
count += 1
if i-1 >= 0 & self.map[i-1][j] > 50:
count += 1
if i+1 < self.sizeX & self.map[i+1][j] > 50:
count += 1
if i-1 >= 0 & j-1 >= 0 & self.map[i-1][j-1] > 50:
count += 1
if i+1 < self.sizeX & j-1 >= 0 & self.map[i+1][j-1] > 50:
count += 1
if i-1 >= 0 & j+1 < self.sizeY & self.map[i-1][j+1] > 50:
count += 1
if i+1 < self.sizeX & j+1 < self.sizeY & self.map[i+1][j+1] > 50:
count += 1
return count
it looks like the conditions which check >=0 work but the once which check the size limit don't
btw i had this exact thing working in php with no problem
Your conditions seem to be correct, apart from substituting & (bitwise and) for and (logical and).
I also suggest pulling the tests out as variables; it helps make your code easier to read.
Also note that you are using columnar indexing, ie map[x][y]; it is more common to use row-aligned indexing ie map[y][x]. It is not "wrong" per se, but may be a bit clumsier to work with.
from random import randint
class Map:
def __init__(self, width, height):
self.width = width
self.height = height
self.map = [[randint(0, 100) for y in range(height)] for x in range(width)]
def neighbor_fn(self, i, j, fn=lambda x: x > 50):
left = i - 1
right = i + 1
above = j - 1
below = j + 1
do_left = left >= 0
do_right = right < self.width
do_above = above >= 0
do_below = below < self.height
return (
(do_left and do_above and fn(self.map[left][above]))
+ (do_above and fn(self.map[i][above]))
+ (do_right and do_above and fn(self.map[right][above]))
+ (do_left and fn(self.map[left][j]))
+ (do_right and fn(self.map[right][j]))
+ (do_left and do_below and fn(self.map[left][below]))
+ (do_below and fn(self.map[i][below]))
+ (do_right and do_below and fn(self.map[right][below]))
)

Categories

Resources