Range() including its bounds for positive and negative steps - python

I want to do a simple program that prints all the numbers in a range A to B, including B.
For ranges having bounds in increasing order, I know that I have to add 1 to the upper bound, like:
range(A, B+1)
But adding 1 to B won't work when the bounds are in decreasing order, like range(17, 15, -1).
How can I code it to work for both increasing and decreasing ranges?

I see why you are facing this issue. Its because you are using the larger value as the first argument and smaller value at the second argument in the range (This is happening due to the negative sign).
For such cases following code will work :
a = 5
b = -5
step = 1
if b < 0:
step = -1
range (a, b + step, step)

I think I don't understand the question properly. There are 3 cases:
A, B both positive
A negative, B positive
A, B both negative
Now if I do this (in Python 2, to avoid having to do list(range(...)): this makes the explanation cleaner):
>>> A = 10; B = 20 # case 1
>>> range(A,B+1)
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
>>> A = -10; B = 2 # case 2
>>> range(A,B+1)
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2]
>>> A = -10; B = -2 # case 3
>>> range(A,B+1)
[-10, -9, -8, -7, -6, -5, -4, -3, -2]
So your remark the last number in the range won't be included doesn't seem to fit with what I can see.
If you are receiving input data where A > B, then the problem is not the negative number, but the fact that range() expects the values to be in ascending order.
To cover that:
>>> A = 2; B = -2 # case 4
>>> A,B = sorted((A,B))
>>> range(A,B+1)
[-2, -1, 0, 1, 2]
This also works for cases 1, 2, and 3.
If I have misunderstood the question please edit it to clarify.

Please check if this works. Thank you.
if A>B and A < 0 and B < 0:
print(list(range(A,B,-1)))
elif A<B:
print(list(range(A,B)))
else:
print(list(range(A,B,-1)))

You could create a function that turns a normal range into one that includes both bounds, like:
def inclusive(r):
return range(r.start, r.stop + r.step, r.step)
You should pass it a range, and it will return a range:
increasing_range = range(2, 5)
print(list(inclusive(increasing_range)))
# [2, 3, 4, 5]
decreasing_range = range(5, -5, -1)
print(list(inclusive(decreasing_range)))
# [5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]
even = range(2, 10, 2)
print(list(inclusive(even)))
# [2, 4, 6, 8, 10]
for odd in inclusive(range(1, 5)):
print(odd)
# 1 2 3 4 5

Last number is never included in python range. You need to adjust the code accordingly.
e.g.
To print values form -5 to -1(included) use,
>>> print(list(range(-5, 0)))
[-5, -4, -3, -2, -1]
In reverse order
>>> print(list(range(-1, -6, -1)))
[-1, -2, -3, -4, -5]

Related

What's an easy way to count reversal points of a dataset?

I have a dataset ('db') with some reversal points. I would like to implement a counter ('counter_vals') starting at 0 that increases at each reversal point. How can I compute these counter values correctly in a simple way?
import numpy as np
import matplotlib.pyplot as plt
db = np.array([12, 0, 6, 3, 0, -3, -6, -3, -6])
x_vals = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
plt.scatter(x_vals, db)
plt.plot(x_vals, db)
plt.show()
The desired output should like
counter_vals = np.array([0, 1, 2, 2, 2, 2, 3, 4, 4])
You might use the derivatives. "a point after that the direction of a slope changes" is indicated by the second derivative. Due to numerical differentiations you will lose the boundary points (but you can improve that by adding a leading zero and by repeating the last element)
db = np.array([12, 0, 6, 3, 0, -3, -6, -3, -6])
deriv0 = np.diff(db) # first derivative
deriv1 = np.sign(np.diff(db)) # signs of first derivative
deriv2 = np.sign(np.diff(deriv1)) # signs of second derivative
deriv3 = np.abs(np.sign(np.diff(deriv1)) ) # absolute value of signs of second derivative
counter_vals = np.cumsum(deriv3)
print("deriv0 =", deriv0)
print("deriv1 =", deriv1)
print("deriv2 =", deriv2)
print("deriv3 =", deriv3)
print()
print("counter_vals =", counter_vals)
deriv0 = [-12 6 -3 -3 -3 -3 3 -3]
deriv1 = [-1 1 -1 -1 -1 -1 1 -1]
deriv2 = [ 1 -1 0 0 0 1 -1]
deriv3 = [1 1 0 0 0 1 1]
counter_vals = [1 2 2 2 2 3 4]

Not able to use this while loop properly

Trying to sum up all the negative numbers in a list using while loop.
given_list_02 = [9, 8, 5, 3, -1, -2, -3, -6]
total3 = 0
i = 0
while True:
if given_list_02[i] >= 0:
i+= 1
else:
total3+= given_list_02[i]
i+= 1
Using for loop (recommended)
In python, for loops are based on iterators. As such, we are able to iterate over the values in the list rather than using a while True loop that updates an index i. This also makes sure we don't get an index out of bound error when i becomes larger than the length of the list:
given_list_02 = [9, 8, 5, 3, -1, -2, -3, -6]
numOfNegatives = 0
for num in given_list_02:
if num < 0:
numOfNegatives+=num
print(numOfNegatives)
Using while loop (not recommended)
You need a terminating condition for the while loop. If we think about what while True does, it will infinitely increment the i variable. So what would happen when i becomes 9? At this point, given_list_02[i] does not exist as given_list_02 only has 8 elements. This is why we need to stop iterating i once it hits 8:
given_list_02 = [9, 8, 5, 3, -1, -2, -3, -6]
total3 = 0
i = 0
while i < len(given_list_02):
if given_list_02[i] >= 0:
i+= 1
else:
total3+= given_list_02[i]
i+= 1
print(total3)
Your while loop does not have a break condition, so it will run indefinitely. As you are accessing list elements, you will access an index out out range at some point.
Try something like this
given_list_02 = [9, 8, 5, 3, -1, -2, -3, -6]
total3 = 0
for e in given_list_02:
if e < 0:
total3 += e
Since you commented, that you want to use a while loop for training purposes, try this:
given_list_02 = [9, 8, 5, 3, -1, -2, -3, -6]
total3 = 0
i = 0
while i < len(given_list_02):
if given_list_02[i] < 0:
total3 += given_list_02[i]
i += 1
Keep in mind though, that you really should prefer the for loop in this case.

Retaining information through recursion under strict conditions (Python)

My partner and I are working on a problem in which we need to reduce an array and run operations on the pieces (in this case, of 2), we are reducing with recursion and sending the left-right hand sides of the array back into function.
We have all this working fine, and for example, using the array [2, 3, 4, 5, 6, 7, 8, 9] we get all the pieces we need.
The problem is, we need each of these pieces to then run the math operation on the next piece we get.
So, for example we recur down to [2, 3] and transform that into [5, -1]. Then we recur down to [4, 5] and change that into [9, -1] and combine them into [14, -2, -4, 0]. This is the left hand side of our array. And this works great. And then it does the right hand side, and it gets the answer we want. This works great.
The problem, is we now need both parts together (can't use global variables). And we have been stuck here for several hours. We can only pass the simplified array through the recursion, and if we initialize an array to hold both parts it will get reset when the right-hand side starts.
Thanks
EDIT: Code: The H is starting matrix that is given, but it doesn't matter, it as no relevance its just there so the unit test goes through (We could use it, but we don't really know how)
The imput for x is [2,3,4,5,6,7,8,9]
def hadmatmult(H, x):
d = 0
n = len(x)
first = 0
last = len(x)
a = [0] * math.floor(n/2)
b = [0] * math.floor(n/2)
if n == 2:
temp1 = x[0]
x[0] = temp1 + x[1]
x[1] = temp1 - x[1]
else:
mid = math.floor((first+last)/2)
for i in range(first, mid):
a[i] = x[i]
hadmatmult(H, a)
for j in range(mid, last):
b[d] = x[j]
d = d + 1
hadmatmult(H, b)
if(len(a) == 2:
adds = [0] * len(a)
subs = [0] * len(a)
for t in range(0, len(a)):
adds[t] = a[t] + b[t]
subs[t] = a[t] - b[t]
#alladds = alladds + adds
#allsubs = allsubs + subs
print(adds)
print(subs)
Output: This outputs the parts, [14, -2, -4, 0] and [30, -2, -4, 0]
If you must recurse, do so in a simpler way. Strip off the elements you need and recurse through the rest.
x = [2,3,4,5,6,7,8,9]
def recurse_and_stuff(x):
if len(x) == 0:
# base case
return []
a, b, = x[:2], x[2:4]
m,n,o,p = [a[0]+a[1], a[0]-a[1], b[0]+b[1], b[0]-b[1]]
result = [[m+o, n+p, m-o, n-p]] + recurse_and_stuff(x[4:])
return result
>>> x = [2,3,4,5,6,7,8,9]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0]]
>>> x = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0], [46, -2, -4, 0], [62, -2, -4, 0]]
# works for longer inputs as long as len(x) % 4 == 0
To recurse all the way down:
import itertools
def do_the_needful(a,b):
try:
zipped = zip(a,b)
except TypeError:
# a and b are ints, not lists of ints
zipped = [(a,b)]
return itertools.chain.from_iterable(zip(*[[aa+bb, aa-bb] for aa,bb in zipped]))
def recurse_and_stuff(x):
if len(x) == 1:
return list(x[0])
# if you only have to iterate through this, there's no
# need to call `list(...)` here.
return recurse_and_stuff([do_the_needful(x[i], x[i+1]) for i in range(0, len(x), 2)])

Summation of only consecutive values in a python array

I am new on python (and even programing!), so I will try to be as clear as I can to explain my question. For you guys it could be easy, but I have not found a satisfactory result on this yet.
Here is the problem:
I have an array with both negative and positive values, say:
x = numpy.array([1, 4, 2, 3, -1, -6, -6, 5, 6, 7, 3, 1, -5, 4, 9, -5, -2, -1, -4])
I would like to sum ONLY the negative values that are continuous, i.e. only sum(-1, -6, -6), sum(-5, -2, -1, -4) and so on. I have tried using numpy.where, as well as numpy.split based on the condition.
For example:
for i in range(len(x)):
if x[i] < 0.:
y[i] = sum(x[i])
However, as you can expect, I just got the summation of all negative values in the array instead. In this case sum(-1, -6, -6, -5, -5, -2, -1, -4)
Could guys share with me an aesthetic and efficient way to solve this problem? I will appreciate any response on this.
Thank you very much
You can use itertools module, here with using groupby you can grouping your items based on those sign then check if it meet the condition in key function so it is contains negative numbers then yield the sum else yield it and at last you can use chain.from_iterable function to chain the result :
>>> from itertools import groupby,tee,chain
>>> def summ_neg(li):
... for k,g in groupby(li,key=lambda i:i<0) :
... if k:
... yield [sum(g)]
... yield g
...
>>> list(chain.from_iterable(summ_neg(x)))
[1, 4, 2, 3, -13, 5, 6, 7, 3, 1, -5, 4, 9, -12]
Or as a more pythonic way use a list comprehension :
list(chain.from_iterable([[sum(g)] if k else list(g) for k,g in groupby(x,key=lambda i:i<0)]))
[1, 4, 2, 3, -13, 5, 6, 7, 3, 1, -5, 4, 9, -12]
Here's a vectorized NumPythonic solution -
# Mask of negative numbers
mask = x<0
# Differentiation between Consecutive mask elements. We would look for
# 1s and -1s to detect rising and falling edges in the mask corresponding
# to the islands of negative numbers.
diffs = np.diff(mask.astype(int))
# Mask with 1s at start of negative islands
start_mask = np.append(True,diffs==1)
# Mask of negative numbers with islands of one isolated negative numbers removed
mask1 = mask & ~(start_mask & np.append(diffs==-1,True))
# ID array for IDing islands of negative numbers
id = (start_mask & mask1).cumsum()
# Finally use bincount to sum elements within their own IDs
out = np.bincount(id[mask1]-1,x[mask1])
You can also use np.convolve to get mask1, like so -
mask1 = np.convolve(mask.astype(int),np.ones(3),'same')>1
You can also get the count of negative numbers in each "island" with a little tweak to existing code -
counts = np.bincount(id[mask1]-1)
Sample run -
In [395]: x
Out[395]:
array([ 1, 4, 2, 3, -1, -6, -6, 5, 6, 7, 3, 1, -5, 4, 9, -5, -2,
-1, -4])
In [396]: out
Out[396]: array([-13., -12.])
In [397]: counts
Out[397]: array([3, 4])
you can flag negative values .... and do this with plain python
prev = False
for i,v in enumerate(a):
j = i + 1
if j < len(a):
if a[i] < 0 and a[j] < 0:
temp.append(v)
prev = True
elif a[i] < 0 and prev:
temp.append(v)
prev = True
elif a[i] > 0:
prev = False
else:
if prev and v < 0:
temp.append(v)
output
print(temp)
[-1, -6, -6, -5, -2, -1, -4]
with intertools i would do just that
def sum_conseq_negative(li):
neglistAll = []
for k, g in groupby(li, key=lambda i:i<0):
negList = list(g)
if k and len(negList) > 1:
neglistAll.extend(negList)
return sum(negList), len(negList)
sumOf, numOf = sum_conseq_negative(li)
print("sum of negatives {} number of summed {}".format(sumOf,numOf))
sum of negatives -25 number of summed 7

iterate the range in for loop to satisfy the condition

According to answered solutions I have tested the loop. I want the range swtich from 6 to 10; 11 to 16 and so one until the condition is satisfied but the loop keep running from 1 to 5.
set_mean = -10
#calculated_mean = None
energy = []
calculated_mean = float('inf')
while calculated_mean > set_mean:
for i in range(1, 5):
energy.append(i-i*i)
print(energy)
calculated_mean = sum(energy[-2:])/2
print(calculated_mean)
How I can automate the range so that it switch to next five values in second loop and so on if the condition is not satisfied. For this example, it will satisfied if loop will run from 6 to 10.
Thanks!
Supposing you are just asking to do the dynamic way use the following:
set_mean = -10
#calculated_mean = None
energy = []
calculated_mean = float('inf')
x = 1
while calculated_mean > set_mean:
for i in range(x, x+4): # you can change step size here by passing it as last argument
energy.append(i-i*i)
print(energy)
calculated_mean = sum(energy[-2:])/2
print(calculated_mean)
x = x + 1
Output is:
[0]
0
[0, -2]
-1
[0, -2, -6]
-4
[0, -2, -6, -12]
-9
[0, -2, -6, -12, -2]
-7
[0, -2, -6, -12, -2, -6]
-4
[0, -2, -6, -12, -2, -6, -12]
-9
[0, -2, -6, -12, -2, -6, -12, -20]
-16
I think this is exactly what you want. Because the loop stops when you get -16
i don't see any conditionals, this loop will run forever as of your current version since calculated_mean will always be greater than set_mean since you are making the energy list larger every time you go through the loop, i don't really understand what your "condition" for calculates_mean is but if it's just the average of the last 2 numbers in the list it will never be greater than your set_mean which will result in an infinite loop.

Categories