python efficient if statements conditions [closed] - python

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 months ago.
Improve this question
I created a sample program for 3 data points however, I have many data points and need more efficient code to run. The logic is I am comparing every Pi with its next P(i+1) and post comparing all the differences, I am selecting the max value and taking its relevant BSPi & SSPi.
additional condition is if P[i] is greater than p[i+1]; it should be greater than 50.
sp1=100
sp2=150
sp3=200
sp4=250
p1=90
p2=40
p3=120
p4=150
if p1-p2>=0:
d1=p1-p2-50
bsp1=sp2
ssp1=sp1
else:
d1=p2-p1
bsp1=sp1
ssp1=sp2
if p2-p3>=0:
d2=p2-p3-50
bsp2=sp3
ssp2=sp2
else:
d2=p3-p2
bsp2=sp2
ssp2=sp3
if p3-p4>=0:
d3=p3-p4-50
bsp3=sp4
ssp3=sp3
else:
d3=p4-p3
bsp3=sp3
ssp3=sp3
data = {'d1': d1,'d2': d2, 'd3': d3,}
max_data=max(data, key=data.get)
if max_data=='d1':
bsp=bsp1
ssp=ssp1
elif max_data=='d2':
bsp=bsp2
ssp=ssp2
else:
bsp=bsp3
ssp=ssp3
print(bsp)
print(ssp)

Create an array to contain sp = [sp1, ... , spn]
Create an array to contain p = [p1, ... , pn]
Initialize a value max = 0
Initialize a value index = 0
Iterate through range of p (given that len(p) > 1), calculate the difference of i and i+1 elements. If the difference is greater than max, store the difference and store the index.
In the end, store into bsp = sp[index] and ssp = sp[index+1]
def find_max_in_data(p, sp):
max_diff = 0
index = 0
for i in range(len(p)-1):
diff = abs(p[i+1] - p[i])
if diff > max_diff:
max_diff = diff
index = i
ssp = sp[index]
bsp = sp[index+1]
EDIT: For more particular code,
def find_max_in_data(p, sp):
max_diff = 0
bspindex = 0
sspindex = 0
for i in range(len(p)-1):
if p[i] - p[i+1] >= 0:
diff = p[i] - p[i+1] - 50
if diff > max_diff:
max_diff = diff
bspindex = i+1
sspindex = i
else:
diff = p[i+1] - p[i]
if diff > max_diff:
max_diff = diff
bspindex = i
sspindex = i+1
ssp = sp[sspindex]
bsp = sp[bspindex]

I suggest a different approach.
Rather than defining many discrete variables, write a class that incorporates all of the associated variables for a "data point". Something like this (with full property, str and repr functionality):
class DataPoint:
def __init__(self, p, sp):
self._p = p
self._sp = sp
self._ssp = None
self._bsp = None
self._d = None
#property
def p(self):
return self._p
#property
def sp(self):
return self._sp
#property
def ssp(self):
return self._ssp
#ssp.setter
def ssp(self, v):
self._ssp = v
#property
def bsp(self):
return self._bsp
#bsp.setter
def bsp(self, v):
self._bsp = v
#property
def d(self):
return self._d
#d.setter
def d(self, v):
self._d = v
def __repr__(self):
return f'{self.p=} {self.sp=} {self.ssp=} {self.bsp=} {self.d=}'
def __str__(self):
return f'p={self.p}, sp={self.sp}, ssp={self.ssp}, bsp={self.bsp}, d={self.d}'
Using this structure you can build a list of classes like this:
data_points = []
data_points.append(DataPoint(90, 100))
data_points.append(DataPoint(40, 150))
data_points.append(DataPoint(120, 200))
data_points.append(DataPoint(150, 250))
Note how the p and sp values relate to the ones in the question.
Now that we have a list of these classes we can write a single piece of code to process them and populate them with appropriate values.
for dpa, dpb in zip(data_points, data_points[1:]):
if dpa.p - dpb.p >= 0:
dpa.d = dpa.p - dpb.p - 50
dpa.bsp = dpb.sp
dpa.ssp = dpa.sp
else:
dpa.d = dpb.p - dpa.p
dpa.bsp = dpa.sp
dpa.ssp = dpb.sp
print(dpa)
This gives the following output:
p=90, sp=100, ssp=100, bsp=150, d=0
p=40, sp=150, ssp=200, bsp=150, d=80
p=120, sp=200, ssp=250, bsp=200, d=30
Note that there are just 3 outputs. This is as expected due to the way the comparisons are carried out.

Related

What is the minimum number of case (or if/else) statements required to calculate all unknown values corresponding to properties of an object?

Consider a right-angle triangle, which has the properties
Hypotenuse (side)
Adjacent (side)
Opposite (side)
Area
Given any 2 of these properties, it is always possible to calculate the value of the other 2. My question relates to what the most efficient/elegant way of doing this is.
At present, the only way of doing this that I can think of is to use (4C2)*2 = 12 case statements, each relating to a possible combination of inputsa that may be provided.
For example, using python you might have something like
class RightAngleTriangle():
def __init__(this, propertyType1, propertyValue1, propertyType2, propertyValue2):
this.adjacent = 0
this.opposite = 0
this.hypotenuse = 0
this.area = 0
if (propertyType1 == "adjacent" and propertyType2 == "opposite"):
this.adjacent = propertyValue1
this.opposite = propertyValue2
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "opposite" and propertyType2 == "adjacent"):
this.adjacent = propertyValue2
this.opposite = propertyValue1
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "adjacent" and propertyType2 == "hypotenuse"):
this.adjacent = propertyValue1
this.hypotenuse = propertyValue2
this.opposite = (propertyValue2**2 + propertyValue1**2)**0.5
this.area = (this.opposite * this.adjacent)/2
...and so on...
You could then create your triangle object, and print its four properties, using code (in this case python) like the below.
t1 = RightAngleTriangle("adjacent", 10, "opposite", 12)
print(t1.adjacent)
print(t1.opposite)
print(t1.hypotenuse)
print(t1.area)
This is hideous. Is there a more eligant solution to this problem?
Yes, at least two - one using args and one using key word args. So:
class RightAngleTriangle():
def __init__(self, *args):
self.adjacent = 0
self.opposite = 0
self.hypotenuse = 0
self.area = 0
for property_type, property_value in zip(args[::2], args[1::2]):
setattr(self, property_type, property_value)
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
This would work with your current input, but let's agree - it's still not very elegant solution. So, let's use kwargs:
class RightAngleTriangle():
def __init__(self, adjacent=0, opposite=0, hypotenuse=0):
self.adjacent = adjacent
self.opposite = opposite
self.hypotenuse = hypotenuse
self.area = 0
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
And now you can simply call this code as:
t1 = RightAngleTriangle(adjacent=10, opposite=12)

Python - high disk usage in SumTree

I've encountered some weird behaviour of my python program. Basically when I tried to create adn fill a SumTree of length larger than 1000, my disk usage increases a lot to ~300MB/s then the programme died.
I'm pretty sure there's no file r/w involved in this process, and the problem is with the add function. The code is shown below.
import numpy as np
class SumTree():
trans_idx = 0
def __init__(self, capacity):
self.num_samples = 0
self.capacity = capacity
self.tree = np.zeros(2 * capacity - 1)
self.transitions = np.empty(self.capacity, dtype=object)
def add(self, p, experience):
tree_idx = self.trans_idx + self.capacity - 1
self.transitions[self.trans_idx] = experience
self.transitions.append(experience)
self.update(tree_idx, p)
self.trans_idx += 1
if self.trans_idx >= self.capacity:
self.trans_idx = 0
self.num_samples = min(self.num_samples + 1, self.capacity)
def update(self, tree_idx, p):
diff = p - self.tree[tree_idx]
self.tree[tree_idx] = p
while tree_idx != 0:
tree_idx = (tree_idx - 1) // 2
self.tree[tree_idx] += diff
def get_leaf(self, value):
parent_idx = 0
while True:
childleft_idx = 2 * parent_idx + 1
childright_idx = childleft_idx + 1
if childleft_idx >= len(self.tree):
leaf_idx = parent_idx
break
else:
if value <= self.tree[childleft_idx]:
parent_idx = childleft_idx
else:
value -= self.tree[childleft_idx]
parent_idx = childright_idx
data_idx = leaf_idx - self.capacity + 1
return leaf_idx, self.tree[leaf_idx], self.transitions[data_idx]
#property
def total_p(self):
return self.tree[0] # the root
#property
def volume(self):
return self.num_samples # number of transistions stored
Here's an example where this SumTree object will be used:
def add(self, experience)
max_p = np.max(self.tree.tree[-self.tree.capacity:])
if max_p == 0:
max_p = 1.0
exp = self.Experience(*experience)
self.tree.add(max_p, exp)
where Experience is a named tuple and self.tree is a Sumtree instance, when I removed the last line the high disk usage disappears.
Can anyone help me with this?
I finally sort this out because each experience is a tuple of namedtuple and I'm creating another namedtuple Experience from it. Fixed by changing experience to a tuple of numpy arrays.

How can I create a running average of the last N items in a time series?

My basic idea was to create a linked list, and as each new value comes in, add 1/N times the new value and subtract 1/N times the first value, then move the pointer to first along by one and free the memory that had been associated with first.
This won't ultimately be implemented in Python but just to get the process clear in my head, I tried to write it in Python, but my implementation is flawed. Do I need a doubly linked list for this? Is there an alternative approach (not linked-list based) that would be better?
Here's my attempt so far:
class Link:
def __init__(self,val):
self.next = None
self.value = val
class LinkedList:
def __init__(self,maxlength):
self.current_link = None
self.maxlength = maxlength
self.sum = 0.
self.average = None
self.length = 0
self._first_link = None
def add_link(self,val):
new_link = Link(val)
new_link.next = self.current_link
self.current_link = new_link
if self._first_link is None:
self._first_link = self.current_link
self.sum += val
if self.length < self.maxlength:
self.length += 1
else:
self.sum -= self._first_link.value
self._first_link = self._first_link.next # this line is flawed
self.average = self.sum/self.length
def get_first(self):
return self._first_link.value
# Main
ll = LinkedList(5)
for ii in xrange(10):
ll.add_link(ii)
print ii,ll.get_first(),ll.average
The problem is that _first_link gets set to a value that doesn’t have a next. That is, _first_link gets set to the first item that's added, but its next is None, so I don't see how to move it along by 1 as I want to. This is what makes me wonder if a doubly linked list is needed.
I'd appreciate any advice.
I think the simplest implementation is to use a circular linked list (a.k.a. a ring):
class Link(object):
def __init__(self, value=0.0):
self.next = None
self.value = value
class LinkedRing(object):
def __init__(self, length):
self.sum = 0.0
self.length = length
self.current = Link()
# Initialize all the nodes:
last = self.current
for i in xrange(length-1): # one link is already created
last.next = Link()
last = last.next
last.next = self.current # close the ring
def add_val(self, val):
self.sum -= current.value
self.sum += val
self.current.value = val
self.current = self.current.next
def average(self):
return self.sum / self.length
# Test example:
rolling_sum = LinkedRing(5)
while True:
x = float(raw_input())
rolling_sum.add_val(x)
print(">> Average: %f" % rolling_sum.average())
You can implement this using collections.deque and the numerically stable math for maintaining running averages:
import collections
class AveragingBuffer(object):
def __init__(self, maxlen):
assert( maxlen>1)
self.q=collections.deque(maxlen=maxlen)
self.xbar=0.0
def append(self, x):
if len(self.q)==self.q.maxlen:
# remove first item, update running average
d=self.q.popleft()
self.xbar=self.xbar+(self.xbar-d)/float(len(self.q))
# append new item, update running average
self.q.append(x)
self.xbar=self.xbar+(x-self.xbar)/float(len(self.q))
if __name__=="__main__":
import scipy
ab=AveragingBuffer(10)
for i in xrange(32):
ab.append(scipy.rand())
print ab.xbar, scipy.average(ab.q), len(ab.q)
Okay, I thought of a solution that works in O[1] time. I'm still curious if anyone has a linked-list-based solution, but this solution avoids the LL entirely:
class Recent:
def __init__(self,maxlength):
self.maxlength = maxlength
self.length = 0
self.values = [0 for ii in xrange(maxlength)]
self.index = 0
self.total = 0.
self.average = 0.
def add_val(self,val):
last = self.values[self.index%self.maxlength]
self.values[self.index%self.maxlength] = val
self.total += val
self.total -= last
if self.length < self.maxlength:
self.length += 1
self.average = self.total / self.length
self.index += 1
def print_vals(self):
print ""
for ii in xrange(self.length):
print ii,self.values[ii%self.maxlength]
print "average:",self.average
# Example to show it works
rr = Recent(5)
for ii in xrange(3):
rr.add_val(ii)
rr.print_vals()
for ii in xrange(13):
rr.add_val(ii)
rr.print_vals()

Python object1 = object2

I am writing a vector class in python (just to see if i can). i ran into a problem with the subtract method and i have no idea what could be causing this.
this is the class (i omitted "class Vector:").
def __init__(self, p):
print self
self.p = p
def __str__(self):
return str(list(self.p))
def equals(self, v):
if type(self) == type(v):
return str(self) == str(v)
return false
def size(self):
return len(self.p)
def add(self, v):
a = self.p
b = v.p
if self.size() == v.size():
for i in range(0, self.size()):
a[i] += b[i]
return Vector(a)
raise Exception()
def subtract(self, v):
a = self.p
b = v.p
if self.size() == v.size():
for i in range(0, self.size()):
a[i] -= b[i]
return Vector(a)
raise Exception()
def dot(self, v):
total = 0
if self.size() == v.size():
for i in range(0, len(self.p)):
total += self.p[i] * v.p[i]
return total
raise Exception()
def norm(self):
total = 1
if self.size() == v.size():
for i in range(0, len(self.p)):
total += self.p[i]^2
return total
raise Exception()
when i try to do:
a = Vector([1,1])
a.subtract(Vector[1,1])
print a
my thought says i should get [1,1] as output because i do not change any values of Vector a when i do the subtraction, i return a new vector with the values it should have. when i print the object it shows me that it is in a different space in memory but my output from 'print a' is [0,0]
also if i do
a = Vector(1,1)
b = a
a.subtract(Vector([1,1])
print a,b
my output is [0,0][0,0], what i want is [0,0][1,1]
why does b change with a ?
First question:
(1, 1) - (1, 1) == (0, 0)
The output of your program is correct. You change the values of a in your function with a[i] -= b[i] where a is the list of coordinates (not a copy of the list) in self and b the list of coordinates (again, not a copy) in v.
Second question:
b = a
a and b are now the same object (not different objects with the same value), so they change simultaneously.
Think of a and b as addresses for your computer.
a = Vector(1,1) # There is a Vector object somewhere in memory, e.g. 12345. Then a = 12345
b = a # b = a = 12345 (still the address of the same Vector object)
a.subtract(Vector([1,1])) # Change whatever is stored at the address of a = 12345
print a,b # both are still the address of the same object you modified in the previous step!

Most object oriented way of solving this in python?

I've been told to write a simple program that generates coupon codes, which should offer more than two algorithms (any two) and that the algorithm and the number of codes generated should be read from a config file. Also I've been told that the solution would involve using a known design pattern and that I should look for what pattern is.
I've come up with two solutions for this, but I don't think I've found a proper OOP design pattern that fits for the problem, since objects are data with methods that operate over that data, and in this problem there is little data to operate over, it's more a function (functional?) problem to my naive eyes. Here are the two, one is basically executing the proper static method for the algorithm in the config file and the other returns a reference to a function. Both generate the numbers and print them to the screen.
First method:
class CouponGenerator:
SEQUENTIAL_NUMBERS = "sequentialNumbers"
FIBONACCI_NUMBERS = "fibonacciNumbers"
ALPHANUMERIC_SEQUENCE = "alphanumericSequence"
quantity = 0
algorithm = ""
def __init__(self, quantity, algorithm):
self.quantity = quantity
self.algorithm = algorithm
def generateCouponList(self):
numbers = list()
if self.algorithm == self.SEQUENTIAL_NUMBERS:
numbers = CouponGenerator.generateSequentialNumbers(self.quantity)
elif self.algorithm == self.FIBONACCI_NUMBERS:
numbers = CouponGenerator.generateFibonacciSequence(self.quantity)
for number in numbers:
print number
#staticmethod
def getCouponGenerator(configFile):
cfile = open(configFile)
config = cfile.read()
jsonconfig = json.loads(config)
cg = CouponGenerator(jsonconfig['quantity'], jsonconfig['algorithm'])
return cg
#staticmethod
def generateSequentialNumbers(quantity):
numbers = list()
for n in range(1, quantity+1):
zeroes = 6-len(str(n))
numbers.append(zeroes*"0"+str(n))
return numbers
#staticmethod
def generateFibonacciSequence(quantity):
def fib(n):
a, b = 0, 1
for _ in xrange(n):
a, b = b, a + b
return a
numbers = list()
for n in range(1, quantity+1):
number = fib(n)
zeros = 6-len(str(number))
numbers.append(zeros*"0"+str(number))
return numbers
if __name__ == "__main__":
generator = CouponGenerator.getCouponGenerator("config")
generator.generateCouponList()
Second solution:
class CouponGenerator:
#staticmethod
def getCouponGenerator(algorithm):
def generateSequentialNumbers(quantity):
numbers = list()
for n in range(1, quantity+1):
zeroes = 6-len(str(n))
numbers.append(zeroes*"0"+str(n))
return numbers
def generateFibonacciSequence(quantity):
def fib(n):
a, b = 0, 1
for _ in xrange(n):
a, b = b, a + b
return a
numbers = list()
for n in range(1, quantity+1):
number = fib(n)
zeros = 6-len(str(number))
numbers.append(zeros*"0"+str(number))
return numbers
generators = {"sequentialNumbers": generateSequentialNumbers,
"fibonacciNumbers": generateFibonacciSequence}
return generators[algorithm]
class CouponGeneratorApp:
configFile = "config"
def __init__(self):
cfile = open(self.configFile)
config = cfile.read()
self.jsonconfig = json.loads(config)
self.generateCouponCodes()
def generateCouponCodes(self):
generator = CouponGenerator.getCouponGenerator(self.jsonconfig["algorithm"])
numbers = generator(self.jsonconfig["quantity"])
for n in numbers:
print n
if __name__ == "__main__":
app = CouponGeneratorApp()
If you want to make it a little more object oriented I suggest you use some kind of strategy pattern, that means, use a class per generation algorithm (which should have a common interface) and specify that CouponGenrator use an object which implements this interface to do whatever it has to do. This is theory and making interface and everything in your case might be a little to much.
http://en.wikipedia.org/wiki/Strategy_pattern
you could try something like :
class SequentialGenerator(Object):
def implementation():
...
class FibonacciGenerator(Object):
def implementation():
...
class CouponGenerator(Object):
def set_generator(generator):
# set self.generator to either an instance
# of FibonacciGenerator or SequentialGenerator
def generate_coupon_code():
# at some point calls self.generator.implementation()

Categories

Resources