New values for intervals of numbers - python

I have numpy arrays that look like this:
[2.20535093 2.44367784]
[7.20467093 1.54379728]
.
.
.
etc
I want to take each array and convert it like this:
[1 1]
[2 0]
0 means that the values are below 2. 1 means that the values are between 1 and 3. 2 means they are above 3.
I want to use a switch case function in python for this.
This is what I wrote until now:
def intervals(input):
match input:
case num if 0 <= num.all() < 2:
input = 0
case num if 2 <= num.all() < 3:
input = 1
case num if 3 <= num.all() <= math.inf:
input = 2
return input
But it doesn't seem to work as expected.

Without using a switch case, you can use:
num = np.array([[2.20535093, 2.44367784],
[7.20467093, 1.54379728]])
print(num) # [[2.20535093 2.44367784], [7.20467093 1.54379728]]
num[num < 2] = 0
num[np.logical_and(num > 1, num < 3)] = 1
num[num > 3] = 2
print(num) # [[1 1], [2 0]]

Your first two conditions are in conflict, since a value could be at the same time below 2 and between 1 and 3. Assuming the "below 2" condition wins in this case, a one line solution to your problem could be like that:
import numpy as np
arr = np.random.rand(5000000, 2)
new_arr = (arr > 2) + (arr > 3)
Here you are assigning +1 if value is above 2 and +1 if value is above 3 (Trues are casted to int and summed).
The approach is also slightly faster that other proposed solution, though less readable.

Related

Function in python that outputs either -1 or 0 or 1

Is there like a (modified) round function where the output would be either -1,0,1 depending on to what number is the input the closest? E.g. 0 => 0, -2154 => -1, 10 => 1
Currently I am using normal if else statements:
if i == 0:
return 0
elif i > 0:
return 1
else:
return -1
But is there any way how I can make this a one-line code? By for instance using some modified round function.
You can use numpy.sign()
import numpy as np
print(np.sign(1.2))
print(np.sign(-3.4))
print(np.sign(0))
output:
1.0
-1.0
0
Without any imports:
def sign(i):
return (i>0) - (i<0)
print(sign(1))
print(sign(-3))
print(sign(0))
output:
1
-1
0
Here is a one-liner function using plain vanilla python and no imports. It can hardly get simpler than this.
def f(i):
return -1 if i < 0 else int(bool(i))
In [5]: f(0)
Out[5]: 0
In [6]: f(1)
Out[6]: 1
In [7]: f(-5)
Out[7]: -1
As the other answer commented, you can do it easily by using the numpy.sign() method, that directly returns your desired result.
However, you can do it directly using the built-in math function and the method copysign(x, y), which copies the sign of the second value to the first. By setting the first value as a boolean it will be interpreted as 0 if the original value is 0, and 1 otherwise. Then we copy the sign of value, which will transform the 1 into -1 or leave it positive.
from math import copysign
values = [1, 200, 0, -3, 45, -15]
for value in values:
print(int(copysign(bool(value), value)))
which outputs:
1
1
0
-1
1
-1

List of binary numbers: How many positions have a one and zero

I have a list of integers, e.g. i=[1,7,3,1,5] which I first transform to a list of the respective binary representations of length L, e.g. b=["001","111","011","001","101"] with L=3.
Now I want to compute at how many of the L positions in the binary representation there is a 1 as well as a zero 0. In my example the result would be return=2 since there is always a 1 in the third (last) position for these entries. I would be happy for any comment. I think, ideally I should do many Xor operations at the same time. However, I'm not sure how I can do this efficiently.
Edit: Thanks for the many answers!! I have to check which one is the fastest.
One observation is that if you take the AND of all numbers, and also the OR of all numbers, then the XOR of those two results will have a 1 where the condition is fulfilled.
So:
from functools import reduce
from operator import and_, or_
def count_mixed_bits(lst):
xor = reduce(and_, lst) ^ reduce(or_, lst)
return bin(xor).count("1")
count_mixed_bits([1,7,3,1,5]) # 2
There's a numpy.binary_repr method that accepts length. Unfortunately, it can't handle arrays. But you can apply a functionality of np.unravel_index instead:
def check(arr, lenght):
positions = np.array(np.unravel_index(i, (2,)*lenght))
return positions, np.sum(np.sum(positions, axis=1) != len(arr))
>>> positions, output = check(i, 3)
>>> print(positions)
>>> print(output)
[[0 1 0 0 1]
[0 1 1 0 0]
[1 1 1 1 1]]
2
Here is a solution, I suspect it is not very efficient, but it is easy to understand.
I loop over the digits and find the unique set, then I count the number of entries with a set length of two:
# create a binary list of 3 elements from input list of integers
i=[1,7,3,1,5]
b=['{0:03b}'.format(x) for x in i]
# loop over the digit position (1,2,3)
cnt=[]
for pos in range(3):
cnt.append(len(set([c[pos] for c in b])))
# cnt now contains a list of either 2(=both 1 and 0 present) or 1 (unique)
# so now we count the number of entries with "2"
result=cnt.count(2)
print (result)
answer:
2
First of all your question is tagged with numpy but your array is not a numpy array.
Here is a solution that uses numpy:
import numpy as np
def has_zeroes_and_ones_at_index(arr, index_from_right):
shifted_arr = np.right_shift(arr, index_from_right)
has_one_at_index = shifted_arr % 2 == 1
return(True in has_one_at_index and False in has_one_at_index)
arr = np.array([1, 7, 3, 1, 5])
res= has_zeroes_and_ones_at_index(arr, 1)
print(res)
Because the numbers are stored in binary we can use bit shifting to move all bits of the numbers to the right and then look at the last bit. We dont have to cast them to a binary format before.
5 (101) right shift by one -> 2 (010)
We then create a mask to see which numbers have a one in the last bit and return True when in the mask there is at least one True element and one false element.
You can use python bitwise operators for this task.
def find(aList, nn):
return sum(
filter(
lambda bb: bb > 0 ,
(
( 1 <= (sum( (aa & 1<<kk) > 0 for aa in aList)) < len(aList) )
for kk in range(nn)
)
)
)
>>> find([1,7,3,1,5],3)
2
>>> find([],3)
0
>>> find([7],3)
0
>>> find([7,1],3)
2
>>> find([7,1,7],3)
2

Increment array in python, like a base number system

Say I have an array like [0 0 0 0] and I want to iterate it on a base-like scale. So say I pick base 100, and assume that I want to do it in a little endian system. My output would look like:
[1 0 0 0]
[2 0 0 0]
...
[99 0 0 0]
[0 1 0 0]
My code currently is in a function 'indexArray' but I was wondering it it was possible to do this without an if statement in a much simpler way?
def indexArray(enteredArr):
enteredArr[0] += 1
for i in range(len(enteredArr)):
if enteredArr[i] > 99:
enteredArr[i] = 0
enteredArr[i + 1] += 1
return enteredArr
As usual in Python, there's an easier way if you look for it.
Your main loop could be:
for i in itertools.product(range(BASE), repeat=NDIM):
... stuff ...
For your example, BASE=100 and NDIM=4, but the same approach works for any other values.
i will be a tuple of array values counting up like (0, 0, 0, 0), (0, 0, 0, 1) ... (BASE-1, BASE-1, BASE-1, BASE-1).
num = 45
base = 3
num_base = []
remainder = num
# the remainders of the integer divisions of the number by the base are the digits of the number in the new base
# see also here: https://math.stackexchange.com/questions/111150/changing-a-number-between-arbitrary-bases
while remainder:
num_base.append(remainder % base)
remainder //= base
# join the list in reverse while changing type of digits to characters
print("".join(str(x) for x in num_base[::-1]))

how to use two variables in for loop?

Define a function calls addFirstAndLast(x) that takes in a list of numbers and returns the sum of the first and last numbers.
Examples
>>> addFirstAndLast([])
0
>>> addFirstAndLast([2, 7, 3])
5
>>> addFirstAndLast([10])
10
My question is this that i can not use two variables in for loop, so what should i do , how to fix the error. and what could be the improved code of this problem.
def addFirstAndLast(x):
total = 0
total1 = 0
total2 = 0
pos_last = []
for num, last in x:
num = str(num)
num = num[0]
total1 += int(num)
last = last % 10
pos_last = pos_last.append(last)
total = sum(pos_last)
total2 = total+total1
return total2
print addFirstAndLast([2, 7, 3])
3 distinct cases: 1) the list is empty, 2) the list has one element, 3) the list has two or more elements.
Try without loop:
def addFirstAndLast(x):
if len(x) == 0:
return 0
elif len(x) < 2:
return x[0]
else:
return x[0]+x[-1]
>>> print addFirstAndLast([2, 7, 3])
5
>>> print addFirstAndLast([2, 3])
5
>>> print addFirstAndLast([3])
3
x is a list of integers, when using x in a for loop , each iteration would get a single integer. But when you do -
for num, last in x:
You are trying to unpack a single integer into 2 places, this cannot happen, and it is causing your issue.
I am not even sure what the rest of your logic does , you can do this simply using -
def addFirstAndLast(x):
l = len(x)
return x[0] + x[-1] if l > 1 else x[0] if l > 0 else 0
Examples -
>>> addFirstAndLast([])
0
>>> addFirstAndLast([2, 7, 3])
5
>>> addFirstAndLast([10])
10
Explanation of the logic - The expression is -
((x[0] + x[-1]) if l > 1 else x[0]) if l > 0 else 0
It first checks the outer most if condition, if length is 0 it returns 0 , if not it goes onto inner if condition.
In the inner if condition, if length is greater than 1(meaning there are 2 or more elements) it returns sum of first and last, otherwise (which means length is greater than 0 but less than 2 , meaning its 1) it returns first element.

Can't assign to operator

i = 0
num = 0
while i <= 1000:
if i % 3 and i % 5 == 0:
num + i = num <--- Adding Up Numbers Divisable by 3 & 5...
i += 1
print num
Error:* can't assign to operator (line 5)
Are you sure you don't want:
num = num + i
or equivalently:
num += i
?
Note that this can be done a little easier using sum, range and a generator expression:
sum( x for x in range(0,1000,5) if x % 3 == 0 )
#^only take every 5th element (0, 5, 10, 15 ...)
#^ Also only take elements divisible by 3
If you're planning on running this code on only python 2.x, you can change range to xrange in the above expression. Also, when reading other people's code, you'll sometimes see if x % 3 == 0 written as if not x % 3 in this type of context, but I prefer the first version (it seems a little more explicit to me).
num + i = num <--- Adding Up Numbers Divisable by 3 & 5...
You cannot assign a value to an expression.
Also, why not just add 5 each time and check that it is divisible by 3? You'll already know it is divisible by 5...

Categories